Как добавить столбец с фильтром в сетку заказов Magento 2?

Опубликовано: 2020-02-03

Часто администраторам магазинов Magento 2 требуются дополнительные опции, когда дело доходит до использования и настройки сетки заказов — фильтрация по определенному стандартному параметру может стать настоящей проблемой.

Недавно ко мне обратился разработчик Magento с вопросом. Пытался расширить сетку заказов на Magento 2.3.1. Старые посты, доступные в сети, не помогли ― естественно, за последние два года в Magento многое изменилось.

Исходя из его вопроса, предлагаю посмотреть решение для конкретного случая:

Нам нужно добавить в сетку заказов столбец — с каким-то региональным кодом покупателя, совершившего покупку. Кроме того, администратор магазина должен иметь возможность фильтровать заказы по этому вновь добавленному столбцу.

Это может показаться простым, но есть несколько вещей, на которые стоит обратить внимание. Например, у покупателя, совершившего покупку, может не быть адреса доставки — в случае виртуального заказа. Или, как мы будем перечислять регионы? Все это следует учитывать, приступая к разработке. Основываясь на вопросах, упомянутых ранее, давайте предположим:

  • виртуальные продукты не будут иметь регион доставки («нулевой»). Это поможет вам подобрать их по этому параметру,
  • регионы будут перечислены и иметь вид кодов без преобразования их в метки, как это есть в Magento 2 по умолчанию.

*Обратите внимание, что в конце этой статьи будет ссылка на модуль с открытым доступом на GitHub. Тем не менее, я включаю ссылку в этот абзац, если вам не удастся закончить эту статью: https://github.com/magevorx/articles-extended-orders-grid.

Однако, несмотря на нехватку времени, я призываю вас читать дальше.

Таким образом, чтобы добавить новую колонку в сетку заказов, нужно:

Оглавление

  • 1. Создайте новый модуль
  • 2. Добавьте столбец в сетку
    • Пояснения
  • 3. Добавьте данные в столбец
  • Как добавить дополнительный столбец?
  • [Обновление] Как добавить столбец с информацией об элементах заказа?

1. Создайте новый модуль

Во-первых, давайте определимся с именами модуля и поставщика. Ну, мне не нужно выбирать имя вендора ― это MageWorx (как будто у меня есть выбор, шучу). Я все еще могу выбрать имя модуля. Пусть это будет ExtendedOrdersGrid (кстати, у нас есть одноименное расширение для Magento 2). На самом деле, использование MageWorx в качестве имени поставщика в пространстве имен не дает вам права запрашивать бесплатную поддержку. В любом случае, если у членов нашей службы поддержки были хорошие выходные, вы все равно можете попробовать в понедельник.?

Давайте создадим следующий каталог: `app/code/MageWorx/ExtendedOrdersGrid`. Для регистрации модуля нам понадобятся стандартные файлы:

 > registration.php php <?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'MageWorx_ExtendedOrdersGrid', __DIR__ ); > composer.json json { "name": "mageworx/module-extended-orders-grid", "description": "Extended Orders Grid Extension", "require": { "magento/module-ui" : ">=100.1.0 < 102", "magento/module-sales" : ">=100.0.0 <103" }, "type": "magento2-module", "version": "1.0.0", "license": [ "OSL-3.0", "AFL-3.0" ], "autoload": { "files": [ "registration.php" ], "psr-4": { "MageWorx\\ExtendedOrdersGrid\\": "" } } } > etc/module.xml xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="MageWorx_ExtendedOrdersGrid" setup_version="1.0.0"> <sequence> <module name="Magento_Sales"/> <module name="Magento_Ui"/> </sequence> </module> </config>

Важно отметить, что я урезал авторские права, которые автоматически добавляются в мою IDE. Таким образом, вы можете использовать этот код без проблем. ?

Затем выполните несколько команд:

 > sudo -u www-data php bin/magento module:enable MageWorx_ExtendedOrdersGrid > sudo -u www-data php bin/magento setup:upgrade

И вуаля! Теперь наш модуль можно увидеть в Magento 2! О, если вы работаете на удаленном сервере, не забудьте передать файлы перед тестированием.

2. Добавьте столбец в сетку

Затем, используя пользовательский интерфейс Magento 2, давайте добавим новый столбец в стандартную сетку. Для этого создайте файл:

 > view/adminhtml/ui_component/sales_order_grid.xml

со следующим содержанием (его значение мы обсудим далее):

 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"> <columns name="sales_order_columns"> <column name="code"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item> <item name="label" xsi:type="string" translate="true">Region Code</item> <item name="sortOrder" xsi:type="number">60</item> <item name="align" xsi:type="string">left</item> <item name="dataType" xsi:type="string">text</item> <item name="visible" xsi:type="boolean">true</item> <item name="filter" xsi:type="string">text</item> </item> </argument> </column> </columns> </listing>

