Magento2 How to add row pattern/dynamic rows to the custom product attribute.
Here is the sample output
Magento2 How to add row pattern/dynamic rows to the custom product attribute.
Here is the sample output
Here are the high-level things we need to do.
Create Product custom attribute -> Which attribute you want to show the row pattern / dynamic rows
Create di.xml -> Add our custom Data provider into the product form
Create DataProvider -> Set row patter fields and data to the attribute
Create Observer (events.xml) -> Add/Update attribute value while saving the product in the Magento Admin.
Observer is optional because you can set backend model like "backend => Magento\Eav\Model\Entity\Attribute\Backend\Serialized.php"
during create the product. So this is taken care of the save/load product with array serialized value.
In my case, this doesn't work throws the error when going to the catalog product so i remove the backend model and did the events.xml
Here are the steps we have to follow
Assume product custom attribute called "attraction_highlights"
You can create the custom product attribute through Magento Admin/ setup script.
The general structure of the di.xml file is:
File path like MAGENTO_ROOT\app\code\NAMESPACE\MODULE\etc\adminhtml\di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\Pool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="attractionHighlights" xsi:type="array">
<item name="class" xsi:type="string">Born\Attractions\Ui\DataProvider\Product\Form\Modifier\Highlights</item>
<item name="sortOrder" xsi:type="number">100</item>
</item>
</argument>
</arguments>
</virtualType>
</config>
Then you just create the data provider file (Highlights.php) :
File path like MAGENTO_ROOT\app\code\NAMESPACE\MODULE\Ui\DataProvider\Product\Form/Modifier/Highlights.php
Here is a sample code of the DataProvider (The data provider children you can load from the XML file also)
<?php
namespace Born\Attractions\Ui\DataProvider\Product\Form\Modifier;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\StockDataFilter;
use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\CatalogInventory\Api\Data\StockItemInterface;
use Magento\CatalogInventory\Api\StockConfigurationInterface;
use Magento\Ui\Component\Container;
use Magento\Ui\Component\Form\Element\DataType\Number;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\Textarea;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Modal;
/**
* Data provider for attraction highlights field
*/
class Highlights extends AbstractModifier
{
const ATTRACTION_HIGHLIGHTS_FIELD = 'attraction_highlights';
/**
* @var LocatorInterface
*/
private $locator;
/**
* @var ArrayManager
*/
private $arrayManager;
/**
* @var array
*/
private $meta = [];
/**
* @var string
*/
protected $scopeName;
/**
* @param LocatorInterface $locator
* @param ArrayManager $arrayManager
*/
public function __construct(
LocatorInterface $locator,
ArrayManager $arrayManager,
$scopeName = ''
) {
$this->locator = $locator;
$this->arrayManager = $arrayManager;
$this->scopeName = $scopeName;
}
/**
* {@inheritdoc}
*/
public function modifyData(array $data)
{
$fieldCode = self::ATTRACTION_HIGHLIGHTS_FIELD;
$model = $this->locator->getProduct();
$modelId = $model->getId();
$highlightsData = $model->getAttractionHighlights();
if ($highlightsData) {
$highlightsData = json_decode($highlightsData, true);
$path = $modelId . '/' . self::DATA_SOURCE_DEFAULT . '/'. self::ATTRACTION_HIGHLIGHTS_FIELD;
$data = $this->arrayManager->set($path, $data, $highlightsData);
}
return $data;
}
/**
* {@inheritdoc}
*/
public function modifyMeta(array $meta)
{
$this->meta = $meta;
$this -> initAttractionHighlightFields();
return $this->meta;
}
/**
* Customize attraction highlights field
*
* @return $this
*/
protected function initAttractionHighlightFields()
{
$highlightsPath = $this->arrayManager->findPath(
self::ATTRACTION_HIGHLIGHTS_FIELD,
$this->meta,
null,
'children'
);
if ($highlightsPath) {
$this->meta = $this->arrayManager->merge(
$highlightsPath,
$this->meta,
$this->initHighlightFieldStructure($highlightsPath)
);
$this->meta = $this->arrayManager->set(
$this->arrayManager->slicePath($highlightsPath, 0, -3)
. '/' . self::ATTRACTION_HIGHLIGHTS_FIELD,
$this->meta,
$this->arrayManager->get($highlightsPath, $this->meta)
);
$this->meta = $this->arrayManager->remove(
$this->arrayManager->slicePath($highlightsPath, 0, -2),
$this->meta
);
}
return $this;
}
/**
* Get attraction highlights dynamic rows structure
*
* @param string $highlightsPath
* @return array
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function initHighlightFieldStructure($highlightsPath)
{
return [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'dynamicRows',
'label' => __('Highlight Rows'),
'renderDefaultRecord' => false,
'recordTemplate' => 'record',
'dataScope' => '',
'dndConfig' => [
'enabled' => false,
],
'disabled' => false,
'sortOrder' =>
$this->arrayManager->get($highlightsPath . '/arguments/data/config/sortOrder', $this->meta),
],
],
],
'children' => [
'record' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Container::NAME,
'isTemplate' => true,
'is_collection' => true,
'component' => 'Magento_Ui/js/dynamic-rows/record',
'dataScope' => '',
],
],
],
'children' => [
'title' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Title'),
'dataScope' => 'title',
'require' => '1',
],
],
],
],
'description' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Textarea::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Description'),
'dataScope' => 'description',
'require' => '1',
],
],
],
],
'icon' => [
'arguments' => [
'data' => [
'config' => [
'formElement' => Input::NAME,
'componentType' => Field::NAME,
'dataType' => Text::NAME,
'label' => __('Icon Name'),
'dataScope' => 'icon',
],
],
],
],
'actionDelete' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => 'actionDelete',
'dataType' => Text::NAME,
'label' => '',
],
],
],
],
],
],
],
];
}
}
?>
Then you just create the events.xml file (events.xml) :
File path like MAGENTO_ROOT\app\code\NAMESPACE\MODULE\etc\adminhtml\events.xml
Here is a sample code of the events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_product_save_before">
<observer name="attraction_highlights_save_before" instance="Born\Attractions\Observer\SerializedAttractionHighlights" />
</event>
</config>
Then you just create the observer file (SerializedAttractionHighlights.php) :
File path like MAGENTO_ROOT\app\code\NAMESPACE\MODULE\Observer/SerializedAttractionHighlights.php
Here is a sample code of the SerializedAttractionHighlights.php
<?php
/**
* Copyright © 2017 BORN . All rights reserved.
*/
namespace Born\Attractions\Observer;
use \Magento\Framework\Event\Observer;
use \Magento\Framework\Event\ObserverInterface;
class SerializedAttractionHighlights implements ObserverInterface
{
const ATTR_ATTRACTION_HIGHLIGHTS_CODE = 'attraction_highlights';
/**
* @var \Magento\Framework\App\RequestInterface
*/
protected $request;
/**
* Constructor
*/
public function __construct(
\Magento\Framework\App\RequestInterface $request
)
{
$this->request = $request;
}
public function execute(Observer $observer)
{
/** @var $product \Magento\Catalog\Model\Product */
$product = $observer->getEvent()->getDataObject();
$post = $this->request->getPost();
$post = $post['product'];
$highlights = isset($post[self::ATTR_ATTRACTION_HIGHLIGHTS_CODE]) ? $post[self::ATTR_ATTRACTION_HIGHLIGHTS_CODE] : '';
$product -> setAttractionHighlights($highlights);
$requiredParams = ['title','description'];
if (is_array($highlights)) {
$highlights = $this -> removeEmptyArray($highlights, $requiredParams);
$product -> setAttractionHighlights(json_encode($highlights));
}
}
/**
* Function to remove empty array from the multi dimensional array
*
* @return Array
*/
private function removeEmptyArray($attractionData, $requiredParams){
$requiredParams = array_combine($requiredParams, $requiredParams);
$reqCount = count($requiredParams);
foreach ($attractionData as $key => $values) {
$values = array_filter($values);
$inersectCount = count(array_intersect_key($values, $requiredParams));
if ($reqCount != $inersectCount) {
unset($attractionData[$key]);
}
}
return $attractionData;
}
}
Magento2 Row Pattern Reference Link → http://devdocs.magento.com/guides/v2.2/pattern-library/getting-user-input/row_pattern/row_pattern.html
@raheem.unr note to your answer:
/etc/di.xml is not the correct file to put the modifiers, you should put it in /etc/adminhtml/di.xml else it wont work. (Magento 2.2.*)