Как сделать мультикатегории на 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г.
Поблагодарить автора
Отправить деньги
Комментарии: 17
Хм. Интересно, а вместо кучи лайков можно сделать так?
"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]]
Благодарю!!! Работает!!!
Всем привет, пытаюсь в этот отбор добавить второе условие на проверку заполненности поля players_settings
Но что не получается, так ничего не выводит:
Но что не получается, так ничего не выводит:
'where' => [
0 => "0 < FIND_IN_SET(" ~ $itzx ~ ", replace(`TVplayers_game`.`value`, '||', ','))" ,
1 => '{"players_settings:!=":null}',
],
Подскажите как правильно прописать условие 1 => '{«players_settings:!=»:null}'?
А зачем вам условие players_settings != null?
Ведь вы по умолчанию проверяете уже в каких категориях состоит ресурс через 1е условие.
Или опишите задачу.
Ведь вы по умолчанию проверяете уже в каких категориях состоит ресурс через 1е условие.
Или опишите задачу.
Вторым условием нужно убрать ресурсы, у которых поле players_settings не заполнено
Зачем второе условие если первым вы уже это делаете.
Если нет совпадений по первому, то не выведет ничего. Тем более пустое.
Если нет совпадений по первому, то не выведет ничего. Тем более пустое.
Первым условием мы смотрим «категорию», а вторым условием нужно исключить ресурсы где не заполнено другое TV
А, ясно. Попробуйте так:
'where' => [
0 => "0 < FIND_IN_SET(" ~ $itzx ~ ", replace(`TVplayers_game`.`value`, '||', ','))" ,
1 => "`TVplayers_settings`.`value` IS NOT null",
],
Если это не сработает, то попробуйте так:'where' => [
0 => "0 < FIND_IN_SET(" ~ $itzx ~ ", replace(`TVplayers_game`.`value`, '||', ',')) AND `TVplayers_settings`.`value` IS NOT null"
],
Первый вариант работает, второй тоже работает
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.