Универсальные списки-каталоги (list)

В проекте можно реализовать несколько универсальных каталогов со своей разметкой. Программно это совсем другой тип работы каталога, со своей схемой реализации.

  • Каталогов на одном домене может быть несколько, все они храняться в as_lists, каждый имеет свою разметку, и свои процедуры search и getItem.
  • Такие каталоги не привязаны жёстко к базе. Таким образом, выступать в роли Категорий и Фильтров могут абсолютно разные данные (а не as_cat_categories и as_cat_filters как в варианте выше).
  • Сохранение URL элемента каталога необходимо реализовать самостоятельно.

Расположены каталоги по адресу /list/{code}

Формат URL категории

  • /list/{code}/{category1}/{category2}

Формат URL категории с фильтрами

  • /list/{code}/{category1}/{category2}/f/{filter1}-{text1},{text2}/{filter2}-{text3}/{filter3}-{text4}

Формат URL товара

  • /list/{code}/{category1}/{category2}/p/{url элемента каталога}

Управление выведено в Компоненты / Универсальные каталоги:

Таблица as_lists

  • id - id каталога
  • code - код каталога
  • rootURL - код каталога в URL
  • listMakeup - HTML разметка каталога
  • listItemMakeup - HTML разметка списковой части каталога (элементов)
  • itemDetailsMakeup - HTML разметка страницы элемента каталога

Процедура list_{code}_search

Примечание: 

  • в filters также передается langID для локализации.
  • Типы фильтров(typeCode из SELECT 2) :
    • checks - галочки
    • radio - радио-переключатели
    • select - выбор из списка с поиском
    • switch - переключатель да-нет
    • text - строка для ввода
CREATE PROCEDURE [dbo].[list_new_search]
	@filters DictionaryParameter READONLY,
	@cats nvarchar(256),
	@sort nvarchar(24),
	@page int,
	@username nvarchar(32)