Здесь нужно указать, что имя ниоткуда не берется. Для опытных разработчиков это понятно, но для тех, кто только знакомится с Magento 2, добавлю некоторые пояснения в конце этого абзаца.

Мы использовали стандартный узел «столбцы» для добавления столбцов в сетку, где также должен быть добавлен новый столбец с именем «код». Далее запишем атрибуты нашего столбца в сетке:

1) Компонент . Это класс JS, который отвечает за создание и обработку этого столбца. Он находится в модуле Magento_Ui по следующему адресу:

`vendor/magento/module-ui/view/base/web/js/grid/columns/column.js`

На самом деле, если вы любопытный разработчик, взгляните и на другие реализации. Там много интересного.

2) Этикетка. Это строка с названием столбца, которое будет отображаться конечному пользователю. Не забудьте добавить его в файл локализации i18n, если установлено свойство `translate`. ?

3) Порядок сортировки. Это позиция столбца в сетке. Если модуль был установлен на Magento 2, которым никогда не управлял администратор магазина, это повлияет. В противном случае в используемом Magento 2 наш столбец будет добавлен в конец списка независимо от того, что мы делаем.

4) Выровнять. Это означает выравнивание содержимого столбца. Думаю, это ясно.

5) Тип данных. Это тип данных, которым мы будем манипулировать. В нашем случае это просто строка текста. Однако это может быть список или логическое значение, а также числа, если мы говорим, например, о количестве товаров.

6) Видимый. Это не что иное, как видимость столбца, хотя многое зависит от того, использовалась сетка ранее или нет.

Все данные о сетке хранятся в базе данных Magento, в таблице `ui_bookmark`. Таким образом, если на момент установки нашего модуля нет записи о нашей колонке для измененной сетки, чуда не ждите.

7) Фильтр . Это тип фильтра. Здесь мы указали, что фильтрация должна производиться как с обычным текстом. Во время выполнения кода он преобразуется в условие MySQL `LIKE %value%`.


Расширения MageWorx для Magento 2

Пояснения

Короче говоря, имя расширенного компонента пользовательского интерфейса должно соответствовать имени оригинала. В нашем случае это `vendor/magento/module-sales/view/adminhtml/ui_component/sales_order_grid.xml`, и он рассчитывается из корня модуля `view/adminhtml/ui_component/sales_order_grid.xml`.

