0

On the Product edit form in the Admin area, I have added a multiselect field by overwriting the modifyMeta() method on Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier as so:

<?php
namespace Dotancohen\Foobar\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Element\Select;
use Magento\Ui\Component\Form\Fieldset;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Form\Element\MultiSelect;

class Features extends AbstractModifier {

    public function modifyMeta(array $meta)
    {
        $meta_extend = [
            'dc-product-foobar' => [
                'arguments' => [
                    'data' => [
                        'config' => [
                            'label' => __('Foobar'),
                            'collapsible' => true,
                            'componentType' => Fieldset::NAME,
                            'dataScope' => 'data.dc-product-foobar',
                            'sortOrder' => 10,
                        ],
                    ],
                ],
                'children' => [
                    'testmultiselect' => [
                        'arguments' => [
                            'data' => [
                                'config' => [
                                    'label'         => __('Multiselect Element'),
                                    'componentType' => Field::NAME,
                                    'formElement'   => MultiSelect::NAME,
                                    'dataScope'     => 'testmultiselect',
                                    'dataType'      => Text::NAME,
                                    'sortOrder'     => 10,
                                    'options'       => [
                                        ['value' => '0', 'label' => __('One')],
                                        ['value' => '1', 'label' => __('Two')],
                                        ['value' => '2', 'label' => __('Three')],
                                        ['value' => '3', 'label' => __('Four')],
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ];

        $meta = array_replace_recursive($meta, $meta_extend);
        return $meta;
    }

}

This code does properly add the Multiselect field. How can I now set specific values as being selected? The data is coming from an outside process, not the database, so I need to set the values here in PHP.

I've tried the obvious adding of elements 'selected' => 'selected' and 'selected' => true to the relevant values' arrays, like so:

'options'       => [
    ['value' => '0', 'label' => __('One'), 'selected' => 'selected'],
    ['value' => '1', 'label' => __('Two'), 'selected' => true],
    ['value' => '2', 'label' => __('Three')],
    ['value' => '3', 'label' => __('Four')],
],

Neither approach worked. Then I tried to add some JavaScript that might select the elements, as suggested here:

// Javascript on the fieldset itself
'config' => [
    'after_element_html' => '<script>require([], function(){console.log("Hello, fieldset!");});</script>',
    'label' => __('Features'),
    'collapsible' => true,
    'componentType' => Fieldset::NAME,
    'dataScope' => 'data.dc-product-foobar',
    'sortOrder' => 10,
],

// Javascript on the Multiselect element
'config' => [
    'after_element_html' => '<script>require([], function(){console.log("Hello, Multiselect!");});</script>',
    'label'         => __('Multiselect Element'),
    'componentType' => Field::NAME,
    'formElement'   => MultiSelect::NAME,
    'dataScope'     => 'testmultiselect',
    'dataType'      => Text::NAME,
    'sortOrder'     => 10,
    'options'       => [
        ['value' => '0', 'label' => __('One')],
        ['value' => '1', 'label' => __('Two')],
        ['value' => '2', 'label' => __('Three')],
        ['value' => '3', 'label' => __('Four')],
    ],
],

However the JavaScript is not being invoked. I do see that that the two after_element_html values are appearing in some escaped JSON string in the HTML, but the <script> tag itself is not in the DOM.

How can I either assert that specific options' values are to be selected in the PHP array, or how can I get that JavaScript invoked so that I could select them using JavaScript?

Edit: Here is the complete code file:

<?php
namespace DotanCohen\Foobar\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\MultiSelect;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Form\Fieldset;

class Features extends AbstractModifier {

    const FIELDSET_NAME = 'so-test-fieldset';
    const FIELD_NAME = 'testmultiselect';

    protected $_locator;


    public function __construct(LocatorInterface $locator)
    {
        $this->_locator = $locator;
    }


    public function modifyData(array $data)
    {
        $product = $this->_locator->getProduct();
        $additionalData = [
            self::FIELD_NAME => ["0", "2"]
        ];

        $productId = $product->getId() ?? null;
        $data[$productId] = $data[$productId] ?? [];
        $data[$productId]['product'] = $data[$productId]['product'] ?? [];
        $data[$productId]['product'] = array_merge($data[$productId]['product'], $additionalData);

        return $data;
    }


    public function modifyMeta(array $meta)
    {
        $meta_extend = [
            self::FIELDSET_NAME => [
                'arguments' => [
                    'data' => [
                        'config' => [
                            'label' => __('Features'),
                            'collapsible' => true,
                            'componentType' => Fieldset::NAME,
                            'dataScope' => 'data.'.self::FIELDSET_NAME,
                            'sortOrder' => 10,
                        ],
                    ],
                ],
                'children' => [
                    self::FIELD_NAME => [
                        'arguments' => [
                            'data' => [
                                'config' => [
                                    'label'         => __('Multiselect from PHP'),
                                    'componentType' => Field::NAME,
                                    'formElement'   => MultiSelect::NAME,
                                    'dataScope'     => self::FIELD_NAME,
                                    'dataType'      => Text::NAME,
                                    'sortOrder'     => 30,
                                    'options'       => [
                                        ['value' => '0', 'label' => __('One')],
                                        ['value' => '1', 'label' => __('Two')],
                                        ['value' => '2', 'label' => __('Three')],
                                        ['value' => '3', 'label' => __('Four')],
                                    ],
                                ],
                            ],
                        ],
                    ]
                ],
            ],
        ];

        $meta = array_replace_recursive($meta, $meta_extend);
        return $meta;
    }


}

And here is the relevant etc/adminhtml/di.xml section:

    <!-- Override magento/module-catalog/Ui/DataProvider/Product/Form/Modifier/CustomOptions.php to filter Customizable Options -->
    <type name="Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions">
        <plugin name="dotancohen_foobar_custom_options" type="DotanCohen\Foobar\Plugin\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions" sortOrder="1" />
    </type>

    <!-- CustomOptions.php: DI on constructor -->
    <type name="DotanCohen\Foobar\Plugin\Catalog\Ui\DataProvider\Product\Form\Modifier\CustomOptions">
        <arguments>
            <argument name="locator" xsi:type="object">Magento\Catalog\Model\Locator\LocatorInterface</argument>
        </arguments>
    </type>
1
  • You don't need to specify locator argument in di for this case. You need to specify any arguments only if you want to specify specific class which is implement LocatorInterface which is different from defined in preference. Othercase is just overhead. Commented Aug 7, 2022 at 19:09

1 Answer 1

2

I'm sure that you've provided not full code, because the provided code will not work

The abstract class \Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier implements \Magento\Ui\DataProvider\Modifier\ModifierInterface and you should implement 2 methods modifyData and modifyMeta.

The method modifyMeta updates metadata for product edit form

The method modifyData updates values for product data which will be used for render product form data.

In your metadata you are specified scope as testmultiselect and in modifyData method you should to be sure you are specified the correct values for your data.

How it should be (example)

app/code/Acme/StackExchange/Ui/DataProvider/Product/Form/Modifier/Features.php

[Updated]

<?php
declare(strict_types=1);

namespace Acme\StackExchange\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Ui\Component\Form\Element\MultiSelect;
use Magento\Ui\Component\Form\Field;
use Magento\Ui\Component\Form\Fieldset;

class Features extends AbstractModifier
{
    protected const FIELDSET_NAME = 'so-test-fieldset';
    protected const FIELD_NAME    = 'testmultiselect';

    protected LocatorInterface $locator;

    public function __construct(LocatorInterface $locator)
    {
        $this->locator = $locator;
    }

    /**
     * @inheritDoc
     */
    public function modifyData(array $data): array
    {
        $product = $this->locator->getProduct();

        $additionalData = [];
        if (!$product->getId()) {
        // some logic for default values if needed
        $additionalData[self::FIELD_NAME] = '0,2';
        } else {
            // some logic to retrieve product data
            $additionalData[self::FIELD_NAME] = $product->getData(self::FIELD_NAME);
        }

        $productId                   = $product->getId() ?? null;
        $data[$productId]            = $data[$productId] ?? [];
        $data[$productId]['product'] = $data[$productId]['product'] ?? [];
        $data[$productId]['product'] = array_merge($data[$productId]['product'], $additionalData);

        return $data;
    }

    /**
     * @inheritDoc
     */
    public function modifyMeta(array $meta): array
    {
        $meta[self::FIELDSET_NAME] = [
            'arguments' => [
                'data' => [
                    'config' => [
                        'componentType' => Fieldset::NAME,
                        'label'         => __('Features'),
                        'dataScope'     => 'data.product',
                        'collapsible'   => true,
                        'opened'        => false,
                        'sortOrder' => 10,
                    ],
                ],
            ],
            'children'  => [
                'container_' . self::FIELD_NAME => [
                    'arguments' => [
                        'data' => [
                            'config' => [
                                'formElement'   => 'container',
                                'componentType' => 'container',
                                'breakLine'     => false,
                                'label'         => __('Multiselect from PHP'),
                                'required'      => false,
                            ],
                        ],
                    ],
                    'children'  => [
                        self::FIELD_NAME => [
                            'arguments' => [
                                'data' => [
                                    'config' => [
                                        'dataType'      => MultiSelect::NAME,
                                        'formElement'   => MultiSelect::NAME,
                                        'visible'       => 1,
                                        'required'      => 0,
                                        'label'         => __('Multiselect from PHP'),
                                        'componentType' => Field::NAME,
                                        'code'          => self::FIELD_NAME,
                                        'source'        => self::FIELDSET_NAME,
                                        'scopeLabel'    => '[GLOBAL]',
                                        'globalScope'   => true,
                                        'sortOrder'     => 30,
                                        'options'       => [
                                            ['value' => '0', 'label' => __('One')],
                                            ['value' => '1', 'label' => __('Two')],
                                            ['value' => '2', 'label' => __('Three')],
                                            ['value' => '3', 'label' => __('Four')],
                                        ],
                                    ],
                                ],
                            ],
                        ],
                    ],
                ],
            ],
        ];

        return $meta;
    }
}

enter image description here

5
  • You are correct, the code was a simplified example. Perhaps oversimplified. I've tried to use modifyData() as you suggested, thank you. However it does not select the elements. I'll update the code in the OP with running example code to see if we can get to the bottom of this. Thank you.
    – dotancohen
    Commented Aug 7, 2022 at 15:30
  • I've edited the question with the complete code file and di.xml. Thank you for looking into this Victor ))
    – dotancohen
    Commented Aug 7, 2022 at 15:43
  • Do you still have any questions or? It's not clear based on your comment or update. I see that you are always set default value and never read value from product. I guess it's some mistake here. Commented Aug 7, 2022 at 18:07
  • 1
    I've updated code with working example Commented Aug 7, 2022 at 19:02
  • Thank you Victor! Your name is appropriate )) I did have the data format returned in modifyMeta() wrong, additionally as we both found out modifyData() needs to add the field value as a string, not an array. Thank you!
    – dotancohen
    Commented Aug 8, 2022 at 12:54

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.