AS
BEGIN
	-- ПРОЦЕДУРА ПОИСКА ПО УНИВЕРСАЛЬНОМУ КАТАЛОГУ
        /*Определяем какие фильтры есть в url, и с учетом этого
          отбираем элементы для каталога*/

	-- Для пагинации
	declare @PAGE_SIZE int = 10, @total int = 0
        /*Сохраняем ID элементов каталога во временой таблице.
          Подчитываем сразу полученное количество*/
	declare @itemIDs table (id int)

      /*Находим, на какой категории мы сейчас стоим, в общем дереве категорий
      @cats - это часть url где указанны категории, например для
       /list/suppliers/category1/category1_1/category1_1_3 будет @cats =
      'category1,category1_1,category1_1_3'*/

	CREATE TABLE dbo.#cats (id int, code nvarchar(256), name nvarchar(256), level
                                int, parentID int)
	insert into #cats
	select id, code, name, 1 [level], parentID
        from au_projectCategories
        where code in (select Value from dbo.split(@cats, ','))

	-- Выбранных категорий может быть несколько, с учетом вложенности вложенность
	declare @catCount int
	select @catCount = count(*) from #cats
	/* Находим конечную точку в дереве на которой остановились, то есть
           максимального уровня (в примере выше это будет category1_1_3)*/
	declare @selectedCatID int
	select top 1 @selectedCatID = id from #cats order by level desc

    -- Обработка возможных фильтров
    -- declare @filterCity nvarchar(256)
    -- select @filterCity = Value from @filters where [Key] = 'city'

    --- добавляем сортировку в Динамический запрос SELECT 1
    declare @sortStatement nvarchar(512)
    set @sortStatement = (case @sort
                    when 'rating' then ' order by name '
                    end	)

    -- Итак, что находим учитывая выбранные категории и отмеченные фильтры?
    insert into @itemIDs
    select distinct id
      from (select id, 111 catID
              from au_suppliers) t
    -- Категории поставщика входящие в выбранные категории, или в дочерние категории выбранных категорий
	where (isnull(@cats,'')='' or t.catID = @selectedCatID
            					or (select parentID from au_projectCategories where id = t.catID) = @selectedCatID )

	select @total = count(*) from @itemIDs

	-- SELECT 1 - SearchItemsResult (настройки поиска, общие параметры каталога)
	select 'Не найдено' EmptyText,
		   (case when isnull(@cats,'')='' then 'Каталог поставщиков услуг' else (select name from au_projectCategories where id = @selectedCatID) end) Title,
		   'desc' Text,
		   @sort Sort,
		   @page [Page],
		   @PAGE_SIZE PageSize,
		   @total Total,
		   'DEMO' SeoTitle,
		   'DEMO' SeoDescription,
		   'DEMO' SeoKeywords,
		   ''  HeadSection,
		   ''  BodyBottomSection,
		   1 Result,
           '' Msg
	-- SELECT 2 - FilterParameter - Показываем либо все услуги, либо относящиеся к выбранной категории
	select 1
    /*select 0 id,
    	   'Город' title,
		   'Город' tooltip,
		   'city' Code,
		   '' defValue,
		   'checks' typeCode,
		   (select stuff( (
						select ','+ name
						  from (select name from as_geo_regions) t1
						for xml path(''), type
						   ).value('.', 'varchar(max)'), 1, 1, ''
						 )
			) as  commaValues,
		   (select top 1 Value from @filters where [Key] = 'city') commaSelected,
3 showCount
*/

	-- SELECT 3 - Cat  CatChain - Выбранные категории
	select * from #cats
	-- SELECT 4 - Cat  InnerCats - отобразим сбоку либо все категории, либо категории входящие в выбранную
    select *
    from au_projectCategories
	where parentID = @selectedCatID or (parentID is null and @catCount = 0)

	drop table #cats

	-- SELECT 5 - Item (ID, Desc, Price, Name, URL) -- Данные отображаемые в перечне элементов каталога
	select s.id,
		  isnull(surname+' '+firstname+' '+lastname, username) as name,
          '' [desc],
          '0' price,
          '/list/test/'+isnull(c.username,'')+'---'+try_cast(s.id as nvarchar) Url
	 from au_suppliers s join ctr_contacts c on c.id = s.contactID
     where s.id in (select id from @itemIDs)

	-- SELECT 6 - ItemParam
    -- Атрибуты элементов каталога
    -- Структура Атрибута: ID, Name, Code, Value, ItemID
    -- В разметке listItemMakeup атрибут отобразится по {param-code}
	select 1 as ID, 'Категории услуг' as Name, 'categories' as Code,
    	   'value1' as Value, 111 as ItemID
    union

    select 2 as ID,
    'О себе' as Name,
    'description' as Code,
    (select isnull(description,'О себе') from ctr_contacts where id = contactID) as value,
    id as ItemID  from au_suppliers

	-- SELECT 7 - Param общие Атрибуты для каталога (ID, Name, Code, Value)
    -- Атрибуты самого каталога
    -- Структура Атрибута: ID, Name, Code, Value
    -- В разметке listMakeup атрибут отобразится по {param-code}
	select 1

	-- SELECT 8 - DictionaryParameter  Sorts
	select 'rating' [key], 'По рейтингу' Value

	-- select 9 -- Start cats (категории которые показываются на 1 экране каталога как дашборд {catList})
	select id, code, name, 1 level, parentID,'' [Image],
    'fas fa-wrench' iconClass
	  from au_projectCategories where @selectedCatID is null and (parentID is null or parentID in (select id from au_projectCategories where parentID is null) )
END

Процедура list_{code}_getItem

ALTER PROCEDURE [dbo].[list_projects_getItem]
	@itemID int,
	@username nvarchar(32),
    @parameters DictionaryParameter READONLY
