Skip to content

Секции ресурса (PRO)

Свои поля на обычной форме ресурса MODX — замена куче TV.

Вы создаёте секцию, кладёте в неё поля, и секция появляется на форме каждого ресурса. PageBlocks → Конструктор → Объекты, с контекстом ресурса.

Секция

НастройкаЧто делает
НазваниеЗаголовок вкладки или панели
КонтекстpbResource — секция относится к форме ресурса
Размещениеtab, panel или default — как она появляется на форме
ОписаниеТекст над полями
ПозицияПорядок среди секций
ПраваКакие пользователи и группы её видят
ОпубликованаНеопубликованная секция не рисуется

Размещение

ЗначениеКуда попадают поля
tabОтдельная вкладка на форме ресурса
panelСворачиваемая панель
defaultСреди стандартных полей, без контейнера

tab — безопасный вариант для всего, где больше двух полей. default — для одного-двух полей, которым место рядом с родными полями ресурса: например, переключателя, меняющего поведение страницы.

Где лежат значения

В site_content.properties, в виде JSON. Не в новой колонке и не в таблице TV.

Это важно в две стороны:

  • добавление поля не требует миграции, а удаление оставляет данные на месте, а не роняет колонку;
  • значение путешествует вместе с ресурсом — дублирование ресурса копирует и его, потому что properties MODX копирует сам.

Обычная выборка properties не разворачивает

Resource::find($id)->seo_title вернёт null, даже когда значение есть. Поле лежит внутри JSON, и разворачивает его только форма ресурса.

В коде читайте явно:

php
$page = Resource::find($id);
$title = $page->properties['seo_title'] ?? '';

В шаблоне текущей страницы поля уже развёрнуты — их разворачивает контроллер.

Таблица и галерея — исключение

tablefield и galleryfield в properties не попадают. Это не недоделка, а разница в природе значения: у текстового поля значение одно — строка; у таблицы их столько, сколько строк завёл редактор, и у каждой свой набор полей; у галереи — сколько файлов загрузили. Такое в JSON ресурса не кладут, иначе набор нельзя ни отсортировать, ни показать постранично, ни отдать через API.

Поэтому оба типа держат свои записи в собственных таблицах, а к ресурсу привязываются тремя колонками:

КолонкаЧто в ней
model_typeclass_key ресурса, у обычной страницы — MODX\Revolution\modDocument
model_idid ресурса
field_idid самого поля в конструкторе
Тип поляКуда пишутся записи
tablefieldтаблица модели, выбранной у таблицы; по умолчанию pb_table_data
galleryfieldpb_files

Сохранение секции оба типа пропускает намеренно: их записи уже в базе. Грид и загрузчик пишут их своими запросами по мере работы, и нажатие «Сохранить» им не нужно — в отличие от остальных полей, которые попадают в ресурс именно в этот момент.

Выбирайте по field_id, а не по морфу

field_id указывает на одно-единственное поле одной секции, поэтому пары model_id + field_id достаточно: строки соседнего поля той же страницы под выборку не попадут. А model_type в базе встречается и коротким именем (modDocument), и полным (MODX\Revolution\modDocument) — условие по нему молча теряет половину строк.

У нового ресурса привязывать не к чему

Пока ресурс не сохранён, у него нет id, и секции на форме не рисуются вовсе. Сначала создайте страницу и сохраните, потом заполняйте таблицу и галерею.

Таблица на форме ресурса

Зачем

Есть страницы, содержимое которых — не текст, а список однотипных записей: слайды промо-блока, характеристики товара, программа тура по дням, тарифы, вопросы и ответы, состав команды, приложенные документы.

В MODX такое обычно делают одним из двух способов, и оба неудобны. Либо TV с разделителями — редактор пишет Москва||Тверь||Клин, шаблон разбирает строку, а добавить к пункту картинку уже нельзя. Либо отдельный ресурс на каждый пункт — тогда в дереве сайта заводится сотня страниц, которые никто не открывает.

Поле-таблица даёт то, чего нет ни у того, ни у другого: у каждой строки свои поля своих типов (число, дата, картинка, richtext, селект), строки переставляются перетаскиванием, их можно публиковать и снимать с публикации по одной, а на фронте — сортировать, фильтровать и листать.

Как завести

  1. PageBlocks → Конструктор → Таблицы — создайте таблицу и опишите её поля. Это шаблон строки: title, text, image, price.
  2. PageBlocks → Конструктор → Объекты — создайте секцию с контекстом pbResource и размещением tab.
  3. В секции добавьте поле типа Таблица, укажите у него имя и выберите таблицу из пункта 1.

Настройки поля

НастройкаЧто делает
ТаблицаКакой конструктор таблицы показывать
Только просмотрСтроки показываются списком: без кнопки создания, меню строки и редактирования
Поле для сортировкиПо какой колонке упорядочить, по умолчанию menuindex
НаправлениеПо возрастанию или по убыванию
Записей на страницеПо умолчанию 20

Ширина принудительно выставляется во всю строку и не меняется — грид в узкой колонке бесполезен.

Пример: программа тура

