Magento 2: Создание грида в adminhtml

в 16:24, , рубрики: Grid, magento 2, Разработка под e-commerce

Краткая памятка по созданию гридов в админке Magento 2. В качестве примера я взял простой грид из трех колонок, данные для которого (коды стран по ISO 3166) поставляются из прописанного в коде массива. Для того, чтобы сфокусироваться на основных аспектах построения грида я отбросил из дескриптора UI-компонента максимум возможного (дополнительные кнопки, фильтры, сортировка, bookmarks, ...) и часть настроек перенес в конструктор провайдера данных для грида. Если можно сделать еще короче без потери читабельности — с максимальным удовлетворением внесу соответствующие правки (UPD: спасибо коллеге Oxidant за контроллер). Код примера на github'е.

Magento 2: Создание грида в adminhtml - 1

ACL

Создаем запись в ACL (./etc/acl.xml) для контроля доступа к гриду:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Flancer32_Sample::sample" title="Samples" sortOrder="10">
                    <resource id="Flancer32_Sample::sample_grid" title="Grid" sortOrder="100"/>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

Menu

Добавляем в меню админки (./etc/adminhtml/menu.xml) дополнительные пункты:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd">
    <menu>
        <add id="Flancer32_Sample::sample"
                title="Sample" translate="title" module="Flancer32_Sample"
                sortOrder="15"
                resource="Flancer32_Sample::sample"/>
        <add id="Flancer32_Sample::sample_grid"
                title="Grid" translate="title" module="Flancer32_Sample"
                sortOrder="100" parent="Flancer32_Sample::sample"
                action="sample/grid"
                resource="Flancer32_Sample::sample_grid"/>
    </menu>
</config>

Адрес перенаправления определяется в action="...", доступ к пунктам меню — в resource="...".

Routes

В файле ./etc/adminhtml/routes.xml регистрируем маршурт fl32_sample_route (внутренний идентификатор) с именем sample (видимый идентификатор, часть URL'а):

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
        <route id="fl32_sample_route" frontName="sample">
            <module name="Flancer32_Sample"/>
        </route>
    </router>
</config>

Controller

Обработчики запросов по адресу .../index.php/admin/sample/grid/* размещаем в каталоге ./src/Controller/Adminhtml/Grid/. Обработчик по-умолчанию: Index.php:

namespace Flancer32SampleControllerAdminhtmlGrid;

class Index
    extends MagentoBackendAppAction
{
    const ACL_RESOURCE = 'Flancer32_Sample::sample_grid';
    const MENU_ITEM = 'Flancer32_Sample::sample_grid';
    const TITLE = 'Sample Grid';

    protected function _isAllowed()
    {
        $result = $this->_authorization->isAllowed(self::ACL_RESOURCE);
        return $result;
    }

    public function execute()
    {
        /** @var MagentoBackendModelViewResultPage $resultPage */
        $resultPage = $this->resultFactory->create(MagentoFrameworkControllerResultFactory::TYPE_PAGE);
        $resultPage->setActiveMenu(self::MENU_ITEM);
        $resultPage->getConfig()->getTitle()->prepend(__(self::TITLE));
        return $resultPage;
    }
}

Все, что делает обработчик — проверяет права пользователя на доступ к гриду и формирует страницу в соответствии с заданным для данного маршрута laout'ом.

Layout

Описание layout'а находится в каталоге ./src/view/adminhtml/layout/ в файле fl32_sample_route_grid_index, название которого состоит из трех частей:

  • fl32_sample_route: внтуренний идентификатор маршрута (см. route.id в ./etc/adminhtml/routes.xml);
  • grid: вторая часть адреса перенаправления (см. action в ./etc/adminhtml/menu.xml);
  • index: имя обработчика по-умолчанию для запросов по адресу .../index.php/admin/sample/grid/ (см. ./src/Controller/Adminhtml/Grid/Index.php);

<?xml version="1.0"?>
<page
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="content">
            <uiComponent name="sample_grid"/>
        </referenceContainer>
    </body>
</page>

В описании задано, что в качестве контента на странице нужно вывести UI-компонент с именем sample_grid.

UI Component

Дескриптор компонента находится в файле ./src/view/adminhtml/ui_component/sample_grid.xml