Сама сетка пользовательского интерфейса добавляется к требуемому контроллеру с помощью макета `vendor/magento/module-sales/view/adminhtml/layout/sales_order_index.xml` следующим образом:

 xml <?xml version="1.0"?> <!-- /** * Copyright Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ --> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <update handle="styles"/> <body> <referenceContainer name="content"> <uiComponent name="sales_order_grid"/> </referenceContainer> </body> </page>

Это означает *добавьте этот компонент пользовательского интерфейса под именем `sales_order_grid` к содержимому страницы* ― так же, как и в случае с блоками. Однако вместо блоков у нас есть uiComponent со своими атрибутами и ограничениями.


Теперь очистите кеш Magento 2 с помощью следующей команды:

 > sudo -u www-data php bin/magento cache:clean config

и проверьте результаты в сетке заказов:

Как добавить столбец с фильтром в сетку заказов Magento 2? | Блог MageWorx Magento

3. Добавьте данные в столбец

Вы могли заметить, что колонка появилась, но ее содержимое выглядит не так, как ожидалось… данные там вообще не отображаются. Честно говоря, мы ничего не добавляли, поэтому там нет контента.

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

 > app/code/MageWorx/ExtendedOrdersGrid/Plugin/AddDataToOrdersGrid.php
 php <?php namespace MageWorx\ExtendedOrdersGrid\Plugin; /** * Class AddDataToOrdersGrid */ class AddDataToOrdersGrid { /** * @var \Psr\Log\LoggerInterface */ private $logger; /** * AddDataToOrdersGrid constructor. * * @param \Psr\Log\LoggerInterface $customLogger * @param array $data */ public function __construct( \Psr\Log\LoggerInterface $customLogger, array $data = [] ) { $this->logger = $customLogger; } /** * @param \Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory $subject * @param \Magento\Sales\Model\ResourceModel\Order\Grid\Collection $collection * @param $requestName * @return mixed */ public function afterGetReport($subject, $collection, $requestName) { if ($requestName !== 'sales_order_grid_data_source') { return $collection; } if ($collection->getMainTable() === $collection->getResource()->getTable('sales_order_grid')) { try { $orderAddressTableName = $collection->getResource()->getTable('sales_order_address'); $directoryCountryRegionTableName = $collection->getResource()->getTable('directory_country_region'); $collection->getSelect()->joinLeft( ['soa' => $orderAddressTableName], 'soa.parent_id = main_table.entity_id AND soa.address_type = \'shipping\'', null ); $collection->getSelect()->joinLeft( ['dcrt' => $directoryCountryRegionTableName], 'soa.region_id = dcrt.region_id', ['code'] ); } catch (\Zend_Db_Select_Exception $selectException) { // Do nothing in that case $this->logger->log(100, $selectException); } } return $collection; } }

Он будет относиться к следующему классу:

`Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory`

 > etc/adminhtml/di.xml xml <?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- Plugins --> <!-- Adds additional data to the orders grid collection --> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <plugin name="mageworx_extended_orders_grid_add_data_to_orders_grid" type="MageWorx\ExtendedOrdersGrid\Plugin\AddDataToOrdersGrid" sortOrder="10" disabled="false"/> </type> </config>

Плагин работает следующим образом:

Так как он будет ловить абсолютно все коллекции, добавим валидацию в нужную таблицу `sales_order_grid`. Когда он будет найден и Magento попытается получить данные об этом, мы делаем соединение таблицы с адресом `sales_order_address` по `order_id` (`entity_id` в таблице `sales_order_grid` и `parent_id` в таблице `sales_order_address` ( псевдоним `soa`)). Теперь у нас есть доступ к данным адреса доставки заказа клиента, которые мы можем использовать для определения `region_id`. Он находится в виде числа в таблице `sales_order_address`. Этот номер соответствует индексу в таблице `directory_country_region` (псевдоним `dcrt`). Поскольку нам нужен этот региональный код из таблицы, как было сказано выше, давайте подключим столбец «код». Этот точный столбец будет выведен (соответствует значению столбца в файле sales_order_grid.xml).

Затем очистите кеш:

 > sudo -u www-data php bin/magento cache:clean config

Войдите в панель администратора в сетку заказов. Если все было сделано правильно, мы увидим наши региональные коды:

Как добавить столбец с фильтром в сетку заказов Magento 2? | Блог MageWorx Magento

Бесплатный доступ к модулю доступен на GitHub: https://github.com/mageworx/articles-extended-orders-grid.

Важное примечание!

Чтобы добавить какие-либо данные в этот столбец, необходимо получить столбец your_column_name и все необходимые данные, добавленные при создании коллекции. То есть нужно сделать что-то другое ― заменить имя таблицы и написать свой джойн. Кроме того, не забудьте при необходимости отредактировать имя столбца в файле `sales_order_grid.xml`.

Как добавить дополнительный столбец?

Если вы решите добавить еще один столбец, следуйте инструкциям, описанным в следующем коммите Git: https://github.com/mageworx/articles-extended-orders-grid/commit/d31c364a25ce493ab64731c5ca0481e146dbbac3.

Там мы добавили столбец sales_order_address telephone адреса типа shipping . Как вы можете видеть в коде коммита, никаких существенных модификаций кода не потребовалось.

Кроме того, эти столбцы можно успешно экспортировать из стандартного интерфейса «сетка заказов».

Вот как выглядит сетка со столбцом «телефон»:

Как добавить столбец с фильтром в сетку заказов Magento 2? | Блог MageWorx Magento

На скриншоте ниже вы можете увидеть те же заказы, экспортированные в файл .csv:

Как добавить столбец с фильтром в сетку заказов Magento 2? | Блог MageWorx Magento

[Обновление] Как добавить столбец с информацией об элементах заказа?

Как вы знаете, все заказы имеют количество предметов, начиная с одного и заканчивая бесконечным числом (да, мечта торговцев). Но как мы можем отобразить эту информацию в сетке заказов, чтобы упростить поиск или анализ?

Мы не можем просто добавить столбец, как мы делали это ранее, потому что в этом случае мы получаем только одно название продукта или SKU в каждой строке. Или записи о заказах будут дублироваться… Нам тоже не нужен этот бардак.

Таким образом, я попытаюсь объяснить, как мы можем сделать это правильно (на мой взгляд).

Предположим, нам нужен столбец данных «Название продукта» в сетке заказов.

Во-первых, давайте добавим новый столбец в определение sales_order_grid , как мы делали это ранее:

 <columns> .... <column name="name"> <argument name="data" xsi:type="array"> <item name="config" xsi:type="array"> <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item> <item name="filter" xsi:type="string">text</item> <item name="label" xsi:type="string" translate="true">Product Name</item> <item name="visible" xsi:type="boolean">false</item> <item name="sortOrder" xsi:type="number">70</item> </item> </argument> </column> </columns>

Затем все, что нам нужно, это построить правильный запрос для нашего нового столбца. Он должен содержать название каждого продукта, купленного в этом заказе, через запятую, с возможностью поиска по значениям этого столбца и т. д. Давайте создадим новый метод в плагине mageworx_extended_orders_grid_add_data_to_orders_grid :

 /** * Adds products name column to the orders grid collection * * @param OrderGridCollection $collection * @return OrderGridCollection */ private function addProductsNameColumn(OrderGridCollection $collection): OrderGridCollection { return $collection; }

и назовите этот метод внутри исходного тела метода:

 if ($collection->getMainTable() === $collection->getResource()->getTable('sales_order_grid')) { try { $orderAddressTableName = $collection->getResource()->getTable('sales_order_address'); ... // Add product's name column $this->addProductsNameColumn($collection); } catch (\Zend_Db_Select_Exception $selectException) { ...

Чтобы получить нужные данные в один столбец, мы должны создать подвыборку с двумя колонками: order_id и name (название товара) в индивидуальной выборке, которую потом можно будет присоединить к основной коллекции:

 // Get original table name $orderItemsTableName = $collection->getResource()->getTable('sales_order_item'); // Create new select instance $itemsTableSelectGrouped = $collection->getConnection()->select(); // Add table with columns which must be selected (skip useless columns) $itemsTableSelectGrouped->from( $orderItemsTableName, [ 'name' => new \Zend_Db_Expr('GROUP_CONCAT(DISTINCT name SEPARATOR \',\')'), 'order_id' => 'order_id' ] ); // Group our select to make only one column for one order $itemsTableSelectGrouped->group('order_id');

Уточнения:

  • Строка $collection->getConnection()->select() создаст новый экземпляр Magento\Framework\Db\Select .
    Это необходимо, потому что мы не можем использовать исходный выбор из коллекции, так как он имеет свои данные внутри, и любые модификации приведут к ошибкам.
  • Столбец name должен содержать все названия продуктов для указанного заказа, т. е. он должен быть сгруппирован с использованием выражения \Zend_Db_Expr('GROUP_CONCAT(DISTINCT name SEPARATOR \',\')') . Для этого мы позже добавим group('order_id') в выбор. Без группировки мы не можем использовать функцию GROUP_CONCAT .

Теперь мы можем добавить наш sub-select в основную коллекцию, и это будет логическим завершением метода addProductsNameColumn :

 // Add our sub-select to main collection with only one column: name $collection->getSelect() ->joinLeft( ['soi' => $itemsTableSelectGrouped], 'soi.order_id = main_table.entity_id', ['name'] ); return $collection;

Уточнения:

  • soi — псевдоним для нашей псевдотаблицы.
  • order_id — это ключ, который мы используем, чтобы связать нашу основную таблицу (сетку) с данными об элементах заказа.
  • ['name'] — единственный столбец, который добавляется к результату, потому что нам не нужна другая информация.

Окончательный результат доступен в официальном репозитории этого примера.
Вот ссылка на конкретный коммит: https://github.com/mageworx/articles-extended-orders-grid/commit/0cdffcd4ba66cacb2fd857ba7626fdbcfc0d6fe3.

Вот как этот столбец выглядит на нашем промежуточном хосте:

?‍? Как добавить столбец с фильтром в сетку заказов Magento 2? | Блог MageWorx Magento

А вот результат экспорта (CSV):

А вот результат экспорта (CSV):

Вот как выглядит запрос при попытке поиска заказов с «черными» продуктами (на нашем хосте разработки):
SELECT main_table .*, soat . telephone , dcrt . code , soi . name ИЗ sales_order_grid КАК main_table
ЛЕВОЕ СОЕДИНЕНИЕ sales_order_address AS soat ON soat.parent_id = main_table.entity_id AND soat.address_type = 'shipping'
LEFT JOIN directory_country_region AS dcrt ON soat.region_id = dcrt.region_id
LEFT JOIN (SELECT GROUP_CONCAT (DISTINCT name SEPARATOR ',') AS name , sales_order_item . order_id FROM sales_order_item GROUP BY order_id ) AS soi ON soi.order_id = main_table.entity_id
ГДЕ soi . name НРАВИТСЯ '%Black%'

Очевидно, это не самый быстрый способ вывода данных. Впрочем, это, наверное, самое простое. Лучше всего собирать данные о названиях товаров в отдельной колонке отдельной таблицы (order_id, products_name) и добавлять эту таблицу без лишней группировки и какой-либо подвыборки.

Для этого мы добавляем группу ('order_id') в наш выбор (в конце кода метода).
Выбор означает элемент.
Группа означает метод выбора.


Думаю, это все. Если у вас есть какие-либо вопросы или пожелания, пожалуйста, оставьте комментарий в поле для комментариев.