Maneira fácil de adicionar um conjunto de campos com campos ao formulário de interface do usuário

Publicados: 2016-08-23

Neste artigo, vamos criar um módulo simples que adicionará um fieldset com campos no formulário de interface do usuário de edição do produto. Além disso, criaremos um observador para interceptar esses dados durante o salvamento do produto.

Primeiro, precisamos criar um módulo Vendor_Product:

1. Crie um aplicativo/código/fornecedor/produto de diretório
2. Crie um arquivo de registro app/code/Vendor/Product/registration.php com o seguinte conteúdo:

 <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Produto_fornecedor',
        __DIR__
    );
    ?>

Crie um arquivo de compositor (se você planeja transferir o módulo) app/code/Vendor/Module/composer.json :

 {
        "name": "fornecedor/produto-módulo",
        "descrição": "N/A",
        "type": "magento2-module",
        "versão": "1.0.0",
        "licença": [
            "OSL-3.0",
            "AFL-3.0"
        ],
        "carregar automaticamente": {
            "arquivos": [
                "registro.php"
            ],
            "psr-4": {
                "Fornecedor\\Produto\\": ""
            }
        }
    }

Agora, crie o arquivo XML principal do módulo app/code/Vendor/Product/etc/module.xml com a dependência do módulo Magento_Catalog porque nossa janela modal será adicionada ao seu formulário:

 <?xml version="1.0"?>
    <config xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
        <module name="Vendor_Product" setup_version="1.0.0">
            <sequência>
                <module name="Magento_Catalog"/>
            </sequência>
        </module>
    </config>

Habilite o módulo digitando o seguinte: bin/magento module:enable Vendor_Product e bin/magento setup:upgrade no diretório raiz do Magento.

Em seguida, adicione o conteúdo do módulo: os metadados do formulário de interface do usuário e o tipo virtual para sua adição.

Crie um arquivo app/code/Vendor/Product/etc/adminhtml/di.xml. Vamos colocar um modificador dentro:

 <?xml version="1.0"?>
<config xmlns:xsi="https://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">
        <argumentos>
            <argument name="modifiers" xsi:type="array">
                <item name="custom-fieldset" xsi:type="array">
                    <item name="class" xsi:type="string">Fornecedor\Product\Ui\DataProvider\Product\Form\Modifier\CustomFieldset</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                </item>
            </argumento>
        </argumentos>
    </virtualType>
</config>

O modificador é responsável pela adição de dados e algumas manipulações com elementos e componentes de formulário de interface do usuário. Existem 2 métodos principais que vieram da interface do modificador (eles devem sempre estar presentes):

 <?php
    /**
     * Direitos autorais 2016 Magento. Todos os direitos reservados.
     * Consulte COPYING.txt para obter detalhes da licença.
     */
    namespace Magento\Ui\DataProvider\Modifier;
    
    /**
     * Interface do Modificador de Classe
     */
    interface ModifierInterface
    {
        /**
         * @param array $data
         * @return array
         */
        função pública modifyData(array $data);
    
        /**
         * @param array $meta
         * @return array
         */
        função pública modifiqueMeta(array $meta);
    }
    ?>

Vamos usar o método modifyMeta neste exemplo. O método modifyData será explicado no próximo artigo.

Agora, crie o arquivo modificador (app/code/Vendor/Product/Ui/DataProvider/Product/Form/Modifier/CustomFieldset.php) com um fieldset personalizado para a página de edição do produto e preencha-o com os campos:

 <?php
namespace Vendor\Product\Ui\DataProvider\Product\Form\Modifier;

use Magento\Catalog\Model\Locator\LocatorInterface;
use Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
use Magento\Framework\Stdlib\ArrayManager;
use Magento\Framework\UrlInterface;
use Magento\Ui\Component\Container;
use Magento\Ui\Component\Form\Fieldset;
use Magento\Ui\Component\Form\Element\DataType\Number;
use Magento\Ui\Component\Form\Element\DataType\Text;
use Magento\Ui\Component\Form\Element\Input;
use Magento\Ui\Component\Form\Element\Select;
use Magento\Ui\Component\Form\Element\MultiSelect;
use Magento\Ui\Component\Form\Field;

