Перейти к основному содержимому

Создание собственных Brick

Архитектура Brick

Brick – это экземпляр Pimcore\Extension\Document\Areabrick\AreabrickInterface, который может быть автоматически загружен с помощью сохранения Brick в специальном пространстве имен внутри вашего бандла, или определяя Brick как сервис и добавляя его в список доступных Brick через тег внедрения зависимостей (DI). Класс Brick – это единственный обязательный файл для Brick, однако большинство Brick будет по крайней мере реализовывать шаблон представления, который рендерится в пользовательском интерфейсе и в режиме редактирования.

Сами по себе шаблоны – это нормальные шаблоны, которые передаются движку рендеринга. Поэтому вы можете использовать все существующие расширения шаблонов и редактируемые элементы Pimcore.

Регистрация Brick

Brick будет зарегистрирован в системе и представлен с помощью идентификатора Brick, который должен быть уникальным в пределах системы. Если идентификатор Brick зарегистрирован дважды (например, несколькими бандлами), возникнет ошибка. Самый простой способ зарегистрировать Brick – просто сохранить его в специальном пространстве имен Document\Areabrick внутри вашего бандла. Каждый бандл будет сканироваться на наличие классов, реализующих AreabrickInterface, и все найденные Brick будут автоматически зарегистрированы в системе. Идентификатор Brick будет сформирован из имени класса реализующего класса путем преобразования имени класса в формат с дефисами. Например, Brick с именем MyCustomAreaBrick будет автоматически зарегистрирован как my-custom-area-brick.

Базовая реализация Brick может выглядеть следующим образом. Поскольку он определен в специальном пространстве имен, Pimcore автоматически создаст сервис app.area.brick.iframe и зарегистрирует его в менеджере Brick с идентификатором iframe.

<?php  

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\AreabrickInterface;

class Iframe implements AreabrickInterface
{
// реализации методов класса
}

Если вам нужно больше контроля над экземпляром Brick (например, если ваш Brick зависит от других сервисов или вы хотите вручную указать идентификатор Brick), вы можете самостоятельно добавить определение сервиса и пометить сервис тегом DI pimcore.area.brick. Brick, определенные вручную, будут исключены из автоматической регистрации, даже если они определены в специальном пространстве имен. Давайте определим наш блок, как указано выше, но предположим, что ему нужен доступ к экземпляру логгера:

# a service.yaml file defining services  
services:
App\Document\Areabrick\Iframe:
arguments: ['@logger']
tags:
- { name: pimcore.area.brick, id: iframe }

Это зарегистрирует Brick, как указано выше, но у вас есть контроль над идентификатором Brick и вы можете использовать контейнер для зависимостей.

Существует также атрибут AsAreabrick, позволяющий контролировать его идентификатор при использовании автоматической загрузки или автоматически помечать его при самостоятельном определении сервиса.

<?php  

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\AreabrickInterface;
use Pimcore\Extension\Document\Areabrick\Attribute\AsAreabrick;
use Psr\Log\LoggerInterface;

#[AsAreabrick(id: 'iframe')]
class Iframe implements AreabrickInterface
{
// implementing class methods
}

Хотя может возникнуть соблазн перезаписать метод getId() в ваших Brick, пожалуйста, убедитесь, что Brick всегда ссылается на идентификатор, который задается с помощью setId($id) при регистрации Brick. Переопределение getId() не повлияет на идентификатор Brick, как он был зарегистрирован в системе при загрузке. Areabrick manager установит зарегистрированный идентификатор при получении экземпляра Brick.

Автоматическое обнаружение шаблона Brick

Для удобства вы можете создать новый Brick, расширив Pimcore\Extension\Document\Areabrick\AbstractTemplateAreabrick, чтобы использовать автоматическое обнаружение шаблона (таким образом, для начала потребуется минимум кода). Шаблон Brick реализует TemplateAreabrickInterface, который определяет следующие методы, которые вы можете использовать для управления автоматическим обнаружением шаблонов. Пожалуйста, убедитесь, что ваш Brick определен внутри бандла, иначе ваши шаблоны не смогут быть автоматически обнаружены.

Расположение шаблона определяет базовый путь, который будет использоваться для поиска ваших шаблонов. Он приводит к следующим расположениям. <bundlePath> - это путь к файловой системе бандла, в котором находится блок, <brickId>

  • идентификатор блока, зарегистрированный в areabrick manager (смотрите ниже).
