Как сделать мультикатегории на MODX с TV-шкой
Всем привет.
Недавно глубоко озадачился этим решением и в результате которого нехитрым (почти) способом можно сделать мультикатегории.
Подходит данное решение для сайтов у которых, наверное не более 5..10к товаров. На больших, думаю, будет тормозить, (не проверял) так что обратная связь приветствуется.
И так, что нам нужно?
1.1. В параметрах выбора у нас Тип — список множественный выбор, в возможных значениях, я обычно вписываю @EVAL (вам следует, использовать другой вариант, если хотите чтобы в MODX 3 у вас это дело не слетело (оно не поддерживается в 3-ке):
В parents — родители из которых брать категории.
where — можно или Isfolder или template использовать — как хотите.
1.2. В параметрах вывода выбираем РАЗДЕЛИТЕЛЬ , (запятая).
1.3. остальное по вкусу. (для каких шаблонов и названия, описания)
В товаре теперь у нас есть TV-шка которая складирует ID дополнительных категорий.
2. настраиваем вывод pdoResource:
2.1. Сложность в том что мультиселект хранит данные в формате: 23||67||288 те с разделителем ||. поэтому выражения с where типа:
1й вернет товары у которых только одна доп категория, которая равна 88 или 99.
2й вернет товары у которых только одна доп категория, которая равна только 88.
3й вернет все товары, у которых встречаются совпадения с 88. (те 23||67||288 — этот товар попадет в выборку.)
4й вообще даже так записывать — не верно, ведь у нас тут не показана выборка по parent = 88 или 99 а она по умолчанию есть и получиться что нужно указывать корневую Parents = 33, но тогда попадут все товары, значит в where еще нужно дописать parent:IN 88 99. Но и этого мало.
2.2. В общем, нам нужно залайкать в нескольких вариантах multi те.:
Есть странный глюк в разборе where и если вы захотите написать все в стиле прилежного MODX-сера:
Ключ в массиве должен быть уникальным, а значит OR:multi:LIKE у нас останется единственный последний («OR:multi:LIKE» => "%|88|%"). Поэтому нам нужно или поставить пробелы дополнительные чтобы ключ отличался или переписать наш код в более sql ориентированный вариант, что и сделаем ниже (вообще опускаю еще некоторые проблемы которые могут (возможно) возникнуть).
Важно сказать что вывод товаров в категории будет работать только непосредственно в конечных категориях, потому что на входе у нас 1 категория будет активная (ниже объясню почему) структура такая:
а вот строка
Если зайдем в категорию Туфли 88, то мы тут увидим только желетку-майку id987. ну и отдельным сниппетом (блоком) выбор категорий 801 и 802. а уже если зайдем в Туфли с правой ногой id801, то увидим товары которые с правой ногой и дополнительный товар Майка простая, что нам и нужно.
2.4.(UDP 30-07-2023). В комментариях справедливо подсказали более короткое решение через функцию mySql
FIND_IN_SET. Небыло случая проверить, но вот довелось. Меня смутила запись 1=1 AND в начале условия, тк не понятно было что к чему. И вот сегодня после некоторых экспериментов стало ясно. Обработка условия в where идет хитрым образом и если в строке не встречаются корректные данные (нет знака сравнения), то строка вообще вылетает из выборки. (те если я удаляю 1=1, то в логе сниппета — это условие с FIND_IN_SET исчезает чудесным образом). Не долго думая, я поправил это недоразумение, и тк. функция FIND_IN_SET возвращает число — позицию строки в списке строк, разделенных запятыми, то логично, чтобы were сработало корректно нам нужно сравнить результат функции с каким-то числом, а точнее с нулем Запись на феноме (кстати, учите феном и делайте на нем):
Если необходима поддержка отображения вложенностей, то это немного другая сказка, которую я смогу рассказать как-нибудь в другой раз.
Но суть там другая, проще создать отдельную таблицу в которой хранить id категории и id товара.
Тогда можно будет через join цеплять входящие в множество категории все товары к ним.
Но проще установить Minishop2 для каталога и не заниматься фигней. :)
Надеюсь, не утомил и жажду утолил.
Всего хорошего!
PS. теперь можно открыть прохладную шипучку и релакснуть.
Обновлено 30 июля 2023г.
Недавно глубоко озадачился этим решением и в результате которого нехитрым (почти) способом можно сделать мультикатегории.
Подходит данное решение для сайтов у которых, наверное не более 5..10к товаров. На больших, думаю, будет тормозить, (не проверял) так что обратная связь приветствуется.
И так, что нам нужно?
- MODX Revolution
- TV поле multi
- Знания sql
- Знания fenom
- Бутылочка с пузыриками (выбирайте по вкусу)
1.1. В параметрах выбора у нас Тип — список множественный выбор, в возможных значениях, я обычно вписываю @EVAL (вам следует, использовать другой вариант, если хотите чтобы в MODX 3 у вас это дело не слетело (оно не поддерживается в 3-ке):
@EVAL return $modx->runSnippet('pdoResources',array(
'parents'=>4,
'where'=>'{"isfolder:=":1}','depth'=>5,'limit'=>0,'sortby'=>'{"pagetitle":"ASC"}',
'tpl'=>'@INLINE [[+pagetitle]] - ([[#[[+parent]].pagetitle]])==[[+id]]',
'outputSeparator'=>'||','tplWrapper'=>'@INLINE Нет==||[[+output]]'));
В tpl Вы можете сделать как хочется вам у меня маска вывода такая: КАТЕГОРИЯ (родительская категория «на всякий случай»).В parents — родители из которых брать категории.
where — можно или Isfolder или template использовать — как хотите.
1.2. В параметрах вывода выбираем РАЗДЕЛИТЕЛЬ , (запятая).
1.3. остальное по вкусу. (для каких шаблонов и названия, описания)
В товаре теперь у нас есть TV-шка которая складирует ID дополнительных категорий.
2. настраиваем вывод pdoResource:
2.1. Сложность в том что мультиселект хранит данные в формате: 23||67||288 те с разделителем ||. поэтому выражения с where типа:
a) 'where'=> ["multi:IN" => [99,88] ]
б) 'where'=> ["multi:=" => 88]
в) 'where'=> ["multi:LIKE" => "%88%"]
Не будут корректно работать.1й вернет товары у которых только одна доп категория, которая равна 88 или 99.
2й вернет товары у которых только одна доп категория, которая равна только 88.
3й вернет все товары, у которых встречаются совпадения с 88. (те 23||67||288 — этот товар попадет в выборку.)
4й вообще даже так записывать — не верно, ведь у нас тут не показана выборка по parent = 88 или 99 а она по умолчанию есть и получиться что нужно указывать корневую Parents = 33, но тогда попадут все товары, значит в where еще нужно дописать parent:IN 88 99. Но и этого мало.
2.2. В общем, нам нужно залайкать в нескольких вариантах multi те.:
LIKE '%|88' и LIKE '88|%' и LIKE '%|88|%' и = '88'
Как видно мы ищем совпадение в конце, совпадение в начале, совпадение в середине и чистое совпадение.Есть странный глюк в разборе where и если вы захотите написать все в стиле прилежного MODX-сера:
'where'=> ["multi:LIKE" => "%|88","OR:multi:LIKE" => "88|%","OR:multi:LIKE" => "%|88|%","multi:=" => "88"]
Это логично, и читаемо, но массив переменных это зло. И в массиве будет уничтожены (точнее перезаписаны данные) угадайте какие?Ключ в массиве должен быть уникальным, а значит OR:multi:LIKE у нас останется единственный последний («OR:multi:LIKE» => "%|88|%"). Поэтому нам нужно или поставить пробелы дополнительные чтобы ключ отличался или переписать наш код в более sql ориентированный вариант, что и сделаем ниже (вообще опускаю еще некоторые проблемы которые могут (возможно) возникнуть).
Важно сказать что вывод товаров в категории будет работать только непосредственно в конечных категориях, потому что на входе у нас 1 категория будет активная (ниже объясню почему) структура такая:
Каталог (33) (кат)2.3. Выборка в результате у нас будет такая для вывода ресурсов на фронте:
— Майки (кат) (55)
— — майка простая артикул 001 (товар) (345) (доп кат 801 и 77)
— — желетка-майка артикул 453 (товар) (987) (доп кат 88)
— Туфли (кат) (88)
— --Туфли с правой ногой (кат) (801)
— --Туфли с левой ногой (кат) (802)
— Брюки (кат) (77)
{set $itzx = $_modx->resource.id} // в зависимости от категории
{'!pdoResources' | snippet : [
'limit' => 0,
'hideContainers' => '1',
'parents' => '33',
'includeTVs' => 'multicat', // нужно обязательно добавить, иначе выборка не стработает.
'processTVs' => 'multicat',
'where' => [
0 => "(`TVmulticat`.`value` LIKE '%|"~$itzx~"' OR `TVmulticat`.`value` LIKE '"~$itzx~"|%' OR `TVmulticat`.`value` LIKE '%|"~$itzx~"|%' OR `TVmulticat`.`value` = '"~$itzx~"' OR parent = "~$itzx~")" ,
1=> [ _ ДРУГИЕ УСЛОВИЯ КАКИЕ_ТО _ ]],
]}
Важный момент обработки массивов. в начале нужно указать 0 => чтобы при обработке это получилось отдельное условие в скобках, потому что после скобок будет идти стандартный AND Например по умолчанию далее идут pulished = 1 deleted = 0, и нам важно чтобы выражение с лайками в скобках воспринималось БД при запросе одним целым, а уже 1 => вы можете добавить свои нужные условия, например template.а вот строка
OR parent = "~$itzx~"
нужна чтобы товары конкретной категории так же были приравнены к «своим».Из последнего следует маленький нюанс, что корректно данное решение будет работать только в каталогах, где вывод товаров идет непосредственно в конечных категориях.Те если мы заходим в категорию Майки (55), мы видим все 2 майки.
Если зайдем в категорию Туфли 88, то мы тут увидим только желетку-майку id987. ну и отдельным сниппетом (блоком) выбор категорий 801 и 802. а уже если зайдем в Туфли с правой ногой id801, то увидим товары которые с правой ногой и дополнительный товар Майка простая, что нам и нужно.
2.4.(UDP 30-07-2023). В комментариях справедливо подсказали более короткое решение через функцию mySql
FIND_IN_SET. Небыло случая проверить, но вот довелось. Меня смутила запись 1=1 AND в начале условия, тк не понятно было что к чему. И вот сегодня после некоторых экспериментов стало ясно. Обработка условия в where идет хитрым образом и если в строке не встречаются корректные данные (нет знака сравнения), то строка вообще вылетает из выборки. (те если я удаляю 1=1, то в логе сниппета — это условие с FIND_IN_SET исчезает чудесным образом). Не долго думая, я поправил это недоразумение, и тк. функция FIND_IN_SET возвращает число — позицию строки в списке строк, разделенных запятыми, то логично, чтобы were сработало корректно нам нужно сравнить результат функции с каким-то числом, а точнее с нулем Запись на феноме (кстати, учите феном и делайте на нем):
[0 => "0 < FIND_IN_SET(" ~ $itzx ~ ", replace(`TVmulticat`.`value`, '||', ','))"]
Таким образом и ошибок меньше и пользы больше. Спасибо @Ivan, что подсказал.Если необходима поддержка отображения вложенностей, то это немного другая сказка, которую я смогу рассказать как-нибудь в другой раз.
Но суть там другая, проще создать отдельную таблицу в которой хранить id категории и id товара.
Тогда можно будет через join цеплять входящие в множество категории все товары к ним.
Но проще установить Minishop2 для каталога и не заниматься фигней. :)
Надеюсь, не утомил и жажду утолил.
Всего хорошего!
PS. теперь можно открыть прохладную шипучку и релакснуть.
Обновлено 30 июля 2023г.
Поблагодарить автора
Отправить деньги
Комментарии: 10
Хм. Интересно, а вместо кучи лайков можно сделать так?
"1 = 1 AND FIND_IN_SET(" ~ $itzx ~ ", replace(TVmulticat`.`value`, '||', ',')) OR parent = " ~ $itzx
В теории можно еще выводить товары из дочерних категорий.
{var $childsCategories = $_modx->getChildIds($itzx) | join}
"1 = 1 AND FIND_IN_SET(" ~ $itzx ~ ", replace(TVmulticat`.`value`, '||', ',')) OR parent = " ~ $itzx ~ " OR parent IN (" ~ $childsCategories ~ ")"
Но тут уже может быть не очень быстро.
нужно будет затестить. полезная функция )
В modx 3 выпилили @EVAL. Как теперь можно запустить конструкцию
@EVAL $output = $modx->runSnippet('snippet');return '' . $output;
?
Если у категорий есть свой (а он по идее должен быть) шаблон, то можно попробовать через SELECT:
@SELECT `pagetitle`,`id` FROM `[[+PREFIX]]site_content` WHERE `published` = 1 AND `deleted` = 0 AND `template` = 15 ORDER BY pagetitle ASC;
А если нужно учитывать context_key в запросе? Для разных языков нужно брать ресурсы из соответствующего контекста. В таблице site_content есть столбец context_key.
о, как. мультиязык на MODX 3? смело.
Посмотрите в сторону @ CHUNK
ну и еще есть вариант файл, но файл нужно сгененрировать заранее:
docs.modx.org/current/ru/building-sites/elements/template-variables/bindings
Посмотрите в сторону @ CHUNK
ну и еще есть вариант файл, но файл нужно сгененрировать заранее:
docs.modx.org/current/ru/building-sites/elements/template-variables/bindings
Прозвучало как приговор)
@CHUNK в параметрах TV просто отдает строку. Т.е. если я в чанке вызываю сниппет [[!snippet]], то в результате текстом [[!snippet]], но не результат выполнения.
@CHUNK в параметрах TV просто отдает строку. Т.е. если я в чанке вызываю сниппет [[!snippet]], то в результате текстом [[!snippet]], но не результат выполнения.
А вы порпробуйте без воскл. знака сниппет в чанке вызвать.
[[Snippet]]
Благодарю!!! Работает!!!
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.