Magento 2: Create Dynamic Row System Configuration

Magento 2 Create Dynamic Row System Configuration - SbDevBlog

In this post, we will create a dynamic row system configuration. This is because we often require dynamic values for rows. For example, we usually save rows as individual fields in separate tables or keep them as JSON in different tables.

However, Magento provides a mechanism to save dynamic rows in its core_config_data table as a system configuration. Hence, we should use it to hold and use dynamic row system configuration.

We can achieve this by using the ArraySerlialized class. We just need to inject the below class as a backend model to save an array of rows in serialised form.

<backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>

We must add a custom class extending AbstractFieldArray as a frontend model to show dynamic row fields in configuration.

<frontend_model>SbDevBlog\Config\Block\Adminhtml\Form\Field\QtyPostCodes</frontend_model>

Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray is responsible for generating and showing fields with dynamic rows.

Let’s do Magento 2: Create Dynamic Row System Configuration.

  • Create a system.xml file inside the adminhtml directory of the module’s etc directory.
<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="sbdevblog" translate="label" sortOrder="10">
            <label>SB Dev Blog</label>
        </tab>
        <section id="sbdevblog" translate="label" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="1">
            <class>separator-top</class>
            <label>SB Dev Blog</label>
            <tab>sbdevblog</tab>
            <resource>SbDevBlog_Config::sbdevblog_config</resource>
            <group id="qty_postcodes" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Qty Available For Post codes</label>
                <field id="availability" translate="label" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Qty Against Post Code</label>
                    <frontend_model>SbDevBlog\Config\Block\Adminhtml\Form\Field\QtyPostCodes</frontend_model>
                    <backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>

Now, we need to create a front-end model class. In my case, the frontend class is named QtyPostCodes. I have created three columns. The first column type is a dropdown box, allowing the admin to select the respective product’s SKU as the column of the dynamic row. The second column allows the administrator to enter the quantity. And the third column allows the administrator to enter the postcode.

Let’s implement back-end and front-end model classes.

  • Create a QtyPostCodes.php file inside the mentioned path in system.xml as a frontend model.
<?php
/**
 * @copyright Copyright (c) sbdevblog (http://www.sbdevblog.com)
 */

namespace SbDevBlog\Config\Block\Adminhtml\Form\Field;

use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\DataObject;

class QtyPostCodes extends AbstractFieldArray
{
    /**
     * @var SkuColumn
     */
    private SkuColumn $skuColumn;

    /**
     * Prepare rendering the new field by adding all the needed columns
     */
    protected function _prepareToRender()
    {
        $this->addColumn('sku', ['label' => __('SKU'), 'size' => '100px', 'renderer' => $this->getSkuRenderer()]);
        $this->addColumn('qty', ['label' => __('Qty'), 'size' => '10px', 'class' => 'required-entry validate-number']);
        $this->addColumn('postcode', ['label' => __('Postcode'), 'size' => '20px', 'class' => 'required-entry']);

        $this->_addAfter = false;
        $this->_addButtonLabel = __('Add');
    }

    /**
     * Prepare existing row data object
     *
     * @param DataObject $row
     * @throws LocalizedException
     */
    protected function _prepareArrayRow(DataObject $row): void
    {
        $options = [];

        $sku = $row->getSku();
        if ($sku !== null) {
            $options['option_' . $this->getSkuRenderer()->calcOptionHash($sku)] = 'selected="selected"';
        }

        $row->setData('option_extra_attrs', $options);
    }

    /**
     * Get Sku Renderer
     *
     * @return SkuColumn
     * @throws LocalizedException
     */
    private function getSkuRenderer(): \SbDevBlog\Config\Block\Adminhtml\Form\Field\SkuColumn
    {
        $this->skuColumn = $this->getLayout()->createBlock(
            SkuColumn::class,
            '',
            ['data' => ['is_render_to_js_template' => true]]
        );
        return $this->skuColumn;
    }
}

Since we have used the drop-down type of the field, we need to create a column-type class as well. For example, suppose you use fields like dropdowns, date and time pickers, etc. Then, could you create the respective column class?

The drop-down column type class extends the following class.

Magento\Framework\View\Element\Html\Select

Let’s create a column-type class. In my case, it is SkuColumn at the exact location of the above class.

  • Could you create a SkuColumn.php file?
<?php
/**
 * @copyright Copyright (c) sbdevblog (http://www.sbdevblog.com)
 */

namespace SbDevBlog\Config\Block\Adminhtml\Form\Field;

use Magento\Framework\View\Element\Html\Select;
use Magento\Framework\View\Element\Context;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Api\SearchCriteriaInterface;

class SkuColumn extends Select
{
    /**
     * @var ProductRepositoryInterface
     */
    private ProductRepositoryInterface $productRepository;

    /**
     * @var SearchCriteriaInterface
     */
    private SearchCriteriaInterface $searchCriteria;

    /**
     * Constructor
     *
     * @param Context $context
     * @param ProductRepositoryInterface $productRepository
     * @param SearchCriteriaInterface $searchCriteria
     * @param array $data
     */
    public function __construct(
        Context                    $context,
        ProductRepositoryInterface $productRepository,
        SearchCriteriaInterface    $searchCriteria,
        array                      $data = []
    )
    {
        $this->productRepository = $productRepository;
        $this->searchCriteria = $searchCriteria;
        parent::__construct($context, $data);
    }

    /**
     * Set "name" for <select> element
     *
     * @param string $value
     * @return $this
     */
    public function setInputName(string $value): static
    {
        return $this->setName($value);
    }

    /**
     * Set "id" for <select> element
     *
     * @param $value
     * @return $this
     */
    public function setInputId($value): static
    {
        return $this->setId($value);
    }

    /**
     * Render block HTML
     *
     * @return string
     */
    public function _toHtml(): string
    {
        if (!$this->getOptions()) {
            $this->setOptions($this->getSourceOptions());
        }
        return parent::_toHtml();
    }

    /**
     * Get Product Options
     *
     * @return array
     */
    private function getSourceOptions(): array
    {
        $products = $this->productRepository->getList($this->searchCriteria)->getItems();
        $skus = [];
        if ($products) {
            foreach ($products as $product) {
                $skus[] = ["label" => $product->getName(), 'value' => $product->getSku()];
            }
        }
        return $skus;
    }
}

Save default values in the configuration.

Finally, to save default values to the configuration, we must create config.xml to save default values in our dynamic rows.

  • Create a config.xml file in the etc directory.
<?xml version="1.0"?>

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <sbdevblog>
            <qty_postcodes>
                <availability>
                    <item1>
                        <sku>24-WG085</sku>
                        <qty>5</qty>
                        <postcode>90001</postcode>
                    </item1>
                    <item2>
                        <sku>24-WG086</sku>
                        <qty>10</qty>
                        <postcode>90002</postcode>
                    </item2>
                    <item3>
                        <sku>24-WG087</sku>
                        <qty>15</qty>
                        <postcode>90003</postcode>
                    </item3>
                </availability>
            </qty_postcodes>
        </sbdevblog>
    </default>
</config>

It is easy to create a dynamic row configuration. I hope you like this post; please subscribe and share it with your connections. Also, please use the comment box to give your suggestions.

You can go ahead and check out the post to save variables in the flag table.

Also, you may Download Source Code

Note: Please verify the code of this blog and the relevant git repository before using it in production.
sb dev blog adobe commece Magento 2

🙂 HAPPY CODING 🙂

One thought on “Magento 2: Create Dynamic Row System Configuration

Leave a Reply

Your email address will not be published. Required fields are marked *