РасположениеПуть
глобальноеtemplates/areas/<brickId>/
бандл<bundlePath>/Resources/views/areas/<brickId>/ для устаревшей структуры бандла (Symfony <= 4) или <bundlePath>/templates/areas/<brickId>/ для современной (Symfony >= 5) структуры бандла

В зависимости от расположения шаблона будут использоваться следующие файлы. Вы всегда можете полностью контролировать расположение, самостоятельно реализовав методы для шаблонов и иконки (см. AreabrickInterface):

ТипРасположение
шаблон представления<templateLocation>/view.html.twig

Если Brick определяет иконку в каталоге public ресурсов пакета, иконка будет автоматически использоваться в режиме редактирования. Если иконка находится в другом месте, вы можете переопределить метод getIcon() и указать URL-адрес, который будет включен в качестве иконки. В режиме редактирования иконка Brick будет найдена в следующем местоположении, и ожидается, что она будет в формате PNG размером 16x16 пикселей: <bundlePath>/Resources/public/areas/<brickId>/icon.png, который преобразуется в URL-адрес /bundles/<bundleUrl>/areas/<brickId>/icon.png при подключении в режиме редактирования.

Вы можете дополнительно реализовать Pimcore\Extension\Document\Areabrick\PreviewAwareInterface, чтобы добавить пользовательскую html-подсказку для вашего Brick, которая будет отображаться в виде всплывающей подсказки при наведении курсора мыши на кнопку добавления Brick.
Учитывая, что наш блок iframe был определен ранее, будут использоваться следующие пути.

Расположение шаблона global

РасположениеПуть
шаблон представленияtemplates/areas/iframe/view.html.twig
путь иконкиpublic/bundles/app/areas/iframe/icon.png
URL иконки/bundles/app/areas/iframe/icon.png

Расположение шаблона bundle

Путь и URL-адрес иконки такие же, как указано выше, но скрипты представления ожидаются внутри бандла.

РасположениеПуть
шаблон представленияtemplates/areas/iframe/view.html.twig

Как создать Brick

Давайте предположим, что наш Brick iframe, определенный выше, отвечает за создание <iframe>, с содержимым с указанного URL-адреса в режиме редактирования. Прежде всего, давайте обновим класс, чтобы добавить метаданные для менеджера расширений, использовать автоматическое обнаружение шаблонов и загружать шаблон представления из templates вместо директории бандла:

<?php  
// src/Document/Areabrick/Iframe.php

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\AbstractTemplateAreabrick;

class Iframe extends AbstractTemplateAreabrick
{
public function getName(): string
{
return 'IFrame';
}

public function getDescription(): string
{
return 'Embed contents from other URL (websites) via iframe';
}

public function needsReload(): bool
{
// необязательно
// здесь вы можете решить, следует ли при добавлении этих Brick запускать перезагрузку
// в интерфейсе редактирования, это может быть необходимо в некоторых случаях. по умолчанию = false
return false;
}
}

Давайте создадим представление в качестве следующего шага. Представления ведут себя точно так же, как представления встроенного контроллера, и у вас есть доступ к текущему документу, режиму редактирования, а также к редактируемым объектам и помощникам по созданию шаблонов, как и везде. Кроме того, в представлении есть переменная instance, которая предоставляет вам доступ к экземпляру Brick. Переменная info (см. ниже) предоставляет вам доступ к метаданным Brick.

/* templates/areas/iframe/view.html.twig */  

{% set urlField = pimcore_input("iframe_url") %}
{% set widthField = pimcore_numeric("iframe_width") %}
{% set heightField = pimcore_numeric("iframe_height") %}
{% set transparentField = pimcore_checkbox("iframe_transparent") %}

{% if editmode %}
<div>
<h2>IFrame</h2>
<div>
URL: {{ urlField|raw }}
</div>
<br/>
<b>Advanced Configuration</b>
<div>
Width: {{ widthField|raw }}px (default: 100%)
</div>
<div>
Height: {{ heightField|raw }}px (default: 400px)
</div>
<div>
Transparent: {{ transparentField|raw }} (default: false)
</div>
</div>
{% else %}
{% if not urlField.isEmpty() %}

{% set transparent = "false" %}
{% set width = "100%" %}
{% set height = "400" %}

