Question

I am using Magento 2 REST API for the mobile application. I am facing some complexity so I need your help.

I have DynamicRows component with two fields in UI Form, the name of the field is localization.

<container name="localization" sortOrder="9" >
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/dynamic-rows/dynamic-rows</item>
            <item name="template" xsi:type="string">ui/dynamic-rows/templates/default</item>
            <item name="componentType" xsi:type="string">dynamicRows</item>
            <item name="label" xsi:type="string" translate="true">Data</item>
            <item name="recordTemplate" xsi:type="string">record</item>
            <item name="addButtonLabel" xsi:type="string">Add Parameter</item>
            <item name="deleteProperty" xsi:type="boolean">false</item>
            <item name="enabled" xsi:type="boolean">false</item>
        </item>
    </argument>

    <container name="record">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true" />
                <item name="component" xsi:type="string" translate="true">Magento_Ui/js/dynamic-rows/record</item>
                <item name="isTemplate" xsi:type="boolean">true</item>
                <item name="is_collection" xsi:type="boolean">true</item>
                <item name="showFallbackReset" xsi:type="boolean">false</item>
            </item>
        </argument>

        <field name="option_label">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Parameter</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="dataScope" xsi:type="string">option_label</item>
                    <item name="showFallbackReset" xsi:type="boolean">false</item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                    <item name="sortOrder" xsi:type="string">10</item>
                </item>
            </argument>
        </field>

        <field name="option_value">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="label" xsi:type="string" translate="true">Value</item>
                    <item name="formElement" xsi:type="string">input</item>
                    <item name="dataScope" xsi:type="string">option_value</item>
                    <item name="showFallbackReset" xsi:type="boolean">false</item>
                    <item name="additionalClasses" xsi:type="array">
                        <item name="cirklestudio_color_picker" xsi:type="boolean">true</item>
                    </item>
                    <item name="validation" xsi:type="array">
                        <item name="required-entry" xsi:type="boolean">true</item>
                    </item>
                    <item name="sortOrder" xsi:type="string">20</item>
                </item>
            </argument>
        </field>

        <actionDelete>
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="componentType" xsi:type="string">actionDelete</item>
                    <item name="dataType" xsi:type="string">text</item>
                    <item name="fit" xsi:type="boolean">false</item>
                    <item name="label" xsi:type="string" />
                    <item name="additionalClasses" xsi:type="string">data-grid-actions-cell</item>
                    <item name="template" xsi:type="string">Magento_Backend/dynamic-rows/cells/action-delete</item>
                </item>
            </argument>
        </actionDelete>
        
    </container>

</container>

The output of the DynamicRows is below

Array
(
    [0] => Array
        (
            [row_id] => 
            [option_label] => tmp_dir
            [option_value] => temp
            [position] => 1
            [record_id] => 0
            [initialize] => true
        )

    [1] => Array
        (
            [record_id] => 1
            [row_id] => 
            [option_label] => main_dir
            [option_value] => APD
            [position] => 2
            [initialize] => true
        )
)

I have declared a method getLocalization() in my Interface

/**
 * Return localized parameters for the application
 *
 * @return mixed[]
 */
public function getLocalization();

Then I have implemented the method

/**
 * @inheritdoc
 */
public function getLocalization()
{
    // Unserialize array
    $data = $this->_getData('localization') ? unserialize($this->_getData('localization')) : "";


    $output = [];
    foreach ($data as $param) {
        $output[$param['option_label']] =  $param['option_value'];
    }

    return $output;
}

In the above method, I have refined the DynamicRows array output as per the requirements.

Array
(
    [tmp_dir] => temp
    [main_dir] => APD
)

When I hit the REST API endpoint, I get the following response.

<localization>
    <item>temp</item>
    <item>APD</item>
</localization>

Whereas I expect the following response

<localization>
    <tmp_dir>temp</tmp_dir>
    <main_dir>APD</main_dir>
</localization>

I have searched around different articles but couldn't find the best answer to suit my requirements.

Question: How I will get my expected result keeping this thing in mind that DynamicRows data will not be always the same, might be another record has more than two fields with different option_label and option_value?

Was it helpful?

Solution

I have managed to come up to the following:

enter image description here

I think you will struggle to get more from it. Basically, the item keys do come from Magento core parsing method so that your result comes to you as an xml.

I can detail my steps but it does amount to 2 more interfaces and a bit of parsing.. Do let me know if this output is something you could compromise with.

Joseph's answer is simpler than mine (more elegant). But below is the core from my solution. I also put a link to the bitbucket repository for you to try out by yourself. https://bitbucket.org/magstaging/apiparser/src/master/

    $output = [];
            foreach ($data as $param) {
                $output[$param['option_label']] =  $param['option_value'];
            }
    
            return $this->parsedDataInterfaceFactory->create([
                'itemsData' => $output
            ]);


class ParsedResult implements ParsedDataInterface
{
    /**
     * @var \Mbs\Calculator\Api\Data\ParsedDataItemInterface[]
     */
    private $items = [];

    /**
     * OperationResult constructor.
     * @param array $itemsData
     * @param \Mbs\Calculator\Api\Data\ParsedDataItemInterfaceFactory $dataItemInterfaceFactory
     */
    public function __construct(
        $itemsData,
        \Mbs\Calculator\Api\Data\ParsedDataItemInterfaceFactory $dataItemInterfaceFactory
    ) {
        foreach ($itemsData as $itemLabel => $itemValue) {
            $this->items[] = $dataItemInterfaceFactory->create([
                'label' => $itemLabel,
                'value' => $itemValue
            ]);
        }
    }

    /**
     * @return \Mbs\Calculator\Api\Data\ParsedDataItemInterface[]
     */
    public function getLocalization()
    {
        return $this->items;
    }
}

class Item implements ParsedDataItemInterface
{
    /**
     * @var string
     */
    private $label;

    /**
     * @var string
     */
    private $value;

    /**
     * @inheritdoc
     */
    public function __construct(
        string $label,
        string $value
    ) {
        $this->label = $label;
        $this->value = $value;
    }

    /**
     * @inheritdoc
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * @inheritdoc
     */
    public function getValue()
    {
        return $this->value;
    }
}

OTHER TIPS

To get the desired result, you need to create more classes.

In this case, getLocalization should return an instance of a class, like:

/**
 * @return \MyCompany\MyModule\Model\Localization[]
 **/
public function getLocalization(): array
{
    // Unserialize array
    $data = $this->_getData('localization') ? unserialize($this->_getData('localization')) : "";

    return array_map(function($option) {
        return $this->localizationFactory->create([
            'label' => $param['option_label'],
            'value' => $param['option_value']
        ]);
    });
}

Where $this->localizationFactory is: \MyCompany\MyModule\Model\LocalizationFactory.

LocalizationFactory should have two methods:

  • getLabel
  • getValue

This might seem verbose, and it is. However, this route gives you 100% control over the output. Testability is a bonus.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top