2

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?

asked Oct 2, 2020 at 19:47
2
  • is it possible to put more code? more of the uicomponents would be helpful.. Commented Oct 3, 2020 at 8:13
  • In Ui Component there is only a single DynamicRow field/container, I have updated my question. Commented Oct 3, 2020 at 10:42

2 Answers 2

4

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;
 }
}
answered Oct 3, 2020 at 19:52
6
  • Thank you very much for your time! I think I can manage such output. I am waiting for your detailed answer. Commented Oct 3, 2020 at 22:04
  • Wow, amazing logic, thanks a ton mate. You said that Joseph answer is more elegant, is this possible to elaborate his answer? Once again, thank you very much for your precious time. Commented Oct 4, 2020 at 11:24
  • 1
    if you use josepth's code and directly use the interface \Mbs\Calculator\Api\Data\ParsedDataItemInterface, you'll get the same result as mine.. But be careful, the interfaces annotations need updating and that is a big part of this work.. can you accept btw, aby little point counts Commented Oct 4, 2020 at 11:29
  • Thanks once again @Herve, I really appreciate your time and efforts. Commented Oct 4, 2020 at 11:36
  • 1
    I am sure you understand we all like good questions.. this was really interesting even though I had a few sweats for it. thank you indeed Commented Oct 4, 2020 at 12:03
3

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.

answered Oct 3, 2020 at 20:38
1
  • Do you mean mapper classes? Like I have to map my output to another class? The factory class is usually used to fetch data from the database/table. How I will be able to map such a class over my output? I shall be very thankful if you can further elaborate on the class. Commented Oct 3, 2020 at 22:08

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.