Наследование блоков в PageBuilder

Потребовалось тут на днях реализовать наследуемые блоки в PageBuilder`е.
На сайте несколько уровней вложенных документов, и на внутренних часто повторяется один и тот же блок (или блоки). Наполнять одинаковые блоки одним и тем же контентом во-первых, долго, во-вторых, не рационально. Значит нужно сделать блоки наследуемыми от родительского ресурса. Желательно сделать это просто, без заморочек, и при необходимости иметь возможность без усилий расширить эту функциональность на уже имеющиеся блоки.

Ok. Определяем внешний интерфейс максимально просто:


return PageBuilderInheritable::make(
    [
        'title' => 'Наши преимущества',
        'image' => 'assets/plugins/pagebuilder/images/thumbup.png',

        'container' => [
            'pbService',
        ],

        'fields' => [
            'title' => [
                'caption' => 'Заголовок блока',
                'type'    => 'text',
            ],
            'items' => [
                'caption' => 'Элементы',
                'type'    => 'group',
                'fields'  => [
                    'text' => [
                        'caption' => 'Текст',
                        'type'    => 'textarea',
                        'height'  => '80px',
                    ],
                    'icon' => [
                        'caption' => 'Иконка',
                        'type'    => 'image',
                    ],
                ],
            ],
        ],
    ],
    'title',
    str_replace('.php', '', basename(__FILE__))
);


В первом параметре передаем массив настроек блока.

Во втором параметре указываем поле, из которого будем позже брать описание блока, чтобы отличать его от других однотипных. Здесь имеется ввиду именно поле из массива «fields», а не опция «title». Однако, если поля не существует или оно пусто, будет использована опция «title», при этом поля в списке доступных для наследования не будут отличимы, что не очень удобно. Позже покажу, что я имею ввиду.

В третьем параметре передается идентификатор блока. Зачем он нужен в evo2.x и как используется, Дмитрий рассказывал в этом видео https://www.youtube.com/watch?v=YM_qY9xRlU4

Итак, интерфейс определен, выглядит довольно просто для использования. Для применения к существующим блокам, достаточно обернуть их конфиг в вызов PageBuilderInheritable::make()

Теперь сам метод make:
доступен в гисте на гитхабе https://gist.github.com/delphinpro/14dc6a3d79fe06edba3b3cd437fce3bf

Функция получает идентификаторы всех родительских ресурсов, дергает из таблицы pagebuilder блоки этих ресурсов, дополнительно получает заголовки страниц и добавляет два поля в блок «inherit» (флаг наследования) и «inheritId» (идентификатор наследуемого блока).

В админке выглядит как чекбокс и выпадающий список:



В этом выпадающем списке как и используется текст из поля «title» (или другого указанного) для различия между блоками. Плюс добавляется заголовок страницы, с которой будет наследоваться блок.

Для вывода в шаблоне использован следующий вызов

@include('partials.pb.container', ['data' => $pageBuilder])

Сам партиал контейнер

@foreach($data as $block)
  @includeIf('partials.pb.blocks.'.$block['block_id'], ['data' => $block])
@endforeach

(@includeIf предотвращает ошибку, если шаблон блока не будет найден)

В базовый контроллер добавим пару методов.

Доступный в наследуемых контроллерах метод, выполяет сниппет PageBuilder и рекурсивно ищет родительские блоки при необходимости

protected function getPageBuilderBlocks($container = 'default')
    {
        if (is_array($container)) {
            $container = join(',', $container);
        }

        if (!is_string($container)) {
            throw new \Exception('Argument 1 passed to '.__METHOD__.' must be of the type array or string, '.gettype($container).' given');
        }

        $blocks = $this->evo->runSnippet('PageBuilder', [
            'container' => $container,
            'renderTo'  => 'array',
        ])[0];

        foreach ($blocks as &$block) {
            $blockInherit = !empty($block['inherit']);
            $blockInheritId = !empty($block['inheritId']) ? (int)$block['inheritId'] : null;
            if ($blockInherit && $blockInheritId > 0) {
                $val = $this->pageBuilderBlockInherit($blockInheritId);
                $block = array_merge($block, $val);
            }

            unset ($block['inherit'], $block['inheritId']);
        }

        return $blocks;
    }


Приватный метод — рекурсивный, для поиска наследуемых блоков

private function pageBuilderBlockInherit(int $blockId)
    {
        $block = (array)DB::table('pagebuilder')->find($blockId);

        if ($block) {
            $fields = json_decode($block['values'], true);
            $blockInherit = !empty($fields['inherit']);
            $blockInheritId = !empty($fields['inheritId']) ? (int)$fields['inheritId'] : null;

            if ($blockInherit && $blockInheritId > 0) {
                $fields = $this->pageBuilderBlockInherit($blockInheritId);
            }

            return $fields;
        }

        return [];
    }


Теперь в любом контроллере страницы, на которой используется PageBuilder получаем данные следующим образом:

$this->data['pageBuilder'] = $this->getPageBuilderBlocks('pbService');

'pageBuilder' — переменная шаблона, к торой будут находится блоки.
'pbService' — имя TV-шки.


1 комментарий

avatar
Как вовремя, вы запилили такое решение, очень понадобилось. Интересный подход и красивое решение.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.