2

I've created a module with a custom config table. One of the columns is a select box with few options. When I select option and save configuration, selected option is saved in a DB table but not showing in admin.

My setup is like this:

system.xml

<field id="document_name_map" translate="label comment" sortOrder="21" showInDefault="1" showInWebsite="1" showInStore="0">
                <label>Document Name Mapping</label>
                <frontend_model>Custommodule\Name\Block\Adminhtml\System\Config\Form\Field\DocumentNameMapping</frontend_model>
                <backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>
            </field>

DocumentNameMapping.php

protected function _prepareToRender() {
    $this->addColumn('document_name', ['label' => __('Document Name'), 'type' => 'store']);
    $this->addColumn(
        'component_code',
        [
            'label' => __('Primgroup Document Component Code'),
            'renderer' => $this->_getComponentCodeRenderer()
        ]
    );
    $this->_addAfter = false;
    $this->_addButtonLabel = __('Add');
}

/**
 * @return \Magento\Framework\View\Element\BlockInterface
 */
protected function  _getComponentCodeRenderer()
{
    if (!$this->_typeRenderer) {
        $this->_typeRenderer = $this->getLayout()->createBlock(
            '\Primegroup\Oneflow\Block\Adminhtml\System\Config\Form\Field\Type\ComponentCode',
            '',
            ['data' => ['is_render_to_js_template' => true]]

        );
    }
    return $this->_typeRenderer;
}

/**
 *
 */
protected function _prepareArrayRow(\Magento\Framework\DataObject $row)
{
    $componentCode = $row->getData('component_code');
    $options = [];
    if ($componentCode) {
        $key = 'option_' . $this->_getComponentCodeRenderer()->calcOptionHash($componentCode);
        $options[$key] = 'selected="selected"';
    }
    $row->setData('option_extra_attrs', $options);
}

and ComponentCode.php is creating options from product attribute.

So all this creates this table: enter image description here

So, when I choose option and save I can see that right selected option is saved in a DB but here always showing just first.

In DB:a:1:{s:18:"_1498484025538_538";a:2:{s:13:"document_name";s:5:"jhgjg";s:14:"component_code";s:3:"INN";}}

I noticed that selected field looks like:

<select name="groups[rendering_settings][fields][document_name_map][value][_1498462459122_122][component_code]" id="" class="" title=""><option value="TAG">TAG</option><option value="WRAP">WRAP</option><option value="WRP">WRP</option><option value="INN">INN</option><option value="CVR">CVR</option><option value="ITM">ITM</option></select>

I really think that some JS needs to be changed, because in that select box code I cannot see option_

EDIT [SOLVED]

So if your config table contains input fields only, you can use this as a <backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>

But If your config table contains fields like dropdowns, multiselect...you will need to make a custom backend_module that will do serialisation of a data. In my case I've created this: <backend_model>Custommodule\Name\Model\Adminhtml\System\Config\DocumentNameMapping</backend_model> that looks like this:

<?php

namespace Custommodule\Name\Model\Adminhtml\System\Config;

use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Value;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Math\Random;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;

class DocumentNameMapping extends Value
{
    /**
     * @var Random
     */
    protected $mathRandom;

    /**
     * DocumentNameMapping constructor.
     *
     * @param Context               $context
     * @param Registry              $registry
     * @param ScopeConfigInterface  $config
     * @param TypeListInterface     $cacheTypeList
     * @param Random                $mathRandom
     * @param AbstractResource|null $resource
     * @param AbstractDb|null       $resourceCollection
     * @param array                 $data
     */
    public function __construct(
        Context $context,
        Registry $registry,
        ScopeConfigInterface $config,
        TypeListInterface $cacheTypeList,
        Random $mathRandom,
        AbstractResource $resource = null,
        AbstractDb $resourceCollection = null,
        array $data = []
    ) {
        $this->mathRandom = $mathRandom;
        parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
    }

    /**
     * Prepare data before save
     *
     * @return $this
     */
    public function beforeSave()
    {
        $value = $this->getValue();
        $result = [];
        foreach ($value as $data) {
            if (empty($data['document_name']) || empty($data['component_code'])) {
                continue;
            }
            $documentName = $data['document_name'];
            if (array_key_exists($documentName, $result)) {
                $result[$documentName] = $this->appendUniqueCompCodes($result[$documentName], $data['component_code']);
            } else {
                $result[$documentName] = $data['component_code'];
            }
        }
        $this->setValue(serialize($result));
        return $this;
    }

    /**
     * Process data after load
     *
     * @return $this
     */
    public function afterLoad()
    {
        $value = unserialize($this->getValue());
        if (is_array($value)) {
            $value = $this->encodeArrayFieldValue($value);
            $this->setValue($value);
        }
        return $this;
    }

    /**
     * Encode value to be used in \Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray
     *
     * @param array $value
     * @return array
     */
    protected function encodeArrayFieldValue(array $value)
    {
        $result = [];
        foreach ($value as $documentName => $componentCode) {
            $id = $this->mathRandom->getUniqueHash('_');
            $result[$id] = ['document_name' => $documentName, 'component_code' => $componentCode];
        }
        return $result;
    }

    /**
     * Append unique countries to list of exists and reindex keys
     *
     * @param array $docNamesList
     * @param array $compCodesList
     * @return array
     */
    private function appendUniqueCompCodes(array $docNamesList, array $compCodesList)
    {
        $result = array_merge($docNamesList, $compCodesList);
        return array_values(array_unique($result));
    }

}

Example suggested by Aaron was perfect for solving this problem. Thank you

2
  • Any help for saving dropdown and 3 input field? May 10, 2018 at 9:49
  • can you open this codes on github or elsewhere? I'm facing similar issues.
    – lpFranz
    Aug 7, 2018 at 17:21

1 Answer 1

3

In situations like this, it's a good idea to try and find an example in the magento core code of what you're trying to do. In this case, the countrycreditcard field in the Magento\Braintree module will do. This is the field declaration:

<field id="countrycreditcard" translate="label" sortOrder="220" showInDefault="1" showInWebsite="1" showInStore="0">
    <label>Country Specific Credit Card Types</label>
    <frontend_model>Magento\Braintree\Block\Adminhtml\Form\Field\CountryCreditCard</frontend_model>
    <backend_model>Magento\Braintree\Model\Adminhtml\System\Config\CountryCreditCard</backend_model>
    <config_path>payment/braintree/countrycreditcard</config_path>
</field>

The main point where your code deviates from the example is the backend_model being used. You should look at Magento\Braintree\Model\Adminhtml\System\Config\CountryCreditCard and adapt it to your specific field to create a custom backend model to use (instead of Magento\Config\Model\Config\Backend\Serialized\ArraySerialized). It should be mostly a matter of replacing 'country_id' and 'cc_types' with your own column ids.

1
  • Thank you, Aaron, that was exactly what I was looking for. I've Edit my question based on your answer.
    – iva
    Jun 27, 2017 at 20:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.