Выход версии 2.3.0 приблизил использование PWA на фронте Magento-приложений на расстояние вытянутой руки. И если для фронта видны какие-то подвижки в применяемых технологиях, то с админкой всё гораздо стабильнее — старый добрый лабиринт из различных типов файлов, которые нужно поредактировать, чтобы на UI проявилось что-то полезное, усовершенствовать не планируется. В этой статье я описываю создание собственного рендерера для колонки грида в админке — вещи довольно несложной и, в то же самое время, довольно полезной при правильном применении. Например, рендерер для формирования в гриде заказов ссылки на карточку клиента, оформившего заказ:

Базовые компоненты рендерера
"Родной" рендерер для ссылок в Magento состоит из двух файлов:
- ./module-ui/view/base/web/js/grid/columns/link.js — код компонента;
- ./module-ui/view/base/web/templates/grid/cells/link.html — UI-шаблон компонента (knockout);
В процессе обработки шаблона используются функции, предоставляемые кодом (объект $col). Входными данными для обработки являются также текущая строка грида (объект $row):
<div class="data-grid-cell-content" if="!$col.isLink($row())" text="$col.getLabel($row())"/> <a class="action-menu-item" if="$col.isLink($row())" text="$col.getLabel($row())" attr="href: $col.getLink($row())"/>
Данные для грида загружаются через провайдер данных. Типовой запрос примерно такой: "http://.../admin/mui/index/render/?namespace=sales_order_grid...". Структуру данных можно увидеть через панель инструментов разработчика в браузере. Для грида заказов она примерно такая:
{ "items": [ { "id_field_name": "entity_id", "entity_id": "1", "status": "pending", "store_id": "Main Website<br\/> Main Website Store<br\/> Default Store View<br\/>", "store_name": "Main Website\nMain Website Store\n", "customer_id": "1", "base_grand_total": "RUB34.68", "base_total_paid": null, "grand_total": "RUB34.68", "total_paid": null, "increment_id": "000000001", "base_currency_code": "RUB", "order_currency_code": "RUB", "shipping_name": "Alex Gusev", "billing_name": "Alex Gusev", "created_at": "2018-12-22 19:35:19", "updated_at": "2018-12-22 19:35:20", "billing_address": "Street,Riga,R\u012bga,1010", "shipping_address": "Street,Riga,R\u012bga,1010", "shipping_information": "Flat Rate - Fixed", "customer_email": "alex@flancer64.com", "customer_group": "1", "subtotal": "RUB24.68", "shipping_and_handling": "RUB10.00", "customer_name": "Alex Gusev", "payment_method": "checkmo", "total_refunded": "RUB0.00", "signifyd_guarantee_status": null, "orig_data": null, "actions": { "view": { "href": "http:\/\/sample.local.flancer64.com\/admin\/sales\/order\/view\/order_id\/1\/", "label": "View" } } } ], "totalRecords": 1 }
Собственный рендерер
Таким образом, для создания собственного рендерера нам нужно задать UI-компонент, состоящий из двух файлов:
- JS-код компонента;
- Knockout-шаблон компонента;
Моей текущей задачей является создание рендерера, который бы выводил в ячейку грида заказов ссылку на клиента, оформившего заказ. Для формирования ссылки на клиента мне нужно использовать идентификатор соответствующего клиента — customer_id. Можно написать свой собственный шаблон для рендеринга, но в данном случае меня вполне устраивает имеющийся шаблон (./module-ui/view/base/web/templates/grid/cells/link.html). Достаточно переписать JS-код, который бы возвращал нужный результат при вызове функций $col.getLink($row()) и $col.isLink($row()).
Я разделил свой код на две части. Файл base.js содержит базовую логику для формирования ссылки, используемой в шаблоне, а файл customer_name.js позволяет настраивать базовую логику формирования ссылки в соответствии с задачами конкретного столбца.
Базовый функционал
В качестве базы я беру существующий UI-компонент column:
define([ "Magento_Ui/js/grid/columns/column", "mageUtils" ], function (Column, utils) { ... }
и (пере)определяю его атрибуты, указывая, что для рендеринга используется шаблон ui/grid/cells/link (из модуля Magento_Ui):
return Column.extend({ defaults: { /** * Replace idAttrName & route in children. */ /* name of the identification attribute */ idAttrName: "customer_id", /* route part to the page */ route: "/customer/index/edit/id/", bodyTmpl: "ui/grid/cells/link" } });
а затем (пере)определяю методы, используемые в шаблоне.
isLink (ссылку можно сформировать, если данные record содержат атрибут с именем, хранящимся в this.idAttrName):
isLink: function (record) { const result = !!utils.nested(record, this.idAttrName); return result; }
getLink:
getLink: function (record) { const id = utils.nested(record, this.idAttrName); const result = ROOT_URL + this.route + id; return result; }
Ссылка на карточку клиента
В файле customer_name.js базовый функционал переопределяется таким образом, чтобы формировалась ссылка на карточку клиента "http://.../admin/customer/index/edit/id/..." на основании идентификатора клиента customer_id:
define([ "Flancer32_GridLink/js/grid/column/link/base" ], function (Column) { "use strict"; return Column.extend({ defaults: { idAttrName: "customer_id", route: "/customer/index/edit/id/" } }); });
Подключение рендерера
Кастомный рендерер подключается к гриду в файле с определением соответствующего UI-компонента. В нашем случае это ./module-sales/view/adminhtml/ui_component/sales_order_grid.xml. В собственном модуле создается файл ./view/adminhtml/ui_component/sales_order_grid.xml в котором переопределяем рендерер для соответствующего столбца:
<listing ...> <columns name="sales_order_columns"> <column name="customer_name" component="Vendor_Module/js/grid/column/link/customer_name"> <settings> <visible>true</visible> </settings> </column> </columns> </listing>
Опция settings/visible нужна для того, чтобы колонка "customer_name" была видима в гриде (по-умолчанию она не видна).
Порядок загрузки
При сборке воедино всех xml-дескрипторов различных частей приложения в Magento (в том числе и описание UI-компонентов) важен порядок обработки дескрипторов, относящихся к одним и тем же компонентам, но находящимся в разных модулях. В нашем случае это ./view/adminhtml/ui_component/sales_order_grid.xml. Если платформа сначала обработает дескриптор из нашего модуля, а потом из sales-модуля, то при слиянии дескрипторов конфигурация sales-модуля заместит нашу конфигурацию в тех местах, где определяются одни и те же атрибуты (так, параметр settings/visible будет равен "false"), хотя рендерер все равно будет использоваться наш (sales-модуль не определяет рендерер для ячейки "Customer Name").
Порядок загрузки прописывается в ./etc/module.xml:
<config ...> <module name="Vendor_Module" setup_version="0.1.0"> <sequence> <module name="Magento_Sales"/> </sequence> </module> </config>
В этом случае наш модуль будет загружаться после модуля "Magento_Sales" и наши настройки, в случае совпадения с настройками в sales-модуле, заместят настройки sales-модуля.
Резюме
Предоставляемый Magento платформой набор рендереров довольно базовый (например, не нашёл рендерера для целых чисел с выравниванием по правому краю), но создание собственных рендереров может оживить стандартный вид Magento-грида в админке.
Код данной публикации оформлен в виде модуля "mage2_ext_grid_column_renderer".
