VirtualSelect¶
Published: 2019-07-25
Author: Nickolas Burr
Description¶
The use of dependency injection in Magento makes it especially easy to
work with data from di.xml
. However, it is still so commonplace to
see hardcoded data in backend source_model
classes, which amounts to
a bunch of duplicated classes containing disparate data. Unifying the
data in di.xml
makes it easier to maintain and update throughout the
lifetime of the module.
An elegant approach to creating select source_model
classes with populated
data is to create a generic class that implements ArrayInterface
, and use
the power of the <virtualType>
to set the options dynamically.
Usage¶
<?xml version="1.0"?>
<!--
/**
* system.xml
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="vendor" translate="label" sortOrder="100">
<label>Vendor, Inc.</label>
</tab>
<section id="package" translate="label" sortOrder="100"
showInDefault="1" showInWebsite="1" showInStore="1">
<class>separator-top</class>
<label>Package</label>
<tab>vendor</tab>
<group id="general" translate="label" type="text" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>General Settings</label>
<field id="default_status" translate="label" type="select" sortOrder="10"
showInDefault="1" showInWebsite="1" showInStore="1">
<label>Default Status</label>
<!-- The <virtualType> class -->
<source_model>Vendor\Package\Model\Source\Select\Status</source_model>
</field>
</group>
</section>
</system>
</config>
Source¶
<?php
/**
* Generic.php
*/
declare(strict_types=1);
namespace Vendor\Package\Model\Source\Select;
use Magento\Framework\Option\ArrayInterface;
class Generic implements ArrayInterface
{
/** @property array $options */
protected $options = [];
/**
* @param array $data
* @return void
*/
public function __construct(array $data = [])
{
/* Inverts KV for array_walk. */
$data = array_flip($data);
array_walk(
$data,
[
$this,
'setOption'
]
);
}
/**
* @param int|string|null $value
* @param int|string $key
* @return void
*/
protected function setOption($value, $key): void
{
$this->options[] = [
'label' => __($key),
'value' => $value,
];
}
/**
* @return array
*/
public function toOptionArray()
{
return $this->options;
}
}
<?xml version="1.0"?>
<!--
/**
* di.xml
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Vendor\Package\Model\Source\Select\Status"
type="Vendor\Package\Model\Source\Select\Generic">
<arguments>
<argument name="data" xsi:type="array">
<item name="pending" xsi:type="string">Pending</item>
<item name="closed" xsi:type="string">Closed</item>
<item name="open" xsi:type="string">Open</item>
<item name="on_hold" xsi:type="string">On Hold</item>
</argument>
</arguments>
</virtualType>
</config>