<?xml version="1.0" encoding="UTF-8"?>
<listing
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="provider" xsi:type="string">sample_grid.sample_grid_data_source</item>
            <item name="deps" xsi:type="string">sample_grid.sample_grid_data_source</item>
        </item>
        <item name="spinner" xsi:type="string">sample_grid_columns</item>
    </argument>
    <dataSource name="sample_grid_data_source">
        <argument name="dataProvider" xsi:type="configurableObject">
            <argument name="class" xsi:type="string">Flancer32SampleUiComponentDataProviderGrid</argument>
            <argument name="name" xsi:type="string">sample_grid_data_source</argument>
        </argument>
    </dataSource>
    <columns name="sample_grid_columns">
        <column name="code2">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="sorting" xsi:type="string">asc</item>
                    <item name="label" xsi:type="string" translate="true">Alpha-2</item>
                </item>
            </argument>
        </column>
        <column name="code3">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Alpha-3</item>
                </item>
            </argument>
        </column>
        <column name="code_num">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="label" xsi:type="string" translate="true">Numeric</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

Я постарался сделать код компонента как можно меньше (сравните с аналогичным для CMS Pages).

На что следует обратить внимание:

  • имя компонента `sample_grid` совпадает с именем файла и используется в описании `js_config` (provider & deps);
  • в настройках spinner'а указывается имя `columns`-компонента, после заполнения данными которого spinner скрывается;
  • настройки data source'а спрятаны в конструкторе класса `Flancer32SampleUiComponentDataProviderGrid`;
  • имена столбцов грида совпадают с именами полей в данных;
  • без указания настроек сортировки (`sorting`) хотя бы для одного столбца грид не загружается;

DataProvider

За поставку данных отвечает класс Flancer32SampleUiComponentDataProviderGrid. Конструктор принимает из дескриптора компонента только один параметр (name), все остальные либо инжектятся Object Manager'ом при создании провайдера данных, либо создаются в нем же. Данные не зависят от фильтров/сортировки и всегда возвращаются одни и те же (захардкожены в самом провайдере).

namespace Flancer32SampleUiComponentDataProvider;

class Grid
    extends MagentoFrameworkViewElementUiComponentDataProviderDataProvider
{
    public function __construct(
        $name,
        MagentoFrameworkApiSearchReportingInterface $reporting,
        MagentoFrameworkApiSearchSearchCriteriaBuilder $searchCriteriaBuilder,
        MagentoFrameworkAppRequestInterface $request,
        MagentoFrameworkApiFilterBuilder $filterBuilder,
        MagentoFrameworkUrlInterface $url
    ) {
        $primaryFieldName = 'id';
        $requestFieldName = 'id';
        $meta = [];
        $updateUrl = $url->getRouteUrl('mui/index/render');
        $data = [
            'config' => [
                'component' => 'Magento_Ui/js/grid/provider',
                'update_url' => $updateUrl
            ]
        ];
        parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request,
            $filterBuilder, $meta, $data);
    }

    public function getData()
    {
        $result = [
            'items' => [
                ['code2' => 'AU', 'code3' => 'AUS', 'code_num' => '036'],
                ['code2' => 'AT', 'code3' => 'AUT', 'code_num' => '040'],
                ['code2' => 'AZ', 'code3' => 'AZE', 'code_num' => '031']
            ],
            'totalRecords' => 3
        ];
        return $result;
    }

}

Резюме

Создание гридов в Magento 2 — это увлекательное занятие, которому можно посвятить не только свободные часы, но дни, а может быть даже и недели. Конечно, со временем оно станет менее увлекательным и более обыденным, но пока все еще остается возможность добавить в админку свой собственный грид не в пару кликов, а путем вдумчивого и кропотливого изменения если и не десятка файлов, то около того (если бы я трусливо не захаркодил данные в провайдере — точно достиг бы этого уровня, а может быть даже и превысил). Возможно кто-то окажется более смелым и захочет использовать встроенный MagentoFrameworkViewElementUiComponentDataProviderDataProvider и зарегистрировать для него в ./src/etc/di.xml соответствующую коллекцию:

<type name="MagentoFrameworkViewElementUiComponentDataProviderCollectionFactory">
    <arguments>
        <argument name="collections" xsi:type="array">
            <item name="sample_grid_data_source" xsi:type="string">VendorModuleModelResourceModelGridCollection</item>
        </argument>
    </arguments>
</type>

Пожелаю ему в этом удачи. Я, к сожалению, на данный момент свой лимит увлекательности исчерпал.

Всем счастливого Magento 2 coding'а!

Автор: flancer

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js