Простой способ добавить набор полей с полями в UI-форму

Опубликовано: 2016-08-23

В этой статье мы собираемся создать простой модуль, который добавит набор полей в UI-форму редактирования товара. Также мы создадим наблюдатель для перехвата этих данных при сохранении товара.

Во-первых, нам нужно создать модуль Vendor_Product:

1. Создайте каталог app/code/Vendor/Product.
2. Создайте регистрационный файл app/code/Vendor/Product/registration.php со следующим содержимым:

 <?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Продукт_Поставщика',
        __КАТАЛОГ__
    );
    ?>

Создайте файл композитора (если вы планируете перенести модуль) app/code/Vendor/Module/composer.json:

 {
        "name": "поставщик/модуль-продукт",
        "описание": "Н/Д",
        "тип": "magento2-модуль",
        "версия": "1.0.0",
        "лицензия": [
            «ОСЛ-3.0»,
            «АФЛ-3.0»
        ],
        "автозагрузка": {
            "файлы": [
                "регистрация.php"
            ],
            "пср-4": {
                "Поставщик\\Продукт\\": ""
            }
        }
    }

Теперь создадим основной XML-файл модуля app/code/Vendor/Product/etc/module.xml с зависимостью от модуля Magento_Catalog, потому что наше модальное окно будет добавлено в его форму:

 <?xml версия="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">
            <последовательность>
                <имя модуля="Magento_Catalog"/>
            </последовательность>
        </модуль>
    </config>

Включите модуль, введя следующее: bin/magento module:enable Vendor_Product и bin/magento setup:upgrade в корневом каталоге Magento.

Затем добавьте содержимое модуля: метаданные UI-формы и виртуальный тип для ее добавления.

Создайте файл app/code/Vendor/Product/etc/adminhtml/di.xml. Внутри мы поместим модификатор:

 <?xml версия="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">
        <аргументы>
            <argument name="modifiers" xsi:type="array">
                <item name="custom-fieldset" xsi:type="array">
                    <item name="class" xsi:type="string">Поставщик\Продукт\Ui\DataProvider\Product\Form\Modifier\CustomFieldset</item>
                    <item name="sortOrder" xsi:type="number">10</item>
                </item>
            </аргумент>
        </аргументы>
    </ виртуальный тип>
</config>

Модификатор отвечает за добавление данных и некоторые манипуляции с элементами и компонентами UI-формы. Есть 2 основных метода, которые пришли из интерфейса модификатора (они всегда должны присутствовать):

 <?php
    /**
     * Copyright 2016 Magento. Все права защищены.
     * Подробнее о лицензии см. COPYING.txt.
     */
    пространство имен Magento\Ui\DataProvider\Modifier;
    
    /**
     * Интерфейс модификатора класса
     */
    Модификатор интерфейсаИнтерфейс
    {
        /**
         * @param массив $данные
         * @возвратный массив
         */
        публичная функция modifyData(массив $data);
    
        /**
         * массив @param $meta
         * @возвратный массив
         */
        публичная функция modifyMeta(массив $meta);
    }
    ?>

В этом примере мы собираемся использовать метод ModifyMeta. МетодmodifyData будет объяснен в следующей статье.

Теперь создайте файл модификатора (app/code/Vendor/Product/Ui/DataProvider/Product/Form/Modifier/CustomFieldset.php) с пользовательским набором полей для страницы редактирования продукта и заполните его полями:

 <?php
пространство имен Vendor\Product\Ui\DataProvider\Product\Form\Modifier;

используйте Magento\Catalog\Model\Locator\LocatorInterface;
используйте Magento\Catalog\Ui\DataProvider\Product\Form\Modifier\AbstractModifier;
используйте Magento\Framework\Stdlib\ArrayManager;
используйте Magento\Framework\UrlInterface;
используйте Magento\Ui\Component\Container;
используйте Magento\Ui\Component\Form\Fieldset;
используйте Magento\Ui\Component\Form\Element\DataType\Number;
используйте Magento\Ui\Component\Form\Element\DataType\Text;
используйте Magento\Ui\Component\Form\Element\Input;
используйте Magento\Ui\Component\Form\Element\Select;
используйте Magento\Ui\Component\Form\Element\MultiSelect;
используйте Magento\Ui\Component\Form\Field;

