Что такое pbFavorites
pbFavorites — закладки на любую модель для сайтов на PageBlocks 3.x. Товар, заявку, статью, компанию, человека: модель называется в вызове, а не зашита в компонент.
Пакет бесплатный и магазина не требует — в этом его смысл. Закладка на что угодно универсальна: её хочет блог, каталог услуг, справочник организаций, доска объявлений. Избранное выросло внутри pbShop и умело один товар; теперь оно живёт отдельно и умеет что угодно.
Это не самостоятельное приложение. Без PageBlocks компонент пишет ошибку в лог и выключается: оттуда приходят таблицы, роутинг, контроллеры, файловые шаблоны, миграции, шаблонизатор и — отдельно важное — механика анонимной сессии.
Нужен PageBlocks alfa24 или новее
Гостевое владение по cookie (Support\GuestToken) живёт во фреймворке с alfa24. На версии старше пакет выключается с записью в лог, а не работает «только для вошедших»: молча урезанное избранное хуже выключенного — кнопка есть, нажатие проходит, список пуст.
Что делает компонент и чего не делает
Это главная мысль, и дальше вся документация поделена именно по ней.
Компонент хранит и переключает. Таблица, уникальность пары «владелец + объект», гость по токену, слияние при входе, предел списка, чистка брошенного, счётчик, прогрев страницы.
Поставщик модели отвечает на три вопроса, потому что ответить на них может только тот, чьи это строки:
| Вопрос | У товара | У заявки на груз |
|---|---|---|
| Что считать видимым | выложен (published()) | не просрочена и не снята |
| Как нарисовать строку | цена «от», наличие, «в корзину» | маршрут, дата, вес |
| Какой у записи адрес | от главной категории, которой может не быть | от номера заявки |
Без этого разделения универсальное избранное либо печатает удалённое, либо заводит вторую копию правила видимости — и та однажды разойдётся с первой.
Один результат — два пути
// Вёрстка — шаблон MODX
[[!pbFavoriteButton? &model=`pbshop.product` &id=`[[+id]]`]]
// Вёрстка — Fenom в файловом шаблоне
{'!pbFavoriteButton' | snippet: ['model' => 'pbshop.product', 'id' => $product->id]}
// Разработчик — прямой вызов, сниппет не нужен вовсе
$favorites = modx()->services->get('pbfavorites');
$favorites->has('pbshop.product', $product->id);Ни один путь не «правильнее» и не проходит через другой: верстальщик не обязан открывать IDE, разработчик не обязан пользоваться сниппетами.
Паритет двусторонний и проверяется тестом: срез и порядок (&limit, &offset, &sortby, &sortdir) считает сервис, а сниппет их только разбирает — поэтому возможности, доступной лишь в шаблоне, у пакета нет.
Свою логику вокруг закладок сайт вешает событиями — добавлено, убрано, отказано, очищено, слито при входе, — а не правкой компонента.
Словарь параметров — тот же, что у pbShop
&tpl, &tplWrapper, &tplEmpty, &limit, &offset, &sortby, &sortdir, &outputSeparator, &toPlaceholder, &showLog. Своих имён пакет не вводит и синонимов не плодит: выучивший вызов в магазине пишет вызов в закладках, не открывая документацию.
⚠️ Реестр моделей — это безопасность, а не удобство
Короткий ключ (pbshop.product) приезжает из браузера: он в теле запроса и в атрибуте кнопки. Без белого списка любой желающий пошлёт model=modxUser&id=1 и получит строку в таблице, а поставщик вывода однажды напечатает из неё то, что печатать не должен.
Поэтому модель обязана быть зарегистрирована компонентом вместе со своим поставщиком, а незнакомый ключ — отказ, а не «заведём строку и разберёмся потом». Снаружи ключ короткий, в базе лежит полное имя класса: ключ из запроса не превращается в произвольный класс, а модель находится однозначно.
Гость — наравне с вошедшим
«Сначала войдите» на кнопке — это отказ по нажатию, то есть ровно то, чего избранное должно избегать: закладку ставят раньше, чем решают заводить учётную запись.
Гость опознаётся токеном в cookie pb_wish (180 дней). При входе его список переезжает на учётную запись, дубли выбрасываются. Слияние предел не проверяет: выбросить закладки, которые человек ставил сам, ради ровного числа хуже, чем список на сто один объект.
Токен выдаётся только на первом действии. Страница сама по себе cookie не заводит — иначе она появлялась бы у каждого прохожего, и HTTP-кеш перестал бы работать на всём сайте.
Откуда он такой
Пакет собран из двух работавших реализаций, а не написан с нуля, и у каждой половины своя причина быть:
| Откуда | Что дало |
|---|---|
| универсальное избранное живого проекта (грузы, транспорт, компании, консультанты, люди) | морф-таблица, белый список моделей, прогрев страницы одним запросом, счётчик наблюдателей, insertOrIgnore против гонки двух вкладок |
| pbShop | гость по токену, предел, слияние при входе, чистка брошенного |
Ни одна из двух не покрывала другую: у первой не было гостей, у второй — ничего, кроме товара.
Куда идти дальше
- Быстрый старт — установить, напечатать кнопку, собрать страницу списка. Без PHP.
- Разработчику — контракт поставщика, реестр, сервис, события, схема, крон.