The answer above from Nirav Patel is really useful, but the actual plugin code is unnecessarily complicated and just dumps in a SQL query directly, rather than using the the available parent methods to manipulate the query.
The idea is that before the order grid table is loaded, its possible to intercept the call with a plugin and modify the database query, joining other tables, and selecting the additional columns you need, which in this case is the SKU.
The class responsible for loading the data rendered in the sales order grid is \Magento\Sales\Model\ResourceModel\Order\Grid\Collection
, so we need define our plugin in our modules /etc/di.xml
to intercept this class:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Sales\Model\ResourceModel\Order\Grid\Collection">
<plugin name="[vendor]_[module]_myplugin" type="[Vendor]\[Module]\Plugin\Admin\Order\Grid" sortOrder="10" disabled="false"/>
</type>
</config>
Then create the actual plugin itself in the file referenced above [Vendor]\[Module]\Plugin\Admin\Order\Grid
<?php
namespace [Vendor]\[Module]\Plugin\Admin\Order;
class Grid
{
public function beforeLoad(\Magento\Sales\Model\ResourceModel\Order\Grid\Collection $subject)
{
$subject->join(
['sales_order_item' => 'sales_order_item'],
'sales_order_item.order_id = main_table.entity_id')
->addExpressionFieldToSelect('sku', "GROUP_CONCAT({{sku}} SEPARATOR '|')", ['sku' => 'sku'])
->getSelect()->group('main_table.entity_id');
}
}
The $subject
passed to the plugin's beforeLoad
method is an instance of the resource model \Magento\Sales\Model\ResourceModel\Order\Grid\Collection
, responsible for loading the data. The ancestors of this class include the load
method, which is the one we're intercepting.
We're using the \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::join
method to join to the sales_order_item
table to get the SKU data.
Then the \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::addExpressionFieldToSelect
allows us to add in a SQL agregating expression. Which in this case is the GROUP_CONCAT
to create a string of all SKUs for the order. This new aggregate column will be aliased with the name sku
.
Finally we get the select statement with \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection::getSelect
, in order to add the group by clause.
At this point, all we've done is modify the query, so that when \Magento\Framework\Data\Collection\AbstractDb::load
is called, the query is changed to return the extra SKU data in a new column. To actually show this data in the admin grid, we have to add the column to the UI component. This is done by adding a file to the module in the location view/adminhtml/ui_component/sales_order_grid.xml
with the content
<?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="skus" class="[Vendor]\[Module]\Ui\Component\Listing\Column\Sku">
<settings>
<label>Skus</label>
<sortable>false</sortable>
<bodyTmpl>ui/grid/cells/html</bodyTmpl>
<visible>true</visible>
</settings>
</column>
</columns>
</listing>
The class attribute on the column
node is unnessesary as is the bodyTmpl
node, but together these can be used to preprocess the raw SQL result, formatting the SKU data as you want, eg a newline after each SKU. If you include the class attribute, then you need to define the class \[Vendor]\[Module]\Ui\Component\Listing\Column\Sku
as follows:
<?php
namespace [Vendor]\[Module]\Ui\Component\Listing\Column;
use Magento\Ui\Component\Listing\Columns\Column;
class Sku extends Column
{
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
$item[$this->getData('name')] = $this->prepareItem($item);
}
}
return $dataSource;
}
protected function prepareItem(array $item)
{
//return a string based on the $item['sku'] value
}
}