classe CustomFieldset estende AbstractModifier
{

    // Índices de componentes
    const CUSTOM_FIELDSET_INDEX = 'custom_fieldset';
    const CUSTOM_FIELDSET_CONTENT = 'custom_fieldset_content';
    const CONTAINER_HEADER_NAME = 'custom_fieldset_content_header';

    // Nomes dos campos
    const FIELD_NAME_TEXT = 'example_text_field';
    const FIELD_NAME_SELECT = 'example_select_field';
    const FIELD_NAME_MULTISELECT = 'example_multiselect_field';

    /**
     * @var \Magento\Catalog\Model\Locator\LocatorInterface
     */
    $localizador protegido;

    /**
     * @var ArrayManager
     */
    $arrayManager protegido;

    /**
     * @var URLInterface
     */
    protegido $urlBuilder;

    /**
     * @var array
     */
    protegido $meta = [];

    /**
     * @param LocatorInterface $locator
     * @param ArrayManager $arrayManager
     * @param UrlInterface $urlBuilder
     */
    função pública __construct(
        LocatorInterface $ localizador,
        Gerenciador de Matriz $ Gerenciador de Matriz,
        UrlInterface $urlBuilder
    ) {
        $this->localizador = $localizador;
        $this->arrayManager = $arrayManager;
        $this->urlBuilder = $urlBuilder;
    }

    /**
     * Modificador de dados, não faz nada em nosso exemplo.
     *
     * @param array $data
     * @return array
     */
    função pública modifyData(array $data)
    {
        retorna $dados;
    }

    /**
     * Modificador de metadados: adiciona nosso fieldset
     *
     * @param array $meta
     * @return array
     */
    função pública modifiqueMeta(array $meta)
    {
        $this->meta = $meta;
        $this->addCustomFieldset();

        return $this->meta;
    }

    /**
     * Mescle metadados existentes com nossos metadados (não os substitua!)
     *
     * @return nulo
     */
    função protegida addCustomFieldset()
    {
        $this->meta = array_merge_recursive(
            $this->meta,
            [
                static::CUSTOM_FIELDSET_INDEX => $this->getFieldsetConfig(),
            ]
        );
    }

    /**
     * Declare nossa configuração de fieldset
     *
     * @return array
     */
    função protegida getFieldsetConfig()
    {
        Retorna [
            'argumentos' => [
                'dados' => [
                    'configurar' => [
                        'label' => __('Título do conjunto de campos'),
                        'componentType' => Fieldset::NAME,
                        'dataScope' => static::DATA_SCOPE_PRODUCT, // salva os dados nos dados do produto
                        'provedor' => static::DATA_SCOPE_PRODUCT . '_fonte de dados',
                        'ns' => static::FORM_NAME,
                        'desmontável' => verdadeiro,
                        'sortOrder' => 10,
                        'aberto' => verdadeiro,
                    ],
                ],
            ],
            'crianças' => [
                static::CONTAINER_HEADER_NAME => $this->getHeaderContainerConfig(10),
                static::FIELD_NAME_TEXT => $this->getTextFieldConfig(20),
                static::FIELD_NAME_SELECT => $this->getSelectFieldConfig(30),
                static::FIELD_NAME_MULTISELECT => $this->getMultiSelectFieldConfig(40),
            ],
        ];
    }

    /**
     * Obter configuração para contêiner de cabeçalho
     *
     * @param int $sortOrder
     * @return array
     */
    função protegida getHeaderContainerConfig($sortOrder)
    {
        Retorna [
            'argumentos' => [
                'dados' => [
                    'configurar' => [
                        'rótulo' => null,
                        'formElement' => Container::NAME,
                        'componentType' => Container::NAME,
                        'template' => 'ui/form/components/complex',
                        'sortOrder' => $sortOrder,
                        'content' => __('Você pode escrever qualquer texto aqui'),
                    ],
                ],
            ],
            'crianças' => [],
        ];
    }

    /**
     * Exemplo de configuração de campo de texto
     *
     * @param $sortOrder
     * @return array
     */
    função protegida getTextFieldConfig($sortOrder)
    {
        Retorna [
            'argumentos' => [
                'dados' => [
                    'configurar' => [
                        'label' => __('Exemplo de campo de texto'),
                        'formElement' => Campo::NOME,
                        'componentType' => Entrada::NOME,
                        'dataScope' => static::FIELD_NAME_TEXT,
                        'dataType' => Número::NOME,
                        'sortOrder' => $sortOrder,
                    ],
                ],
            ],
        ];
    }

    /**
     * Exemplo de configuração de campo de seleção
     *
     * @param $sortOrder
     * @return array
     */
    função protegida getSelectFieldConfig($sortOrder)
    {
        Retorna [
            'argumentos' => [
                'dados' => [
                    'configurar' => [
                        'label' => __('Selecionar opções'),
                        'componentType' => Campo::NOME,
                        'formElement' => Selecione::NOME,
                        'dataScope' => static::FIELD_NAME_SELECT,
                        'dataType' => Texto::NOME,
                        'sortOrder' => $sortOrder,
                        'options' => $this->_getOptions(),
                        'visível' => verdadeiro,
                        'desativado' => falso,
                    ],
                ],
            ],
        ];
    }

    /**
     * Exemplo de configuração de campo de seleção múltipla
     *
     * @param $sortOrder
     * @return array
     */
    função protegida getMultiSelectFieldConfig($sortOrder)
    {
        Retorna [
            'argumentos' => [
                'dados' => [
                    'configurar' => [
                        'label' => __('Opções de seleção múltipla'),
                        'componentType' => Campo::NOME,
                        'formElement' => MultiSelect::NAME,
                        'dataScope' => static::FIELD_NAME_MULTISELECT,
                        'dataType' => Texto::NOME,
                        'sortOrder' => $sortOrder,
                        'options' => $this->_getOptions(),
                        'visível' => verdadeiro,
                        'desativado' => falso,
                    ],
                ],
            ],
        ];
    }

    /**
     * Obtenha opções de exemplo como uma matriz de opções:
     * [
     * etiqueta => string,
     * valor => option_id
     * ]
     *
     * @return array
     */
    função protegida _getOptions()
    {
        $opções = [
            1 => [
                'label' => __('Opção 1'),
                'valor' => 1
            ],
            2 => [
                'label' => __('Opção 2'),
                'valor' => 2
            ],
            3 => [
                'label' => __('Opção 3'),
                'valor' => 3
            ],
        ];

        retornar $ opções;
    }
}
?>

