DynamicRows (lub Associative Array) w Magento 2 REST API

Oct 02 2020

Używam Magento 2 REST API do aplikacji mobilnej. Stoję w obliczu złożoności, więc potrzebuję twojej pomocy.

Mam komponent DynamicRows z dwoma polami w formularzu UI, nazwa pola to lokalizacja .

<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>

Dane wyjściowe DynamicRows znajdują się poniżej

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
        )
)

W moim interfejsie zadeklarowałem metodę getLocalization ()

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

Następnie wdrożyłem metodę

/**
 * @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;
}

W powyższej metodzie udoskonaliłem dane wyjściowe tablicy DynamicRows zgodnie z wymaganiami.

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

Kiedy trafiam w punkt końcowy interfejsu API REST, otrzymuję następującą odpowiedź.

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

Podczas gdy oczekuję następującej odpowiedzi

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

Przeszukałem różne artykuły, ale nie mogłem znaleźć najlepszej odpowiedzi, która spełniłaby moje wymagania.

Pytanie: Jak uzyskam oczekiwany wynik, pamiętając o tym, że dane DynamicRows nie zawsze będą takie same, czy inny rekord może mieć więcej niż dwa pola z różnymi etykietami_opcji i wartością_opcji ?

Odpowiedzi

4 HerveTribouilloy Oct 03 2020 at 19:52

Udało mi się dojść do następujących rzeczy:

Myślę, że będziesz miał trudności, aby uzyskać więcej z tego. Zasadniczo klucze pozycji pochodzą z podstawowej metody analizowania Magento, dzięki czemu wynik jest dostarczany jako plik XML.

Mogę szczegółowo opisać moje kroki, ale oznacza to 2 dodatkowe interfejsy i trochę analizy. Daj mi znać, jeśli to wyjście jest czymś, z czym mógłbyś pójść na kompromis.

Odpowiedź Józefa jest prostsza niż moja (bardziej elegancka). Ale poniżej jest rdzeń mojego rozwiązania. Umieściłem również link do repozytorium bitbucket, abyś mógł sam wypróbować.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;
    }
}
2 JosephatSwiftOtter Oct 03 2020 at 20:38

Aby uzyskać pożądany efekt, musisz utworzyć więcej klas.

W takim przypadku getLocalizationnależy zwrócić instancję klasy, na przykład:

/**
 * @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']
        ]);
    });
}

Gdzie $this->localizationFactoryjest: \MyCompany\MyModule\Model\LocalizationFactory.

LocalizationFactory powinien mieć dwie metody:

  • getLabel
  • getValue

Może się to wydawać rozwlekłe i tak jest. Jednak ta trasa zapewnia 100% kontrolę nad wyjściem. Testowalność to bonus.