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:
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