Neste exemplo, precisamos pegar os metadados da forma de interface do usuário existentes e mesclá-los (não reescrever!) com nossos novos dados:

 <?php
/**
 * Mescle metadados existentes com nossos metadados (não os substitua!)
 *
 * @return nulo
 */
função protegida addCustomFieldset()
{
    $this->meta = array_merge_recursive(
        $this->meta,
        [
            static::CUSTOM_FIELDSET_INDEX => $this->getFieldsetConfig(),
        ]
    );
}
?>

Quando terminar, adicione o novo fieldset ao método getFieldsetConfig:

 <?php
/**
 * Declare nossa configuração de fieldset
 *
 * @return array
 */
função protegida getFieldsetConfig()
{
    Retorna [
        'argumentos' => [
            'dados' => [
                'configurar' => [
                    'label' => __('Título do conjunto de campos'),
                    'componentType' => Fieldset::NAME,
                    'dataScope' => static::DATA_SCOPE_PRODUCT, // salva os dados nos dados do produto
                    'provedor' => static::DATA_SCOPE_PRODUCT . '_fonte de dados',
                    'ns' => static::FORM_NAME,
                    'desmontável' => verdadeiro,
                    'sortOrder' => 10,
                    'aberto' => verdadeiro,
                ],
            ],
        ],
        'crianças' => [
            static::CONTAINER_HEADER_NAME => $this->getHeaderContainerConfig(10),
            static::FIELD_NAME_TEXT => $this->getTextFieldConfig(20),
            static::FIELD_NAME_SELECT => $this->getSelectFieldConfig(30),
            static::FIELD_NAME_MULTISELECT => $this->getMultiSelectFieldConfig(40),
        ],
    ];
}
?>

Herdamos do modificador de formulário de interface do usuário do produto abstrato e usamos seu namespace e dados como um provedor: 'provider' => static::DATA_SCOPE_PRODUCT . '_data_source' (onde DATA_SCOPE_PRODUCT é a linha 'data.product').

A opção componentType é uma das principais opções e é responsável pelo tipo de componente. A opção recolhível é responsável por recolher e expandir nosso conjunto de campos. E a opção open define se o fieldset será aberto por padrão durante o desenho do formulário.

Então, consequentemente, adicionamos um cabeçalho ao nosso fieldset no método getHeaderContainerConfig e 3 exemplos de campos: text, select e multiselect. No entanto, nosso produto e formulário não receberão dados até que os adicionemos ao método modifyData. Mas temos a capacidade de transmitir e interceptar os dados durante o salvamento.

Vamos ver como fica o formulário:

O salvamento de dados ocorre dentro do arquivo do controlador do produto vendor/magento/module-catalog/Controller/Adminhtml/Product/Save.php no método execute principal. Se tudo foi feito da maneira correta, nossos dados serão exibidos corretamente nos dados de entrada deste método:

Observe que, se o seu produto não tiver esses atributos desde o início, você deve salvá-los manualmente. Você pode fazer isso no observador.

Primeiro, declare-o no arquivo app/code/Vendor/Product/etc/adminhtml/events.xml (estamos usando o escopo adminhtml porque o formulário não existe no front-end):

 <?xml version="1.0"?>
<config xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="save_example_data" instance="Fornecedor\Produto\Observador\ProdutoSaveAfter" />
    </event>
</config>

Em seguida, crie a classe do observador que apontamos no atributo de instância – app/code/Vendor/Product/Observer/ProductSaveAfter.php:

 <?php
namespace Fornecedor\Produto\Observador;

use \Magento\Framework\Event\ObserverInterface;
use \Magento\Framework\Event\Observer como EventObserver;
use Vendor\Product\Ui\DataProvider\Product\Form\Modifier\CustomFieldset;

classe ProductSaveAfter implementa ObserverInterface
{

    /**
     * @param EventObserver $observer
     */
    função pública execute(\Magento\Framework\Event\Observer $observer)
    {
        /** @var \Magento\Catalog\Model\Product $product */
        $produto = $observador->getEvent()->getProduct();
        if (!$produto) {
            Retorna;
        }

        $exampleTextField = $product->getData(CustomFieldset::FIELD_NAME_TEXT);
        $exampleSelectField = $product->getData(CustomFieldset::FIELD_NAME_SELECT);
        $exampleMultiSelectField = $product->getData(CustomFieldset::FIELD_NAME_MULTISELECT);

        // Manipula os dados aqui
    }
}
?>

Os dados no observador:

Agora, você pode chamar seu próprio modelo do observador e salvar dados nele ou modificá-lo como desejar.

Tome cuidado! Se o salvamento do seu modelo estiver conectado ao salvamento do produto, isso pode levar à recursividade.