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
Best Answer
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 theMagento\Braintree
module will do. This is the field declaration:The main point where your code deviates from the example is the
backend_model
being used. You should look atMagento\Braintree\Model\Adminhtml\System\Config\CountryCreditCard
and adapt it to your specific field to create a custom backend model to use (instead ofMagento\Config\Model\Config\Backend\Serialized\ArraySerialized
). It should be mostly a matter of replacing 'country_id' and 'cc_types' with your own column ids.