Краткая памятка по созданию гридов в админке Magento 2. В качестве примера я взял простой грид из трех колонок, данные для которого (коды стран по ISO 3166) поставляются из прописанного в коде массива. Для того, чтобы сфокусироваться на основных аспектах построения грида я отбросил из дескриптора UI-компонента максимум возможного (дополнительные кнопки, фильтры, сортировка, bookmarks, ...) и часть настроек перенес в конструктор провайдера данных для грида. Если можно сделать еще короче без потери читабельности — с максимальным удовлетворением внесу соответствующие правки (UPD: спасибо коллеге Oxidant за контроллер). Код примера на github'е.
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 Flancer32\Sample\Controller\Adminhtml\Grid;
class Index
extends \Magento\Backend\App\Action
{
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 \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::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">Flancer32\Sample\Ui\Component\DataProvider\Grid</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'а спрятаны в конструкторе класса `Flancer32\Sample\Ui\Component\DataProvider\Grid`;
- имена столбцов грида совпадают с именами полей в данных;
- без указания настроек сортировки (`sorting`) хотя бы для одного столбца грид не загружается;
DataProvider
За поставку данных отвечает класс \Flancer32\Sample\Ui\Component\DataProvider\Grid
. Конструктор принимает из дескриптора компонента только один параметр (name
), все остальные либо инжектятся Object Manager'ом при создании провайдера данных, либо создаются в нем же. Данные не зависят от фильтров/сортировки и всегда возвращаются одни и те же (захардкожены в самом провайдере).
namespace Flancer32\Sample\Ui\Component\DataProvider;
class Grid
extends \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
{
public function __construct(
$name,
\Magento\Framework\Api\Search\ReportingInterface $reporting,
\Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder,
\Magento\Framework\App\RequestInterface $request,
\Magento\Framework\Api\FilterBuilder $filterBuilder,
\Magento\Framework\UrlInterface $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 — это увлекательное занятие, которому можно посвятить не только свободные часы, но дни, а может быть даже и недели. Конечно, со временем оно станет менее увлекательным и более обыденным, но пока все еще остается возможность добавить в админку свой собственный грид не в пару кликов, а путем вдумчивого и кропотливого изменения если и не десятка файлов, то около того (если бы я трусливо не захаркодил данные в провайдере — точно достиг бы этого уровня, а может быть даже и превысил). Возможно кто-то окажется более смелым и захочет использовать встроенный \Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
и зарегистрировать для него в ./src/etc/di.xml
соответствующую коллекцию:
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="sample_grid_data_source" xsi:type="string">Vendor\Module\Model\ResourceModel\Grid\Collection</item>
</argument>
</arguments>
</type>
Пожелаю ему в этом удачи. Я, к сожалению, на данный момент свой лимит увлекательности исчерпал.
Всем счастливого Magento 2 coding'а!
Комментарии (7)
PerlPower
19.09.2016 21:54+1После такого рода публикаций складывается впечатление, что создание интернет-магазинов на магенто по трудоазтратам аналогично их написанию на каком-нибудь фреймворке. Так в чем же его профит?
vpodorozh
20.09.2016 05:03Да. Смахивает на то :)
Но все же как не крути получается быстрее — не на много но быстрее. Ко второй магенте стоит относится как к навороченому фреймворку на базе которого собран конечный продукт из кучи модулей.
Так же можно было б сказать и про первую (или любую другую похожую модульную систему) — но все же в м2 это поярче выражено.
И да, профит в даном случае огромный есть только тогда когда систему заведомо будут поддерживать очень долгое время и большое количество разных людей.
В других случаях, затраты на разработку с лихвой сжирают возможный профит.
М2 для девелопера с чуством перфикционизма и желанием сделать все как по книжке будет очень уютным местечком. Но проблема в том что eCommerce в большенстве случаев не нужно качество, ей нужна быстрота внедрения всего нового что может принести выгоду. К сожалению это не про вторую магенту — как не крути но времени нужно потратить больше на разработку, по сравнению с первой магентой, почти во всем.
Хотя, она все же чертовски интересная:)flancer
20.09.2016 06:24К сожалению перфекционистам в M2 опасно — она еще очень сырая, неоднородная по реализации система с незавершенными архитектурными решениями (те же репозитории, все еще полностью не заменившие ресурс-модели). Любой владелец магазина на М2 —
доброневольный бета-тестер. Работа над выправлением этой ситуации идет, но сможет ли команда разработчиков ее выправить — вопрос открытый. Я бы поставил на них, если бы они отказались от обратной совместимости (которой и так нет) и провели генеральную чистку и реструктуризацию кода и данных, но это стало бы уже М3. Таких маневров community может и не выдержать, а без своих расширений Magento превратится в тот же Shopify, только еще и на своем "железе". Ну а пока разработчики борются со сложностью и, судя по тому, что тесты на Travis'е у них не проходят уже около месяца, борьба идет ожесточенная.
Хотя, она все же чертовски интересная:)
А вот тут полностью согласен :) Весьма любопытный пример распределенной разработки приложений.
flancer
20.09.2016 05:31Богатая функциональность, получаемая "из-коробки" — это раз. Большое кол-во расширений основного функционала, созданных сторонними разработчиками — это два. Все еще значительное community (best practice & support, как отметил коллега Sergiy) — это три. И плюс куча времени и средств закопанного в нее как разработчиками (основного функционала и расширений), так и владельцами магазинов.
И да, профит в даном случае огромный есть только тогда когда систему заведомо будут поддерживать очень долгое время и большое количество разных людей.
Oxidant
Статья понравилась. Но я заметил что все, включая М2 разработчиков, добавляют в зависимости своего класса редирект или резалт фектори вроде
Все контроллеры наследуют свойство $this->resultFactory в котором хранится Magento\Framework\Controller\ResultFactory, т.е. достаточно сделать
и тем самым избавиться от необходимости в некоторых случаях пустого конструктора ради 1 лишней зависимости.
flancer
Спасибо, коллега. Внес изменения в код примера.