AS
BEGIN

	declare @langID int
    select @langID = try_cast(value as int) from @parameters where [key] = 'langID'

    if isnull(@langID,0)=0 set @langID = 2

	select port.id,
    	   isnull(lang.title,port.title) as name,
           0 price,
           iif(isnull(lang.task, port.task)<>'', '

'+iif(@langID=1, 'Initial task', 'Начальная задача')+'

'+ isnull(lang.task, port.task), '') +
           iif(isnull(lang.solution, port.solution)<>'', '

'+iif(@langID=1, 'Solution', 'Решение')+'

'+ isnull(lang.solution, port.solution), '') +
           iif(isnull(lang.answers, port.answers)<>'', '

'+iif(@langID=1, 'Customer response', 'Отклик заказчика')+'

'+ isnull(lang.answers, port.answers), '')  [desc],
		   '' List1Makeup,
		   '

{p1}

{p2}
' List2Makeup,
		   '' List3Makeup,
		   '' List4Makeup,
		   '' List5Makeup,
		   isnull(lang.title,port.title) + ' - FALCON' + iif(@langID=1, ' Project', ' Проект') SeoTitle,
		   isnull(lang.title,port.title) + ' - FALCON' + iif(@langID=1, ' Project', ' Проект') SeoDescription,
		   isnull(lang.title,port.title) + ' - FALCON' + iif(@langID=1, ' Project', ' Проект') SeoKeywords,
            iif(@langID=1, 'Falcon Space Projects', 'Проекты Falcon Space')  BreadcrumbRootTitle

	  from as_falconPortfolio port
      left join as_lang_portfolio lang on lang.portfolioID = port.id and lang.langID = @langID
      where port.id = @itemID


	-- Определяем цепочку категорий
	declare @level int
	CREATE TABLE dbo.#cats (id int, code nvarchar(256), title nvarchar(256))

	insert into #cats
	select id, code, title
	from as_falconPortfolioCategories
	where id = (select catID from as_falconPortfolioItemCategories where itemID = @itemID)

	-- SELECT 2 Cats chain
	select * from #cats
	drop table #cats

	--SELECT 3 ItemParams - Атрибуты (Структура ID, Name, Code, Value, ItemID)
    select 2 as ID, 'Картинка' as Name, 'image' as Code, dbo.rs_resourceLink('falconPortfolio',try_cast(id as nvarchar),0) as value, id as ItemID from as_falconPortfolio where id = @itemID
	union
    select 3 as ID, 'Ссылка' as Name, 'projectUrl' as Code, iif(isnull(url,'')<>'', ' '+url+'', '') as value, id as ItemID from as_falconPortfolio where id = @itemID
    union
    select 3 as ID, 'Айди' as Name, 'itemID' as Code, try_cast(id as nvarchar) as value, id as ItemID from as_falconPortfolio where id = @itemID
    union
    select 3 as ID, 'cta' as Name, 'cta' as Code,  dbo.as_htmlBlockLang('cta',@langID) as value, 0 as ItemID


	--select 1 as ID, 'Фото' as Name, 'photo1' as Code, '
' as value, id as itemID from au_suppliers where id = @itemID
	--union
    --select 2 as ID, 'Оценка' as Name, 'mark' as Code,'12344' value, id as itemID from au_suppliers where id = @itemID

	-- SELECT 4-9
    -- Категории услуг и расценки
	--select '111' p1, '22' p2, '22' p3, '22' p4, '22' p5

END

Примечание: 

  • в процедуру может передаваться также необзяательный параметр @parameters (тип DictionaryParameter), в котором передается langID
  • Процедуру можно найти с демо проекте.

Разметка as_lists: listMakeup

Дефолт разметка:

{title}
<div class="row align-items-start mt-4">
  <div class="col-12 col-sm-12 col-md-12 col-lg-9 ml-auto as-lightbox">
  <div>{items}</div> </div> <div class="col-12 col-sm-12 col-md-12 col-lg-3">

  <div>{innerCats}</div>

  </div>
</div>

<!--Разметка расположения элементов на странице каталога
     Компоненты {...} определяются в процедуру search -->

 Параметры (используются как {name}):

  • filtes - вставка  фильтров
  • innerCats - категории в текущей категории
  • cats - дерево категорий до текущей категории
  • tags - теги для отображения выбранных фильтров
  • viewTypes - 2 вида просмотра каталога
  • title - заголовок каталога
  • foundCount - метка количества результатов
  • desc - описание текущей категории
  • sorts - элемент управления сортировкой
  • items - вывод элементов каталога
  • paging - элемент управления пагинацией
  • param-[code] - вставка дополнительных параметров
    ВАЖНО. Обязательно указывайте {cat}, он необходим для построения адресов. Если не нужно его выводить, используйте 
    {cats}

Разметка as_lists: listItemMakeup

Разметка по умолчанию

<div class="card mb-3" data-itemid="{id}">
   <div class="card-header">
        <h5 class="font-weight-bold">{name}</h5>
   </div>

   <div class="card-body">
       <div class="row align-items-center">
            <div class="col-12 col-sm-12 col-md-12 col-lg-5">
               <a href="#" class="as-lightbox-item" data-title="{name}" data-="" url="/uploads/land/f/{id}.gif"><img src="/uploads/land/f/{id}-thumb.jpg" alt="{param-description}"></a>
             </div>

            <div class="col-12 col-sm-12 col-md-12 col-lg-7 ">
                <div class="text-dark mt-2">{param-description}</div><br>
                <div class="badge badge-pill badge-primary btn-sm"><small>{price}
                </small></div>
            </div>
       </div>
    </div>
</div> 

 Параметры (использовать как {code}):

  • id - ID элемента
  • name - наименование элемента
  • desc - описание элемента
  • price - цена
  • url - URL страницы элемента
  • imgUrl - картинка элемента
  • addToCart - кнопка добавления в корзину
  • addToFav - кнопка добавления в избранное
  • param-[code] - дополнительные параметры, переданные через sql
  • image - картинка элемента (как image-resource)

Разметка as_lists: itemDetailsMakeup

<!-- Разметка элемента страницы каталоге элеметы {...}
     Определются в процедуре getItem-->
<h2>{name}</h2>
<div><!--{param-mark}--></div>
<div class="row align-items-center">
  <div class="col-lg-2 col-md-2 col-sm-12 col-xs-12 mb-3"><!--{param-photo1}--></div>
  <div class="col-lg-10 col-md-10 col-sm-12 col-xs-12 mb-3">
   <!-- {list1}-->
   <!-- {list2}-->
  </div>
</div>
<div class="card-header">
  <ul class="nav nav-tabs card-header-tabs">
    <li class="nav-item">
      <a class="nav-link active" data-toggle="tab" href="#item1">Описание</a>
    </li>
    <li class="nav-item">
      <a class="nav-link" data-toggle="tab" href="#item2">Отзывы <!--{param-revCount}--></a>
    </li>
    <li class="nav-item">
      <a class="nav-link" data-toggle="tab" href="#item3">Портфолио</a>
    </li>
  </ul>
</div>
<div class="card-body tab-content">
  <div class="tab-pane fade show active" id="item1">
    <p class="mt-3 text-dark"><!--{desc}--></p>
  </div>
  <div class="tab-pane fade" id="item2">
    {list3}
  </div>
  <div class="tab-pane fade" id="item3">
    Портфолио отсутствует
  </div>
</div>

Параметры (использовать как {code}):

  • id - ID элемента
  • name - наименование элемента
  • desc - описание элемента
  • imgUrl - картинка элемента
  • price - цена
  • addToCart - кнопка добавления в корзину
  • addToFav - кнопка добавления в избранное
  • param-[code] - дополнительные параметры, переданные через sql
  • images - галерея картинок элемента
  • list1-list5 (с параметрами p1-p6) - вывод дополнительные списков (их разметка задается в соответствующих параметрах в хранимых процедурах List1Makeup-List5Makeup). 

Примечания по каталогам List

  • Для категорий вы можете также указывать Url параметр и тогда именно он будет браться за основу, а не строить по иерархическому принципу (вложенность категорий).
  • Можно делать innerCats постоянным (т.е. чтобы всегда категории выводились одни и те же, даже когда мы находимся в одной из них). При этом текущая категория будет выделяться (идет проверка по вхождению URL в текущий адрес страницы). Делайте уникальные названия категорий без возможных пересечений (например car и carPlaces - плохие названия категорий, т.к. одно входит в другое название).

Как добавить микроразметку, стили или скрипты в каталог.

Используйте параметры headSection, bodyBottomSection в SELECT 2 в list_search и аналогичные параметры в  list_getItem. Вы можете туда вставлять коды OpenGraph и JSON LD описание страниц.

Как динамически изменить корень хлебных крошек? 

Используйте параметр BreadcrumbRootTitle в SELECT 2 в list_search и аналогичные параметры в  list_getItem. 

Главное применение - при локализации каталога. 

Как сделать другое число скрываемых параметров в фильтре?

Для этого необходимо установить для фильтров в SELECT 2 процедуры search парамтер showCount (по умолчанию 4). 

Как сделать так чтобы фильтры с Not Selected (или не выбрано) не попадали в адрес?

Для этого на страницу надо добавить подобную разметку и перечислить все значения  по умолчанию во вложенных элементах span 

<div class="cat-notSelectedList hide">
  <span data-value="Not selected"></span>
  <span data-value="Не выбрано"></span>
</div>

Как настроить кеширование списка List

Для этого установите в WebConfig в разделе AppSettings параметр listCacheMinutes. Данный параметр указывает сколько минут будет кешироваться результат выдачи в списке List
(кешируются все запросы по списку - вариации выбора фильтров и категорий).

<add key="listCacheMinutes" value="60" />