класс CustomFieldset расширяет AbstractModifier
{

    // Индексы компонентов
    const CUSTOM_FIELDSET_INDEX = 'custom_fieldset';
    const CUSTOM_FIELDSET_CONTENT = 'custom_fieldset_content';
    const CONTAINER_HEADER_NAME = 'custom_fieldset_content_header';

    // Имена полей
    const FIELD_NAME_TEXT = 'example_text_field';
    const FIELD_NAME_SELECT = 'example_select_field';
    const FIELD_NAME_MULTISELECT = 'example_multiselect_field';

    /**
     * @var \Magento\Каталог\Модель\Локатор\ЛокаторИнтерфейс
     */
    защищенный $локатор;

    /**
     * @var Менеджер массивов
     */
    защищенный $arrayManager;

    /**
     * @var УрлИнтерфейс
     */
    защищенный $urlBuilder;

    /**
     * массив @var
     */
    защищенный $ мета = [];

    /**
     * @param LocatorInterface $locator
     * @param ArrayManager $arrayManager
     * @param UrlInterface $urlBuilder
     */
    публичная функция __construct(
        LocatorInterface $ локатор,
        Менеджер массивов $Менеджер массивов,
        УрлИнтерфейс $urlBuilder
    ) {
        $this->locator = $locator;
        $this->arrayManager = $arrayManager;
        $this->urlBuilder = $urlBuilder;
    }

    /**
     * Модификатор данных, в нашем примере ничего не делает.
     *
     * @param массив $данные
     * @возвратный массив
     */
    публичная функция modifyData(массив $data)
    {
        вернуть $данные;
    }

    /**
     * Модификатор метаданных: добавляет наш набор полей
     *
     * массив @param $meta
     * @возвратный массив
     */
    публичная функция modifyMeta(массив $meta)
    {
        $this->meta = $meta;
        $this->addCustomFieldset();

        вернуть $this->meta;
    }

    /**
     * Объединить существующие метаданные с нашими метаданными (не перезаписывать их!)
     *
     * @возврат недействителен
     */
    защищенная функция addCustomFieldset()
    {
        $this->meta = array_merge_recursive(
            $это->мета,
            [
                static::CUSTOM_FIELDSET_INDEX => $this->getFieldsetConfig(),
            ]
        );
    }

    /**
     * Объявить нашу конфигурацию fieldset
     *
     * @возвратный массив
     */
    защищенная функция getFieldsetConfig()
    {
        возвращаться [
            'аргументы' => [
                'данные' => [
                    'конфигурация' => [
                        'метка' => __('Заголовок набора полей'),
                        'componentType' => Набор полей::ИМЯ,
                        'dataScope' => static::DATA_SCOPE_PRODUCT, // сохраняем данные в данных о товаре
                        'поставщик' => static::DATA_SCOPE_PRODUCT . '_источник данных',
                        'ns' => static::FORM_NAME,
                        'складной' => правда,
                        'сортировка' => 10,
                        'открыто' => правда,
                    ],
                ],
            ],
            'дети' => [
                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),
            ],
        ];
    }

    /**
     * Получить конфигурацию для контейнера заголовков
     *
     * @param int $sortOrder
     * @возвратный массив
     */
    защищенная функция getHeaderContainerConfig($sortOrder)
    {
        возвращаться [
            'аргументы' => [
                'данные' => [
                    'конфигурация' => [
                        'метка' => ноль,
                        'formElement' => Контейнер::ИМЯ,
                        'componentType' => Контейнер::ИМЯ,
                        'template' => 'пользовательский интерфейс/форма/компоненты/комплекс',
                        'сортировка' => $sortOrder,
                        'content' => __('Здесь можно написать любой текст'),
                    ],
                ],
            ],
            'дети' => [],
        ];
    }

    /**
     * Пример конфигурации текстового поля
     *
     * @param $sortOrder
     * @возвратный массив
     */
    защищенная функция getTextFieldConfig($sortOrder)
    {
        возвращаться [
            'аргументы' => [
                'данные' => [
                    'конфигурация' => [
                        'метка' => __('Пример текстового поля'),
                        'formElement' => Поле::ИМЯ,
                        'componentType' => Input::NAME,
                        'dataScope' => static::FIELD_NAME_TEXT,
                        'dataType' => Число::ИМЯ,
                        'сортировка' => $sortOrder,
                    ],
                ],
            ],
        ];
    }

    /**
     * Пример конфигурации поля выбора
     *
     * @param $sortOrder
     * @возвратный массив
     */
    защищенная функция getSelectFieldConfig($sortOrder)
    {
        возвращаться [
            'аргументы' => [
                'данные' => [
                    'конфигурация' => [
                        'метка' => __('Выбор параметров'),
                        'componentType' => Поле::ИМЯ,
                        'formElement' => Выбрать::ИМЯ,
                        'dataScope' => static::FIELD_NAME_SELECT,
                        'dataType' => Текст::ИМЯ,
                        'сортировка' => $sortOrder,
                        'опции' => $this->_getOptions(),
                        'видимый' => правда,
                        'отключено' => ложь,
                    ],
                ],
            ],
        ];
    }

    /**
     * Пример конфигурации поля множественного выбора
     *
     * @param $sortOrder
     * @возвратный массив
     */
    защищенная функция getMultiSelectFieldConfig($sortOrder)
    {
        возвращаться [
            'аргументы' => [
                'данные' => [
                    'конфигурация' => [
                        'label' => __('Множественный выбор опций'),
                        'componentType' => Поле::ИМЯ,
                        'formElement' => MultiSelect::NAME,
                        'dataScope' => static::FIELD_NAME_MULTISELECT,
                        'dataType' => Текст::ИМЯ,
                        'сортировка' => $sortOrder,
                        'опции' => $this->_getOptions(),
                        'видимый' => правда,
                        'отключено' => ложь,
                    ],
                ],
            ],
        ];
    }

    /**
     * Получить примеры опций в виде массива опций:
     * [
     * метка => строка,
     * значение => option_id
     * ]
     *
     * @возвратный массив
     */
    защищенная функция _getOptions()
    {
        $ параметры = [
            1 => [
                'метка' => __('Вариант 1'),
                'значение' => 1
            ],
            2 => [
                'метка' => __('Вариант 2'),
                'значение' => 2
            ],
            3 => [
                'метка' => __('Вариант 3'),
                'значение' => 3
            ],
        ];

        вернуть $options;
    }
}
?>