{% if not widthField.isEmpty() %}
{% set width = widthField.data %}
{% endif %}

{% if not heightField.isEmpty() %}
{% set height = heightField.data %}
{% endif %}

{% if transparentField.isChecked() %}
{% set transparent = "true" %}
{% endif %}

<iframe src="{{ urlField }}" width="{{ width }}" height="{{ height }}" allowtransparency="{{ transparent }}" frameborder="0"></iframe>

{% endif %}
{% endif %}

Теперь вы сможете увидеть свой Brick в списке доступных Brick вашего areablock: Areablock bricks list with the iframe brick

В режиме редактирования вы можете просмотреть конфигурацию Brick Iframe: Iframe brick - configuration in the editmode

Информационный объект Brick

Представления и методы Brick будут иметь доступ к объекту Info, содержащему метаданные о текущем Brick. Он отображается как переменная info в представлениях и передается методам Brick в качестве аргумента. Многие методы существуют по историческим причинам, но пара методов может быть полезна при реализации ваших собственных Brick.

МетодОписание
$info->getEditable()Возвращает редактируемый фрагмент, рендерящий Brick
$info->getDocument()Извлекает документ
$info->getDocumentElement($name)Извлекает редактируемый тег из документа
$info->getRequest()Возвращает текущий запрос
$info->getIndex()Возвращает текущий индекс внутри areablock
$info->getParam($name)Извлекает параметр, переданный параметром конфигурации globalParams или params
$info->getParams()Извлекает все параметры, переданные параметром конфигурации globalParams или params

Диалог редактирования элемента (с версии 6.8)

Иногда необходимо собрать дополнительные данные или указать некоторые параметры конфигурации для Brick, которые по умолчанию не должны быть видны. Для таких случаев подходящим инструментом является диалоговое окно редактирования.

Диалоговое окно - это, по сути, настраиваемый интерфейс редактирования, в котором вы можете использовать все поддерживаемые редактируемые элементы в структурированном виде в контексте текущего Brick. Интерфейс редактирования настраивается путем реализации EditableDialogBoxInterface в классе вашего Brick и предоставления простого массива настроек.

Этот конфигурационный массив может содержать либо сами редактируемые элементы (Editables), либо массив формата (не рекомендуется).:

[  
'type' => 'input', // Тип редактируемого элемента
'name' => 'myInput', // Название редактируемого элемента
'config' => [], // Необязательный массив документированных настроек (смотрите соответствующие редактируемые элементы)
'label' => 'My Input Label', // Необязательная метка
'description' => 'Additional Description', // Необязательное описание
]

Простой пример конфигурации

<?php  

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\EditableDialogBoxConfiguration;
use Pimcore\Extension\Document\Areabrick\EditableDialogBoxInterface;
use Pimcore\Model\Document;
use Pimcore\Model\Document\Editable;
use Pimcore\Model\Document\Editable\Area\Info;

class WysiwygWithImages extends AbstractAreabrick implements EditableDialogBoxInterface
{
public function getName(): string
{
return 'WYSIWYG w. Images';
}

public function getEditableDialogBoxConfiguration(Document\Editable $area, ?Info $info): EditableDialogBoxConfiguration
{
$config = new EditableDialogBoxConfiguration();
$config->setWidth(600);
//$config->setReloadOnClose(true);

$config->setItems([
[
(new Editable\Input())
->setName('myDialogInput')
->setLabel('Some additional Text'), // labels are optional

(new Editable\Checkbox())
->setName('myDialogCheckbox')
->setLabel('This is the checkbox label')
->setDialogDescription('This is a description for myDialogCheckbox'), // descriptions are optional

(new Editable\Date())
->setName('myDialogDate')
]
]);

return $config;
}
}

Расширенный пример настройки с использованием макетов

В вашей конфигурации также можно использовать панели вкладок.

<?php  

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\EditableDialogBoxConfiguration;
use Pimcore\Extension\Document\Areabrick\EditableDialogBoxInterface;
use Pimcore\Model\Document\Editable;
use Pimcore\Model\Document\Editable\Area\Info;

