Solution for Magento® layout update incompatibility between versions 2.0.x and 2.1.x


Solution for Magento® layout update incompatibility between versions 2.0.x and 2.1.x

During extension development we want to make sure the extension will work for each Magento® version. Sometimes that’s difficult because the Magento® team can make changes that are not backwards compatible. It can happen for example between Magento® 2.0.x and 2.1.x.

Related Stories:

Layout update incompatibility between versions 2.0.x and 2.1.x

Let’s say that we want to add a new filter, mass actions and column for our sales order grid. We create them through layout update files, which will be called in this case sales_order_grid.xml under view/adminhtml/ui_compontent directory in our extension and we define them under the listingToolbar xml tag for Magento® 2.1.x version, but Magento® 2.0.x requires to place it under the container xml tag.

There is no way to create generic xml code for this case.
In response we have created a solution which works on a low level, when Magento® is reading our extension layout update file we simply replace tag names. It runs very fast, poses no performance problems and is executed only once, then gets cached by Magento® when all layout updates are merged.

Our Solution

Our solution is based on two plugins.

For the first step let’s define our plugins in the admin di config file:

<type name="Magento\Framework\View\Layout\File\Collector\Aggregated">
<plugin name="samplePluginNameGetKey"
type="\Namespace\To\Our\Plugin"
sortOrder="10"/>
</type>
<type name="Magento\Framework\View\Element\UiComponent\Config\FileCollector\AggregatedFileCollector">
<plugin name="samplePluginNameReplace"
type="\Namespace\To\Our\Plugin"
sortOrder="10"/>
</type>


The second step is to create our plugins. The first will save in the registry our layout update file index code which the file collector will generate for it, then in the second plugin we will be able to easily recognize our layout update file content and replace the desired tag name contained within.
Our GetKey plugin will look like:

<?php

namespace Namespace\To\Our\Plugin;

use Closure;
use Vendor\Extension\Model\Config as ModuleConfig;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\ProductMetadata;
use Magento\Framework\Filesystem;
use Magento\Framework\Registry as CoreRegistry;
use Magento\Framework\View\Design\ThemeInterface;
use Magento\Framework\View\Layout\File\Collector\Aggregated;

class GetKey
{
/**
* Core registry object.
*
* @var CoreRegistry
*/
protected $coreRegistry;

/**
* @var Filesystem
*/
protected $filesystem;

/**
* Product meta data object.
*
* @var ProductMetadata
*/
protected $productMetadata;

/**
* Object constructor.
*
* @param CoreRegistry $coreRegistry Core registry object.
* @param Filesystem $filesystem File system object.
* @param ProductMetadata $productMetadata Product meta data object.
*/
public function __construct(
CoreRegistry $coreRegistry,
Filesystem $filesystem,
ProductMetadata $productMetadata
) {
$this->coreRegistry = $coreRegistry;
$this->filesystem = $filesystem;
$this->productMetadata = $productMetadata;
}

/**
* Plugin for getFiles method. Get order archive layout update file index
* and save it in registry to be able to recognize it in further processing.
* It will be applied for magento version lower than 2.1.
*
* @param Aggregated $subject Subject object.
* @param Closure $proceed Closure object.
* @param ThemeInterface $theme Theme object.
* @param string $filePath File path.
*
* @return array
*/
public function aroundGetFiles(
Aggregated $subject,
Closure $proceed,
ThemeInterface $theme,
$filePath
) {
$files = $proceed($theme, $filePath);
if ($filePath !== 'sales_order_grid.xml') {
return $files;
}

$version = $this->productMetadata->getVersion();
if (version_compare($version, '2.1') >= 0) {
return $files;
}

$fileReader = $this->filesystem->getDirectoryRead(DirectoryList::ROOT);

foreach ($files as $index => $file) {
if ($file->getModule() !== 'Vendor_Extension') {
continue;
}

$filePath = $fileReader->getRelativePath($file->getFilename());
$fileKey = sprintf('%x', crc32($filePath));

$this->coreRegistry->register(
ModuleConfig::REGISTRY_SALES_ORDER_GRID_FILE_INDEX,
$fileKey
);
}

return $files;
}
}


And our Replace plugin will look like:

coreRegistry = $coreRegistry;
}

/**
* Plugin for collectFiles method. For Magento® version lower than 2.1
* replace listingToolbar tag to container in sales_order_grid layout update.
*
* @param AggregatedFileCollector $subject Subject object.
* @param array $result Result array.
*
* @return array
*/
public function afterCollectFiles(
AggregatedFileCollector $subject,
array $result
) {
$fileKey = $this->coreRegistry
->registry(ModuleConfig::REGISTRY_SALES_ORDER_GRID_FILE_INDEX);

if ($fileKey === null) {
return $result;
}
if (empty($result[$fileKey])) {
return $result;
}

$this->coreRegistry
->unregister(ModuleConfig::REGISTRY_SALES_ORDER_GRID_FILE_INDEX);

$result[$fileKey] = str_replace(
'listingToolbar',
'container',
$result[$fileKey]
);

return $result;
}
}

Snippet url:
https://bitbucket.org/snippets/cminds/kAor6

Migration to Magento 2