Новые возможности PageBlocks: улучшенная работа с блоками, таблицами, полями и мультиязычностью
Блоки
Теперь список блоков в PageBlocks выровнен по левому краю для лучшей читаемости, а также добавлена группировка блоков.

Чтобы добавить группы для блоков в режиме manager, нужно заполнить системную настройку pageblocks_block_groups, указывая названия групп через запятую.
В режиме разработки достаточно добавить метод group для каждого блока:
Block::make('Slider')
->group('Table')
Block::make('Services')
->group('Service')
Cинхронизация многоразовых блоков.
Многоразовые блоки — это очень удобно. Вы создаете блок один раз, наполняете его нужной информацией, а затем используете его на различных страницах. Самое важное преимущество — синхронизация изменений. Если вы редактируете любой из этих блоков, все его копии на других страницах автоматически обновляются. Это избавляет от рутинной работы по внесению одинаковых правок в каждый блок в отдельности.
Но что делать, если не все поля блока нужно синхронизировать? Раньше в таких случаях приходилось создавать новый блок, что сводило на нет преимущества многоразового использования. Однако сейчас эта проблема решена. Для каждого блока можно выбрать, какие поля будут синхронизированы, а какие останутся уникальными для каждой страницы.

Таблицы
Добавлены настройки:
- Пагинация
- Поиск
- Значения по умолчанию для столбцов таблицы
Добавлен рендер цвета

Поля
Поле JsonGrid
Добавлено новое поле JsonGrid — это простая и удобная таблица, которая позволяет редактировать данные прямо в ячейках. Все изменения сохраняются в формате JSON.
В режиме разработки
Field::make('Team')
->type('jsongrid')
->fields([
Field::make('Name'),
Field::make('Age')
->type('number'),
Field::make('Skills'),
Field::make('Location')
->type('select')
->options([
'New York',
'San Francisco',
'London',
'Berlin',
'Sydney',
'Toronto'
]),
]),
Переключаемое поле

Field::make('Danger')
->type('toggle')
->checked()
->danger(),
Field::make('Warning')
->type('toggle')
->checked()
->warning(),
Field::make('Green')
->type('toggle')
->checked(),
Сниппеты
Добавлена новая системная настройка pageblocks_parser, которая управляет тем, какой парсер будет использоваться для сниппетов. Доступны три варианта:
- modx – стандартный парсер MODX.
- pdoTools – парсер от pdoTools.
- pbFenom – встроенный шаблонизатор Fenom в PageBlocks.
- chunksPath – путь к чанкам (по умолчанию chunks).
- fields – определяет, какие поля получать из базы.
- exclude – исключает определённые поля из выборки.
- tpl.wrapper.nameField – чанк-обёртка для поля
- tpl.item.nameField – чанк для каждой итерации, если значение поля массив
{'!pbResources'|snippet: [
'resources' => '1,2,3',
'tables' => 'seo_list as list'
'tpl' => 'pb-seo-card',
'tpl.wrapper.list' => '@INLINE <ul class="text-start">{$list}</ul>',
'tpl.item.list' => '@INLINE <li>{$title}</li>'
]}
В каждом чанке, включая tpl.item.list, доступны следующие плейсхолдеры:
- _idx – индекс текущего элемента.
- _total – общее количество элементов.
- _first – первый элемент в списке.
- _last – последний элемент в списке.
- pbBlocks – для вывода блоков.
- pbTables – для вывода таблиц.
- pbCollections – для вывода коллекций. Будет удален в версии 2.6.0. Вместо этого используйте pbTables
- pbFiles для вывода файлов.
- pbResources – для вывода ресурсов.
- pbLang – для вывода языков.
- pbMenu – для вывода меню.
- pbMap – для вывода карты.
- pbRelationship – для вывода отношений.
- pbJson – для обработки JSON. Можно использовать как модификатор Fenom.
Вкладка Поля ресурса
Вкладка Поля ресурса теперь более логична и функциональна, чем вкладки Коллекции и Ресурсы, и была добавлена с целью заменить их. В связи с этим вкладки Коллекции и Ресурсы помечены как устаревшие и будут удалены в версии 2.6.0.
Например, можно легко создать вкладку Галерея в ресурсе и добавить поле галерея.

В режиме разработки еще легче
Tab::make('Gallery')
->fields([
Field::make('Images')
->type('gallery')
->sourcePath('/assets/images/')
->thumbnails(' {"webp":{"w":120,"h":90,"q":90,"zc":"1","bg":"000000","f":"webp"}}')
]),
Если вы уже используете коллекции и хотите перейти на поле ресурса, чтобы не заполнять данные повторно, вы можете выполнить следующий код в консоли:
$rows = $modx->getCollection(\pbTableValue::class, [
'collection_id' => 1 // id коллекции
]);
foreach ($rows as $row) {
$row->set('collection_id', 0);
$row->set('field_id', 70); // заменяем на нужный id поля
$row->set('field_name', 'products'); // заменяем на имя поля
$row->save();
}
Builders
В этом разделе описаны новые возможности в режиме разработки.
Поддержка UTM-меток
В файле App/Models/Resource.php добавлена функция getUTM:public static function getUTM(): array
{
return [
UTM::make('test')->label('Test')->params('utm_param=test&utm_email=1'),
UTM::make('test2')->label('Test2')->params('utm_param=test&utm_email=2'),
];
}
В админке:

Здесь мы переключаемся на UTM-метку и копируем блок без метки. Если нужно изменить только заголовок, а остальные данные оставить без изменений, мы синхронизируем блок, но убираем синхронизацию заголовка. Теперь, если открыть страницу с метками utm_param=test&utm_email=1, отобразится именно изменённый блок. Но можно добавлять совершенно разные блоки, так что без UTM-меток будет отображаться один контент, а с метками — другой.
Конструктор панелей
В файле App/Models/Resource.php добавлена функция getPanels, которая добавляет панели в ресурсе.Например, добавляем панель с комментариями:
public static function getPanels(): array
{
return [
Panel::make('Comments')
->position(0) // Пусть это будет самая первая панель
->collapsible() // Панель можно свернуть
->fields([
Field::make('Comments')
->type('table')
->fields([
Field::make('Author')->required(),
Field::make('Comment')->type('textarea')->required(),
])
->columns([
Column::make('Author'),
Column::make('Comment'),
]),
])
];
}
В админке

Добавлена поддержка разрешений доступа к блокам, панелям, вкладкам и полям.
Пример:Block::make('Slider')
->permissions([
'resource' => '1,2,3', // Доступно для ресурсов с ID 1, 2 или 3
'template' => '1,2', // Доступно для шаблонов с ID 1 или 2
'parent' => '2,3' // Доступно для ресурсов, у которых родитель с ID 2 или 3
]);
Группировка столбцов в таблице и renderIf
Теперь можно объединять столбцы в группы, а также использовать renderIf — механизм, позволяющий отображать одну из доступных колонок. Например, если заголовок отсутствует, можно вывести изображение вместо него.Исходный вариант (без группировки и renderIf)
->columns([
Column::make('Background')
->render('color'),
Column::make('Color')
->width(100)
->render('color'),
Column::make('Title')
])

Группируем столбцы
Добавляем общий заголовок Colors и подписываем вложенные колонки:
->columns([
Column::make('Colors')
->group([
Column::make('Background')
->render('color')
->withLabel(), // Автоматический заголовок
Column::make('Color')
->render('color')
->withLabel('Color text'), // Кастомный заголовок
]),
Column::make('Title')
->label('Title / Image')
// Добавляем условие для колонки
->renderIf([
Column::make('Title'),
Column::make('Image')->render('image'),
])
->default('---') // Если нечего отображать, используем значение по умолчанию
])
Как работает renderIf?
- Проверяет поочерёдно указанные столбцы.
- Если Title существует — отображает его.
- Если Title пустой, но есть Image — отображает изображение.
- Если оба пустые — показывает default('---').
Результат:

Роутинг
Необязательные параметры
Пример:
Route::get('/{alias?}', [ResourceController::class, 'index']);
Такой маршрут будет принимать два варианта URL:- / — главная страница
- /some-alias — страница с указанным алиасом
Пример:
public $classKey = \modResource::class;
public function index(string $alias = '', array $request = [])
{
$where = [
'published' => 1,
'deleted' => 0,
];
if (empty($alias)) {
$where['id'] = $this->modx->getOption('site_start', null, 1, 1);
} else {
$where['alias'] = $alias;
}
if (!$resource = $this->modx->getObject($this->classKey, $where)) {
abort(404, 'Resource not found.');
}
$this->modx->resource = $resource;
return response()->view('templates/base');
}
Ограничения регулярных выражений
Если мы определим маршрут так:
Route::get('/{context}/{alias?}', [ResourceController::class, 'context']);
то он примет любой первый сегмент, включая /contacts, что может быть нежелательным.Но нам нужно, чтобы context содержал только язык из двух букв, например en или ru.
Решение:
Route::get('/{context}/{alias?}', [ResourceController::class, 'context'])
->where('context', '[a-z]{2}');
Теперь маршрут принимает только такие URL:- /en/
- /en/some-alias
Мультиязычность
В PageBlocks мультиязычность работает без контекстов. Чтобы активировать её, нужно выполнить следующие шаги:
- Включить системную настройку pageblocks_context_aware.
- В настройке pageblocks_contexts указать нужные языки.
Значения языков нужно задать вручную в формате JSON:
[{"key":"web","value":"Russian"}, {"key":"en","value":"English"}]
В MODX 3 при редактировании настройки pageblocks_contexts будет отображаться удобная таблица для добавления и редактирования языков.В результате появится выпадающий список языков для блоков и таблиц:

Перевод стандартных полей ресурса и полей PageBlocks.
Кроме выпадающего списка языков, к ресурсу добавляется вкладка с названием Перевод.
Поля формируются из системной настройки pageblocks_translate_resource_fields, например: pagetitle, longtitle, description, introtext, content, menutitle, seo_title. Здесь seo_title — это не стандартное поле ресурса, а добавленное через PageBlocks. Все переведённые поля автоматически применяются к ресурсу.
Выводим языки на сайт с помощью сниппета pbLang:
<ul class="navbar-nav ms-auto d-flex align-items-center list-unstyled mb-0">
{'!pbLang' | snippet: [
'tpl' => '@INLINE <li class="nav-item"><a class="nav-link {$activeCls}" lang="{$key}" href="{$url}">{$value}</a></li>'
'showCurrent' => 1
]}
</ul>
Более подробно о мультиязычности будет отдельная статья. Однако стоит учитывать, что все сниппеты PageBlocks учитывают контекст и выводят данные на текущем языке.
Поблагодарить автора
Отправить деньги
Комментарии: 3
Еще не много осталось что бы заменить большое количество компонентов которые не поддерживаются и не развиваются.
Не хватает примеров реализации для полного понимания всего функционала. Спасибо.
Не хватает примеров реализации для полного понимания всего функционала. Спасибо.
Примеры скоро будут.
renderif — только вчера думал, что было бы здорово как то это реализовать, а оно само появляется в обновлении. Класс!
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.