В этом примере нам нужно взять существующие метаданные UI-формы и объединить их (не переписывать!) с нашими новыми данными:

 <?php
/**
 * Объединить существующие метаданные с нашими метаданными (не перезаписывать их!)
 *
 * @возврат недействителен
 */
защищенная функция addCustomFieldset()
{
    $this->meta = array_merge_recursive(
        $это->мета,
        [
            static::CUSTOM_FIELDSET_INDEX => $this->getFieldsetConfig(),
        ]
    );
}
?>

Когда закончите, добавьте новый набор полей в метод getFieldsetConfig:

 <?php
/**
 * Объявить нашу конфигурацию fieldset
 *
 * @возвратный массив
 */
защищенная функция getFieldsetConfig()
{
    возвращаться [
        'аргументы' => [
            'данные' => [
                'конфигурация' => [
                    'метка' => __('Заголовок набора полей'),
                    'componentType' => Набор полей::ИМЯ,
                    'dataScope' => static::DATA_SCOPE_PRODUCT, // сохраняем данные в данных о товаре
                    'поставщик' => static::DATA_SCOPE_PRODUCT . '_источник данных',
                    'ns' => static::FORM_NAME,
                    'складной' => правда,
                    'сортировка' => 10,
                    'открыто' => правда,
                ],
            ],
        ],
        'дети' => [
            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),
        ],
    ];
}
?>

Мы наследуем модификатор UI-формы абстрактного продукта и используем его пространство имен и данные в качестве провайдера: 'provider' => static::DATA_SCOPE_PRODUCT . «_data_source» (где DATA_SCOPE_PRODUCT — это строка «data.product»).

Параметр componentType является одним из основных и отвечает за тип компонента. Сворачиваемая опция отвечает за свертывание и расширение нашего набора полей. И опция открытия определяет, будет ли набор полей открыт по умолчанию во время рисования формы.

Затем мы последовательно добавляем заголовок к нашему набору полей в методе getHeaderContainerConfig и 3 примера полей: текст, выбор и множественный выбор. Однако наш продукт и форма не будут получать данные, пока мы не добавим их в методmodifyData. Но у нас есть возможность передавать и перехватывать данные во время сохранения.

Посмотрим, как выглядит форма:

Сохранение данных происходит внутри файла контроллера продукта vendor/magento/module-catalog/Controller/Adminhtml/Product/Save.php в основном методе выполнения. Если все сделано правильно, то наши данные будут корректно отображаться во входных данных этого метода:

Обратите внимание: если у вашего продукта изначально не было этих атрибутов, вам следует сохранить их вручную. Это можно сделать в наблюдателе.

Во-первых, объявите его в файле app/code/Vendor/Product/etc/adminhtml/events.xml (мы используем область adminhtml, потому что форма не существует во внешнем интерфейсе):

 <?xml версия="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="Vendor\Product\Observer\ProductSaveAfter" />
    </событие>
</config>

Затем создайте класс наблюдателя, который мы указали в атрибуте экземпляра — app/code/Vendor/Product/Observer/ProductSaveAfter.php:

 <?php
пространство имен Поставщик\Продукт\Обозреватель;

используйте \Magento\Framework\Event\ObserverInterface;
используйте \Magento\Framework\Event\Observer как EventObserver;
используйте Vendor\Product\Ui\DataProvider\Product\Form\Modifier\CustomFieldset;

класс ProductSaveAfter реализует ObserverInterface
{

    /**
     * @param EventObserver $наблюдатель
     */
    выполнение публичной функции (\Magento\Framework\Event\Observer $observer)
    {
        /** @var \Magento\Catalog\Model\Product $product */
        $product = $observer->getEvent()->getProduct();
        если (! $ продукт) {
            возвращаться;
        }

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

        // Работа с данными здесь
    }
}
?>

Данные в обозревателе:

Теперь вы можете вызвать свою собственную модель из наблюдателя и сохранить в ней данные или изменить ее по своему усмотрению.

Будь осторожен! Если сохранение вашей модели связано с сохранением товара, то это может привести к рекурсии.