Новые возможности 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 – чанк для каждой итерации, если значение поля массив
Например, выводим seo-карточки из 3 ресурсов и присоединяем таблицу seo_list
{'!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 — страница с указанным алиасом
В контроллере можно определить логику: если $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
А some-alias без context будет обрабатываться другим маршрутом или вернёт 404, если такой маршрут не найден.

Мультиязычность


В PageBlocks мультиязычность работает без контекстов. Чтобы активировать её, нужно выполнить следующие шаги:

  1. Включить системную настройку pageblocks_context_aware.
  2. В настройке pageblocks_contexts указать нужные языки.
Пример настройки (для MODX 2):
Значения языков нужно задать вручную в формате 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 учитывают контекст и выводят данные на текущем языке.
Aleksandr Huz
26 марта 2025, 10:22
modx.pro
547
+15
Поблагодарить автора Отправить деньги

Комментарии: 3

Maks
26 марта 2025, 10:49
+1
Еще не много осталось что бы заменить большое количество компонентов которые не поддерживаются и не развиваются.
Не хватает примеров реализации для полного понимания всего функционала. Спасибо.
    Aleksandr Huz
    26 марта 2025, 10:51
    +1
    Примеры скоро будут.
    Miša Bulic
    26 марта 2025, 20:08
    +1
    renderif — только вчера думал, что было бы здорово как то это реализовать, а оно само появляется в обновлении. Класс!
      Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
      3