Таблицы
Повторяющаяся структура внутри объекта: слайды в слайдере, характеристики товара, участники команды. Таблица описывается один раз, дальше подключается к блоку полем типа Таблица — и редактор наполняет её строками прямо в форме блока.
PageBlocks → Конструктор → Таблицы.
Конструктор таблицы
| Настройка | Что делает |
|---|---|
| Название | Как таблица подписана при выборе. Обязательное |
| Модель | Где лежат строки — см. ниже. Обязательное |
| Пагинация | Разбивать ли список на страницы. Включена по умолчанию |
| Поиск | Строка поиска над списком — и та же галочка делает таблицу источником глобального поиска |
| Модерация | Новые строки ждут проверки — см. Модерация. Галочки нет, пока выключена настройка pageblocks_moderation |
Вкладки: Поля, Вкладки, Столбцы, Поиск, Фильтрация, Действия, API.
Разделение стоит понять сразу: поля — то, что редактор заполняет; столбцы — то, что он видит в списке. Это разные наборы, и они не обязаны совпадать. Поле может быть доступно поиску или фильтру, вообще не имея столбца.
Отдельной таблицы в базе не появляется
Строки всех таблиц лежат в одной pb_table_data, а значения — JSON-ом в колонке data. Принадлежность задаётся тремя ссылками: model_type / model_id — чей это блок, constructor_id — какая таблица, field_id — какое поле.
Что из этого следует
Завести таблицу ничего не стоит: миграций не нужно, схема не меняется. Обратная сторона — искать по «колонке» такой таблицы придётся через JSON_EXTRACT, обычного индекса по ней нет. Для десятков строк на блок это неважно, для десятков тысяч — повод подумать о своей модели.
Столбцы
| Настройка | Что делает |
|---|---|
| Тип колонки | Поле, группа полей или первое непустое из нескольких |
| Поле | Какое поле показывать |
| Заголовок | Переопределяет подпись поля |
| Ширина | В пикселях, до 600. 0 — подобрать автоматически |
| Рендерер | Как показывать значение |
| Значение по умолчанию | Что выводить, когда пусто |
| Сортировка | Разрешить сортировку по этой колонке |
Рендереры: text (длинный текст обрезается), html, image, video, date, boolean, button, color. Колонкам с иконкой и статусом рендерер подставляется сам.
Тип группа собирает несколько полей в одну ячейку, первое непустое — берёт из списка полей то, у которого есть значение. Оба нужны, когда столбцов много, а места мало.
Вывод в шаблоне
Значение поля-таблицы — это массив строк:
<div class="slides">
{foreach $slides as $slide}
<div class="slide">
<h3>{$slide.title}</h3>
<img loading="lazy"
src="{$slide.image.url}"
width="{$slide.image.width}"
height="{$slide.image.height}"
alt="{$slide.image.title}">
</div>
{/foreach}
</div>Необязательное поле проверяем перед выводом:
{if $description}
<p>{$description}</p>
{/if}В шаблон попадают не все строки
Только опубликованные: дата публикации заполнена и не в будущем, строка не удалена. Порядок — тот, что задан перетаскиванием. Строка, которой «нет на сайте», обычно просто не опубликована.
Пресеты: раздел без пятнадцати минут кликов
Каталог — это таблица, пять полей, колонки грида, пункт меню, которым её открывают, и чанк, которым её выводят: четверть часа кликанья, в котором любой шаг можно пропустить. Тот же раздел поставляется рецептом:
# сперва план, ничего не пишется
php core/components/pageblocks/vendor/bin/phinx seed:run \
-c core/components/pageblocks/src/phinx.php -s PresetCatalogSeed
# запись
PB_APPLY=1 php … -s PresetCatalogSeedПлан перечисляет, что именно будет создано; запись включает PB_APPLY=1. Второй прогон не создаёт ничего — всё сперва ищется по имени. После записи сид печатает вызов, который остаётся вставить в страницу:
[[!pbList? &model=`PbTableData` &where=`{"constructor_id":50}` &tpl=`pageblocks.card` &sortby=`menuindex` &limit=`12`]]Вместе с разделом приезжают две демо-строки — чтобы этот вызов сразу выводил товары, а не пустоту. Дальше правится pageblocks.card: это обычный чанк в дереве элементов.
Раздел можно унести целиком, вместе с заведёнными в нём строками:
PB_REMOVE=1 php … -s PresetCatalogSeed # план
PB_REMOVE=1 PB_APPLY=1 php … -s PresetCatalogSeed # записьСвой пресет
Переиспользуемая часть — Boshnik\PageBlocks\Database\Presets\Builder: table(), tab(), field(), menu(), row(), remove(), все идемпотентные и понимающие режим плана. Новый рецепт — сид строк на сто рядом с PresetCatalogSeed, в основном список полей. Пресету не нужны ни своя миграция, ни своя модель: строки лежат в общем pb_table_data под своим constructor_id — ровно так, как работает таблица, собранная в конструкторе руками.
Модель: обычная таблица или ресурс
Модель в настройках — это выбор хранилища.
PbTableData— то, что описано выше: строка живёт в общем хранилище и существует внутри своего блока.PbResource— строка попадает вpb_resourcesи получает собственные колонки ресурса:pagetitle,alias,uri,template,content,menuindex,hidemenuи прочие. Такая строка умеет иметь собственный адрес и собственный шаблон.
Второе нужно, когда строка — это по сути страница: карточка товара, статья каталога, объект справочника. Обычные ресурсы MODX при этом остаются на месте — pb_resources живёт своей таблицей и не подменяет site_content.
Список таких строк можно фильтровать и сортировать как полноценный каталог:

