如何将带过滤器的列添加到 Magento 2 订单网格?
已发表: 2020-02-03通常,Magento 2 商店管理员在使用和自定义订单网格时需要额外的选项——基于特定的开箱即用参数进行过滤可能成为真正的挑战。
最近,一位 Magento 开发人员向我提出了一个问题。 他试图在 Magento 2.3.1 上扩展订单网格。 网络上的旧帖子并没有帮助 - 自然,自过去两年以来,Magento 发生了很大变化。
根据他的问题,我建议您看一下特定案例的解决方案:
我们需要在订单网格中添加一列 - 包含完成购买的客户的一些区域代码。 此外,商店管理员必须能够按此新添加的列过滤订单。
这似乎很容易,但有几件事值得关注。 例如,购买的购物者可能没有任何送货地址——在虚拟订单的情况下。 或者,我们应该如何列出这些地区? 在开始开发时,所有这些都应考虑在内。 基于前面提到的问题,我们假设:
- 虚拟产品将没有交付区域('null')。 这将帮助您根据此参数选择它们,
- 区域将被列出并查看代码,而无需将其转换为标签,就像在默认的 Magento 2 中一样。
*请注意,本文末尾将提供一个指向 GitHub 上开放访问模块的链接。 不过,如果您无法完成本文,我会在本段中包含链接:https://github.com/mageworx/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 UI,让我们在标准网格中添加一个新列。 为此,创建一个文件:
> 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)标签。 这是带有列名的行,将显示给最终用户。 如果 `translate` 属性已经建立,不要忘记将它添加到 i18n 本地化文件中。 ?
3)排序顺序。 这是网格中的列位置。 如果该模块已安装在从未由商店管理员管理过的 Magento 2 上,它将生效。 否则,在正在使用的 Magento 2 上,无论我们做什么,我们的列都会被添加到列表的末尾。
4)对齐。 这意味着对齐列内容。 我想这很清楚。
5)数据类型。 这是我们将要操作的数据类型。 在我们的例子中,这只是一行文本。 但是,如果我们谈论产品数量,它可以是列表或布尔值,也可以是数字。
6)可见。 这只不过是列可见性,尽管很大程度上取决于先前是否使用过网格。
有关网格的所有数据都存储在 Magento 数据库中的 `ui_bookmark` 表中。 因此,如果在我们的模块安装时没有关于修改网格的列的记录,不要指望任何奇迹。
7)过滤器。 这是过滤器类型。 在这里,我们指定应该像处理常规文本一样进行过滤。 在运行代码时,它将转换为 MySQL `LIKE %value%` 条件。
解释
长话短说,扩展 UI 组件的名称必须与原始组件的名称相对应。 在我们的例子中,它是 `vendor/magento/module-sales/view/adminhtml/ui_component/sales_order_grid.xml`,它是从 `view/adminhtml/ui_component/sales_order_grid.xml` 模块根目录计算的。
UI 网格本身使用 `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` 名称下的 UI 组件添加到页面内容* - 就像块的情况一样。 然而,我们得到的不是块,而是 `uiComponent` 及其属性和限制。
现在,使用以下命令清除 Magento 2 缓存:
> sudo -u www-data php bin/magento cache:clean config
并检查订单网格中的结果:
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` (`sales_order_grid` 表中的`entity_id` 和`sales_order_address` 表中的`parent_id` 进行连接( `soa` 别名))。 现在,我们可以访问客户的订单送货地址数据,我们可以使用这些数据来确定“region_id”。 它位于“sales_order_address”表中的一个数字。 这个数字对应于 `directory_country_region` 表中的索引(`dcrt` 别名)。 由于我们需要上面提到的表格中的区域代码,让我们连接“代码”列。 这个精确的列将得到输出(对应于 `sales_order_grid.xml` 中的 `column` 值)。
然后,清除缓存:
> sudo -u www-data php bin/magento cache:clean config
登录管理面板到订单网格。 如果一切都正确完成,我们将看到我们的区域代码显示:
可在 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
类型的地址。 正如您在提交代码中看到的那样,不需要对代码进行重大修改。
此外,这些列可以从标准的“订单网格”界面成功导出。
以下是带有“电话”列的网格的样子:
在下面的屏幕截图中,您可以看到导出到 .csv 文件的相同订单:
[更新] 如何添加包含订单商品信息的列?
如您所知,所有订单都有数量,从一个开始到超出数量(是的,商人的梦想)。 但是我们如何在订单网格中显示该信息以更轻松地开始搜索或运行分析?
我们不能像之前那样只添加一列,因为在这种情况下,每一行我们只会得到一个产品名称或 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) { ...
要在一列中获取所需的数据,我们必须在单个 select 中创建一个包含两列order_id
和name
(产品名称)的子 select,稍后可以将其加入到主集合中:
// 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
函数。
现在,我们可以将我们的子选择添加到主集合中,这将是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
这是该列在我们的暂存主机上的外观:
这是导出的结果(CSV):
以下是尝试使用“黑色”产品(在我们的开发主机上)搜索订单时查询的样子:
选择main_table
.*, soat
。 telephone
, dcrt
。 code
, soi
。 name
FROM sales_order_grid
AS main_table
左连接sales_order_address
AS soat
ON soat.parent_id = main_table.entity_id AND soat.address_type = 'shipping'
左连接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
LIKE '%Black%'
显然,这不是输出数据的最快方式。 但是,这可能是最简单的一个。 最好的方法是在单独的表(order_id,products_name)的单独列中累积有关产品名称的数据,然后添加此表,无需额外分组和任何子选择。
为此,我们将组 ('order_id') 添加到我们的选择中(在方法代码的末尾)。
select 表示一个元素。
组表示选择方法。
我想就是这样。 如果您有任何问题或要求,请在评论栏中留下评论。