Таблица TourDay с полями day (число), title (текст), text (richtext), image (картинка). Секция «Программа», контекст pbResource, размещение tab. В ней одно поле: подпись «Дни», имя days, тип «Таблица», таблица — TourDay.

Редактор открывает страницу тура, переходит на вкладку «Программа» и заводит дни как строки грида. На фронте:

[[!pbList?
    &model=`PbTableData`
    &parent=`[[*id]]`
    &where=`[["field_id","=","12"]]`
    &tpl=`chunk:tourDay`
    &orderBy=`menuindex`
    &limit=`30`
]]

Чанк tourDay получает строку в переменной item:

html
<div class="tour-day">
    <h3>День {$item->day}. {$item->title}</h3>
    {if $item->image}<img src="/{$item->image}" alt="{$item->title|escape}">{/if}
    {$item->text}
</div>

12 — это id поля: он виден в колонке id грида полей конструктора. model=PbTableData годится, пока таблица пользуется моделью по умолчанию; если под неё сделана своя таблица через UI-миграции, подставьте имя её модели.

Остальные параметры — в pbList: &limit, &loadmore, &scopes, свои условия в &where.

Чего ждать не стоит

{$modx->resource->days} вернёт пусто

У ресурса нет свойства с именем поля-таблицы: строки лежат в своей таблице, и разворачивать их у ресурса некому. Пустота при этом не отличается от опечатки в имени — ошибки не будет. Читайте запросом, как в примере выше. То же касается и галереи.

Галерея на форме ресурса

Зачем

Одно поле-картинка закрывает обложку. Всё, что больше — фотоотчёт объекта, галерея работ, сертификаты, комплект документов к странице, — это набор файлов с порядком и подписями. Собирать его из десяти полей image_1 … image_10 бессмысленно: порядок не переставить, лишние поля висят пустыми, одиннадцатая фотография требует миграции.

Галерея даёт один загрузчик на всю страницу: файлы кидаются пачкой, порядок задаётся перетаскиванием, у каждого файла своя подпись и описание. Файлы — любые, не только картинки: видео и PDF лежат рядом с фотографиями, различает их колонка type.

Настройки поля

НастройкаЧто делает
ИсточникИсточник файлов MODX, в который грузим
Путь к файламПапка внутри источника
МиниатюрыКакие размеры генерировать, описываются JSON-ом

Путь понимает плейсхолдеры {resource_id}, {user_id}, {alias} и {id}. На форме ресурса самый полезный — первый:

/gallery/{resource_id}/

Файлы каждой страницы попадают в свою папку, и на диске видно, что чьё. Без плейсхолдера все галереи сайта складываются в один каталог, и через год там десять тысяч файлов.

Миниатюры описываются так:

json
{"webp": {"w": 1200, "h": 800, "q": 85, "zc": "1", "f": "webp"}}

Получатся картинки 1200×800 в WebP, в папке webp рядом с оригиналами.

Пример: фотоотчёт объекта

Секция «Фотографии», контекст pbResource, размещение tab. Поле: подпись «Фотоотчёт», имя photos, тип «Галерея», источник — основной, путь /gallery/{resource_id}/.

Файлы галереи сниппетом не выбрать: pbList ходит по моделям PageBlocks\App\Models\, а PbFile — модель фреймворка. Поэтому запрос пишется сам — в контроллере или сниппете, а в шаблон уходит готовый список:

php
use Boshnik\PageBlocks\Models\PbFile;

$photos = PbFile::query()
    ->where('model_id', $modx->resource->id)
    ->where('field_id', 18)
    ->published()
    ->orderBy('menuindex')
    ->get();
html
<div class="gallery">
    {foreach $photos as $photo}
        {if $photo->type === 'image'}
            <img src="/{$photo->url}" alt="{$photo->title ?: $photo->name}">
        {/if}
    {/foreach}
</div>

18 — снова id поля из грида конструктора. Полезные колонки строки: url, title, description, type, width, height, menuindex. url хранится без ведущего слэша — отсюда /{$photo->url} в примере.

Загруженный файл публикуется сразу, поэтому скоуп published() отсекает только то, что редактор снял с публикации руками.

Ветвитесь по type

В галерее лежат любые файлы, не только картинки: видео и PDF уживаются рядом с фотографиями. Без проверки type в вёрстку поедет <img> с PDF внутри.

Копирование и удаление ресурса

Дублирование ресурса копирует и то и другое. MODX сам копирует properties, то есть обычные поля секции, а PageBlocks на OnResourceDuplicate повторяет строки таблиц и записи галерей с новым model_id. Копируются записи, а не файлы на диске: новая строка галереи указывает на тот же файл.

Удаление ресурса записи не трогает. Удаление в MODX мягкое, страницу можно восстановить из корзины — вместе с таблицами и галереей. Но после очистки корзины строки остаются в своих таблицах привязанными к несуществующему id: на сайте их не видно, место они занимают. Чистить — вручную, по model_id.

Сохранение

Значения собираются на OnBeforeDocFormSave и записываются в ресурс. От вас ничего не требуется — достаточно секции.

Про то же самое для пользователей — Поля пользователя: механизм тот же, отличается контекст.

© PageBlocks 2019-present