class WysiwygWithImages extends AbstractAreabrick implements EditableDialogBoxInterface
{
public function getName(): string
{
return 'WYSIWYG w. Images';
}

public function getEditableDialogBoxConfiguration(Editable $area, ?Info $info): EditableDialogBoxConfiguration
{
$config = new EditableDialogBoxConfiguration();
$config->setWidth(600);

$config->setItems([
'type' => 'tabpanel',
'items' => [
[
'type' => 'panel',
'title' => 'Tab 1',
'items' => [
(new Editable\Wysiwyg())
->setName('myDialogWysiwyg')
->setLabel('Some additional Text'),

(new Editable\Video())
->setName('myDialogVideo'),

(new Editable\Textarea())
->setName('myDialogTextarea'),

(new Editable\Table())
->setName('myDialogTable'),

(new Editable\Snippet())
->setName('myDialogSnippet'),

(new Editable\Select())
->setName('myDialogSelect')
->setConfig([
'store' => [
['foo', 'Foo'],
['bar', 'Bar'],
['baz', 'Baz'],
]
]),

(new Editable\Numeric())
->setName('myDialogNumber'),

(new Editable\Multiselect())
->setName('myDialogMultiSelect')
->setConfig([
'store' => [
['foo', 'Foo'],
['bar', 'Bar'],
['baz', 'Baz'],
]
]),

(new Editable\Checkbox())
->setName('myDialogCheckbox')
->setLabel('This is the checkbox label 😸'),

(new Editable\Input())
->setName('myDialogInput'),
]
],
[
'type' => 'panel',
'title' => 'Tab 2',
'items' => [
(new Editable\Input())
->setName('anotherInput'),

(new Editable\Link())
->setName('myDialogLink'),

(new Editable\Image())
->setName('myDialogImage'),

(new Editable\Embed())
->setName('myEmbed'),

(new Editable\Date())
->setName('myDialogDate'),
]
],
[
'type' => 'panel',
'title' => 'Tab 3',
'items' => [
(new Editable\Renderlet())
->setName('myDialogRenderlet'),

(new Editable\Relations())
->setName('myDialogRelations'),

(new Editable\Relation())
->setName('myDialogRelation')
->setLabel('Just a single relation 😹'),

(new Editable\Pdf())
->setName('myDialogPdf'),
]
]
]
]);

return $config;
}
}


Доступ к данным диалогового окна редактируемого элемента

Редактируемые элементы в диалоговом окне - это обычные редактируемые элементы, они не отличаются от тех, которые определены с помощью шаблона. Таким образом, вы также можете использовать их в шаблоне или получить к ним доступ в своем пользовательском коде.

Методы в классе Brick

Иногда Brick - это нечто большее, чем просто скрипт представления, и содержит некоторые функциональные возможности, которых не должно быть непосредственно в представлении. В этом случае вы можете использовать методы action() и postRenderAction() в классе Brick, которые принимают информационный объект в качестве аргумента. Метод action() не является реальным действием контроллера, это всего лишь небольшой помощник для извлечения некоторой логики и кода из представления. Однако вы можете использовать метод action для подготовки данных для представления (например, для анализа параметров запроса).

Вы можете вернуть объект Response из action() и postRenderAction(), и этот ответ будет отправлен обратно клиенту.

Если вам нужно повлиять на теги открытия и закрытия HTML, вы можете сделать это, настроив getHtmlTagOpen() и getHtmlTagClose() (смотрите пример ниже).

<?php  

namespace App\Document\Areabrick;

use Pimcore\Extension\Document\Areabrick\AbstractTemplateAreabrick;
use Pimcore\Model\Document\Editable\Area\Info;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;

class Iframe extends AbstractTemplateAreabrick
{
// другие методы, определенные выше
//
public function action(Info $info): ?RedirectResponse
{
$myVar = $info->getRequest()->get('myParam');

$info->setParam('myVar', $myVar);

// необязательно, вернуть объект Response
if ('POST' === $info->getRequest()->getMethod()) {
return new RedirectResponse('/foo');
}

return null;
}

// НЕОБЯЗАТЕЛЬНЫЕ МЕТОДЫ

// выполняется после рендеринга Brick
public function postRenderAction(Info $info): ?Response
{
}

// возвращает пользовательский html-враппер (возвращает пустую строку, если вам не нужен враппер).
public function getHtmlTagOpen(Info $info): string
{
return '<span class="customWrapperDiv">';
}

public function getHtmlTagClose(Info $info): string
{
return '</span>';
}
}

Примеры

Вы можете найти множество примеров в демонстрационном пакете.


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