Skip to content

Установить и вызвать ​

Эта страница — для вёрстки: PHP здесь не нужен. Всё, что ниже, пишется в шаблоне MODX и в чанках.

Установка ​

  1. Поставьте PageBlocks alfa24 или новее — без него компонент выключится с записью в лог.
  2. Поставьте пакет pbFavorites через «Установщик».
  3. Проверьте журнал установки: миграция создаёт таблицу pb_favorites и пишет об этом строкой.

Миграции идут отдельным шагом

Если на хостинге отключена функция exec(), резолвер честно скажет об этом в журнале и подскажет команду. Таблицу тогда нужно создать руками:

bash
php core/components/pageblocks/vendor/bin/phinx migrate \
    --configuration=core/components/pbfavorites/src/phinx.php --environment=production

Что должно быть установлено ещё ​

Сам по себе пакет ничего не покажет, и это не недоработка: он не знает ни одной модели, пока её кто-нибудь не зарегистрирует. Регистрируют спутники:

  • pbShop с alfa44 регистрирует pbshop.product и больше ничего не требует;
  • свою модель регистрирует сайт — одной строкой, см. раздел разработчика.

Если закладки «не работают», первым делом посмотрите, что печатает кнопка: при незнакомой модели она отвечает текстом с перечислением известных ключей, а не молчит.

Кнопка в карточке ​

[[!pbFavoriteButton? &model=`pbshop.product` &id=`[[+id]]`]]

&model и &id обязательны. Кнопка сама знает своё состояние («отложить» или «отложено») и сама меняет подпись после нажатия.

Только некешируемым вызовом

