In this post we’ll continue with our project continuing from part 1. Here we’ll add the value of the order attribute order_source to the order grid table when an order is placed.
In the next post, we’ll show this value in the order grid. Again, if you’re mainly interested in the code for the solution, that’s shown in green.
So our task, we want to add the value of order_source to the resource model for sales order grid, so that when the order is placed, all attribute values for the order grid including order_source are saved to the sales_order_grid table. In part 1 we saw an example of dependency injection, one of Magento 2’s fundamental design patterns. There we saw how an object was injected into the constructor of our custom class so that it would be instantiated and available for use when the custom class was instantiated. For our task here of adding an additional column to the resource model of order grid, similarly we can inject, not an object as was the case in part 1, but an argument into the constructor of an existing class. Virtual types are the design pattern in Magento 2 that enable us to do this. Here’s the code we need:
DavidMann/OrderSource/etc/di.xml – add the column value to the order grid table (inject the new column as argument to the grid resource model constructor)
1<?xml version=”1.0″?>
2<config xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:noNamespaceSchemaLocation=”urn:magento:framework:ObjectManager/etc/config.xsd”>
3<virtualType name=”Magento\Sales\Model\ResourceModel\Order\Grid” type=”Magento\Sales\Model\ResourceModel\Grid”>
4<arguments>
5<argument name=”columns”>
6<item name=”order_source” xsi:type=”string”>sales_order.order_source</item>
7</argument>
8</arguments>
9</virtualType>
10</config>
This xml enables us to inject an argument named columns with an item name order_source into the resource model constructor for Magento/Sales/Model/ResourceModel/Grid. When an HTTP request is made all xml files are merged and classified by xml type, e.g. preferences, virtual types, arguments. At this point arguments are classified by resource model and by sub type, e.g. instanceName, cache, reader, data, metadata, mainTableName, columns – a very long list. If you’re interested, you can see this long list of sub types in the ‘extend’ method of vendor/magento/framework/ObjectManager/Config/Config.php under $this _arguments when you run just about any request against Magento.
In the case of the order grid resource model there are 7 sub types stored in the config under arguments and then under vendor\magento\module-sales\Model\ResourceModel\Grid, and we’ll see in a moment when these are retrieved and how they knit neatly into the arguments of the order grid resource model constructor. In the sequence they are stored in the array these are the sub types: mainTableName, gridTableName, orderidField, joins, columns, notSyncedDataProvider and idListProvider. The sub type ‘columns’ of course includes all columns specified in xml core code and in custom di.xml such as this one of ours: DavidMann/OrderSource/etc/di.xml.
Let’s look at the detail of what happens when its time to retrieve the order grid columns when an order is placed. This method is responsible for instantiating the order grid resource model vendor/magento/module-sales/Model/ResourceModel/Grid.php on line 121.
vendor/magento/framework/ObjectManager/Factory/AbstractFactory.php
118protected function createObject($type, $args)
119{
120try {
121return new $type(…array_values($args));
The 3 dot (…) PHP splat operator supplies a variable number of arguments in the instantiation of the object, and that’s simply because objects have a varying number of arguments in their constructor. The object name ‘$type’ and the parameters ‘$arg’ are supplied by the ‘create’ method in vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php and that calls createObject($type, $args). Within the ‘create’ method there’s this call $args = $this->_resolveArguments($requestedType, $parameters, $arguments) on line 59.
vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php
45public function create($requestedType, array $arguments = [])
46{
47$type = $this->config->getInstanceType($requestedType);
48$parameters = $this->definitions->getParameters($type);
49if ($parameters == null) {
50return new $type();
51}
52if (isset($this->creationStack[$requestedType])) {
53$lastFound = end($this->creationStack);
54$this->creationStack = [];
55throw new \LogicException(“Circular dependency: {$requestedType} depends on {$lastFound} and vice versa.”);
56}
57$this->creationStack[$requestedType] = $requestedType;
58try {
59$args = $this->_resolveArguments($requestedType, $parameters, $arguments);
60unset($this->creationStack[$requestedType]);
61} catch (\Exception $e) {
62unset($this->creationStack[$requestedType]);
63throw $e;
64}
65
66return $this->createObject($type, $args);
67}
And taking a peek at this _resolveArguments method in vendor/magento/framework/Factory/Dynamic/Developer.php we see this code:
vendor/magento/framework/Factory/Dynamic/Developer.php
22protected function _resolveArguments($requestedType, array $parameters, array $arguments = [])
23{
24// Get default arguments from config, merge with supplied arguments
25$defaultArguments = $this->config->getArguments($requestedType);
26if (is_array($defaultArguments)) {
27if (count($arguments)) {
28$arguments = array_replace($defaultArguments, $arguments);
29} else {
30$arguments = $defaultArguments;
31}
32}
33
34return $this->resolveArgumentsInRuntime($requestedType, $parameters, $arguments);
35}
The key line for us here is line 25. Now one of the requested types ($requestedType) when the order placed is Magento\Sales\Model\ResourceModel\Order\Grid. And under this $requestedType under _arguments, the 7 sub types we mentioned earlier are listed in an array. And within the array under the sub type ‘columns’, all the columns for order grid are listed including ‘order_source’.
Now back to the instantiation of the order grid resource model triggered within the method createObject($type, $args) in line 67 of vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php. In the argument $args are the 7 arguments retrieved plus ‘context’ that the resolveArguments method adds at the beginning of the array. So consequently we have this array of arguments calling the constructor: context (Magento\Framework\Model\ResourceModel\Db\Context), mainTableName, gridTableName, orderidField, joins, columns, notSyncedDataProvider and idListProvider.
And here’s the constructor:
vendor/magento/module-sales/Model/ResourceModel/Grid.php
63public function __construct(
64Context $context,
65$mainTableName,
66$gridTableName,
67$orderIdField,
68array $joins = [],
69array $columns = [],
70$connectionName = null,
71NotSyncedDataProviderInterface $notSyncedDataProvider = null
72) {
73$this->mainTableName = $mainTableName;
74$this->gridTableName = $gridTableName;
75$this->orderIdField = $orderIdField;
76$this->joins = $joins;
77$this->columns = $columns;
78$this->notSyncedDataProvider =
79$notSyncedDataProvider ?: ObjectManager::getInstance()->get(NotSyncedDataProviderInterface::class);
80parent::__construct($context, $connectionName);
81}
So we can see how neatly the calling array of arguments maps into the sequence of arguments of the constructor in lines 64 through 71. And all columns are mapped into the columns array ($columns = []) including order_source.
So we’ve seen that by specifying a virtual type in di.xml, we can inject an argument (or arguments) into a resource model constructor thereby fundamentally
altering the class’s behavior.
In the next post we’ll show the order_source attribute in the sales_order_grid in Admin.