[[! с восклицательным знаком — обязательно, во всех трёх сниппетах. Состояние закладок персональное; кешированный тег отдал бы список первого посетителя всем остальным.

Со счётчиком «сколько человек отложило»:

[[!pbFavoriteButton? &model=`pbshop.product` &id=`[[+id]]` &watchers=`1`]]

Без &watchers счётчик не считается вовсе — лишний запрос на каждую карточку нужен не всякой витрине.

Счётчик в шапке ​

[[!pbFavoritesCount]]

Без &tpl возвращает одно число, без разметки: счётчик обычно печатают внутрь своего элемента, и обёртка вокруг него мешала бы.

html
<a class="header-wish" href="/favorites">
    Избранное <span>[[!pbFavoritesCount]]</span>
</a>

Считаются видимые закладки: снятое с показа в списке не печатается, и число над ним обещало бы то, чего человек не увидит.

Только товары — [[!pbFavoritesCount? &model=pbshop.product]].

Страница списка ​

Создайте ресурс (например, с алиасом favorites) и позовите в нём:

[[!pbFavorites]]

Без &model печатаются все закладки посетителя: у блога и магазина на одном сайте список один. Только товары:

[[!pbFavorites? &model=`pbshop.product` &tpl=`myFavoriteRow`]]

Страницу держит сайт, а не компонент

Своего адреса у списка нет намеренно. Закладывают на таком сайте и статьи тоже, поэтому адрес вида «/каталог/favorites» обещал бы, что это раздел магазина. Ресурс и его адрес — ваши.

Параметры ​

Словарь общий для всех трёх сниппетов и совпадает с pbShop.

ПараметрЧто делает
&modelключ модели (pbshop.product). Без него — все закладки
&idid объекта. Только pbFavoriteButton, обязателен
&tplчанк строки. У pbFavoritesCount его отсутствие значит «отдай числом»
&tplWrapperчанк обёртки списка; внутри доступны {$output} и {$total}
&tplEmptyчанк пустого списка
&limit, &offsetсрез списка. &limit=0 — без ограничения
&sortby, &sortdirпорядок по полю строки; по умолчанию — последнее отложенное сверху
&outputSeparatorчем склеить строки
&toPlaceholderвывод в плейсхолдер, сниппет возвращает пустоту
&showLogпоказать журнал вызова (видно только вошедшему в менеджер)
&watchersсчитать «сколько человек отложило». Только pbFavoriteButton
&currencyвалюта строк. Передаётся поставщику насквозь

Опечатка не молчит

Незнакомый &model, пропущенный &id, несуществующее поле в &sortby — всё это печатается текстом в точке вызова и уходит в &showLog. Пустой список и опечатка на витрине выглядят одинаково, поэтому компонент их различает за вас.

То же самое есть и в коде

&limit, &offset, &sortby и &sortdir считает сервис, а не сниппет — поэтому разработчик рядом с вами получает их тем же вызовом $favorites->all($model, [], ['limit' => 20]). Это не мелочь для смешанного проекта: возможность, которая есть только в шаблоне, однажды приходится переписывать заново.

Готовый раздел «Избранное» ​

Копируется целиком. Три сниппета, свой чанк строки, скрипт — дальше правится только разметка.

1. Ресурс с алиасом favorites и таким содержимым:

[[!pbFavorites? &tpl=`myFavoriteRow`]]

2. Чанк myFavoriteRow — за основу берётся поставочный pbFavorites.row.tpl. ⚠️ Чанки компонента рисует Fenom, поэтому поля пишутся {$item.title}, а не [[+title]]:

html
<div class="card" data-pbfav-row data-model="{$item.model}" data-id="{$item.item_id}">
    <a href="{$item.url}"><img src="{$item.image}" alt="{$item.title}"></a>
    <a class="card-title" href="{$item.url}">{$item.title}</a>
    {if $item.formatted}<span class="card-price">{$item.formatted}</span>{/if}
    <button type="button" class="card-remove"
            data-pbfav-toggle="{$item.model}" data-pbfav-id="{$item.item_id}" aria-pressed="true">
        {'pbfav_remove'|lexicon}
    </button>
</div>

3. Ссылка в шапке — в общем шаблоне сайта:

html
<a href="/favorites">Избранное <span>[[!pbFavoritesCount]]</span></a>

4. Кнопка в карточке каталога — туда, где выводится товар:

[[!pbFavoriteButton? &model=`pbshop.product` &id=`[[+id]]`]]

5. Скрипт и токен — один раз в шаблоне, см. раздел «Подключить скрипт» ниже.

Строка списка — от поставщика

{$item.formatted} и {$item.image} даёт поставщик товаров pbShop. У своей модели поля будут другие, и их список знает тот, кто писал поставщика.

Своя разметка ​

Цепочка такая: &tpl → чанк в дереве менеджера → файл поставки.

Записи при установке не заводятся. Чтобы перебить разметку, создайте чанк с нужным именем — обновление пакета его не тронет, потому что записей он не трогает вовсе:

Чанк и файлЧто рисует
pbFavorites.rowстроку списка
pbFavorites.listобёртку списка
pbFavorites.emptyпустой список
pbFavorites.buttonкнопку
pbFavorites.countсчётчик (когда позван с &tpl)

В дереве менеджера этих чанков нет

И это осознанная цена, а не недоработка: пакет ставится без резолвера элементов, поэтому записи не заводятся — и найти поставочную разметку, листая дерево, нельзя. Имена берите из таблицы выше.

Файлы лежат в core/components/pbfavorites/elements/chunks/ и названы так же, как записи: pbFavorites.row.tpl, pbFavorites.list.tpl и так далее. Самый короткий путь — скопировать файл, создать чанк с тем же именем, править его.

Чанки рисует Fenom, а не парсер MODX: внутри пишется {$item.title}, а не [[+title]]. Полное имя файлового шаблона — file:pbFavorites:pbFavorites.row, но писать его в &tpl обычно незачем: без параметра дефолт находится сам.

Что доступно в строке ​

Строку собирает поставщик, поэтому набор полей зависит от модели. Пакет добавляет к ним свои два:

ПолеОткуда
{$item.model}пакет: ключ модели
{$item.item_id}пакет: id объекта
{$item.title}, {$item.url}, {$item.image}, {$item.formatted}поставщик товара pbShop

Поля берите мягко — чего поставщик не дал, то не печатается:

{if $item.formatted}<span class="price">{$item.formatted}</span>{/if}

Что нужно скрипту ​

Скрипт пакета (assets/components/pbfavorites/js/favorites.js) ищет в разметке атрибуты. Если пишете свои чанки, сохраните их:

АтрибутНа чёмЗачем
data-pbfav-toggle="<модель>" + data-pbfav-idкнопкапо нажатию переключает
data-pbfav-label-add, data-pbfav-label-inкнопкаподписи состояний; без них скрипт не меняет текст
data-pbfav-countлюбой элементобновляется числом
data-pbfav-rowстрока спискаубранная строка исчезает
data-pbfav-clearкнопкаочистка, в два нажатия
data-pbfav-confirmта же кнопкаподпись подтверждения
data-pbfav-errorлюбой элементкуда напечатать отказ сервера

Разметку скрипт не собирает

Новые строки в списке не появляются без перезагрузки, и это решение: собирай их скрипт, вёрстка жила бы в двух местах — в чанке и в JS, — и правка чанка молча не доезжала бы до перерисованного списка. Закладку ставят в каталоге, а смотрят в списке; эти два места редко открыты одновременно.

Подключить скрипт ​

Пакет не вставляет себя в страницу: чужой шаблон — не его дело. Добавьте в свой шаблон:

html
<script src="/assets/components/pbfavorites/js/favorites.js" defer></script>

И токен — запросы пакета идут POST'ом, PageBlocks их проверяет. В файловом шаблоне на Fenom это одна строка в <head>:

{meta_csrf}

Скрипт ищет токен в meta[name="csrf-token"], а если его нет — в input[name="_token"] на странице. Так что в шаблоне MODX, где функции Fenom недоступны, достаточно, чтобы на странице была любая форма PageBlocks с её скрытым полем токена.

Без токена кнопка «просто не работает»

Запрос уйдёт и вернётся отказом, а на витрине это выглядит как сломанная кнопка. Если нажатие ничего не делает — посмотрите в консоли ответ /favorites/toggle первым делом.

Без скрипта ​

Кнопка без JS не работает — она шлёт запрос. Если скрипт не нужен, список всё равно печатается сниппетом: страница с [[!pbFavorites]] показывает отложенное и без единой строки JavaScript.

pbFavorites — закладки на любую модель, бесплатно