Magento 2. Добавление Изображений В Динамический Массив

Приветствую вас, жители Хабры! Набравшись немного смелости, я решил написать свою первую статью, а точнее поделиться небольшим опытом, на, как мне кажется, интересную тему, а именно как в конфиге добавить загрузчик файлов в динамический массив.

Итак, начнем.

Сначала давайте создадим модуль и его базовую структуру.

Г-н/ImageDynamicConfig/registration.php

  
  
  
  
  
  
  
  
  
  
  
   

<Эphp \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, 'Mr_ImageDynamicConfig', __DIR__ );

Mr/ImageDynamicConfig/etc/module.xml

<Эxml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd "> <module name="Mr_ImageDynamicConfig" setup_version="1.0.0"/> </config>

Далее начнем пошагово описывать все необходимые элементы: И первым делом создадим сам конфиг: Mr/ImageDynamicConfig/etc/adminhtml/system.xml

<Эxml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd "> <system> <tab id="mr" translate="label" sortOrder="400"> <label>Mr</label> </tab> <section id="swatch" translate="label" type="text" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1"> <class>separator-top</class> <label>Image Array Swatch</label> <tab>mr</tab> <resource>Mr_ImageDynamicConfig::config</resource> <group id="image_serializer" translate="label" type="text" sortOrder="140" showInDefault="1" showInWebsite="1" showInStore="1"> <field id="image" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0"> <label>Image</label> <frontend_model>Mr\ImageDynamicConfig\Block\Adminhtml\System\Config\ImageFields</frontend_model> <backend_model>Mr\ImageDynamicConfig\Model\Config\Backend\Serialized\ArraySerialized</backend_model> <upload_dir>var/uploads/swatch/image_serializer</upload_dir> </field> </group> </section> </system> </config>

Для динамического массива строка Mr\ImageDynamicConfig\Block\Adminhtml\System\Config\ImageFields вообще не нов, а класс ImageFields отображает все основные столбцы и показывает, как они должны выглядеть Mr/ImageDynamicConfig/Block/Adminhtml/System/Config/ImageFields.php

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Block\Adminhtml\System\Config; use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray; class ImageFields extends AbstractFieldArray { const IMAGE_FIELD = 'image'; const NAME_FIELD = 'name'; private $imageRenderer; protected function _prepareToRender() { $this->addColumn( self::IMAGE_FIELD, [ 'label' => __('Image'), 'renderer' => $this->getImageRenderer() ] ); $this->addColumn( self::NAME_FIELD, [ 'label' => __('Name'), ] ); $this->_addAfter = false; $this->_addButtonLabel = __('Add'); } private function getImageRenderer() { if (!$this->imageRenderer) { $this->imageRenderer = $this->getLayout()->createBlock( \Mr\ImageDynamicConfig\Block\Adminhtml\Form\Field\ImageColumn::class, '', ['data' => ['is_render_to_js_template' => true]] ); } return $this->imageRenderer; } }

здесь в методе _prepareToRender мы объявляем столбцы, которые будут в динамическом массиве, и если столбец имеет поле, отличное от текстового ввода, мы описываем рендерер для этого поля (метод getImageRenderer).

В строке 38 мы визуализируем блок \Mr\ImageDynamicConfig\Block\Adminhtml\Form\Field\ImageColumn, который даст нам html-код вместо ввода с выбором файлов и отображением файла.

Mr/ImageDynamicConfig/Block/Adminhtml/Form/Field/ImageColumn.php

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Block\Adminhtml\Form\Field; use Mr\ImageDynamicConfig\Block\Adminhtml\ImageButton; class ImageColumn extends \Magento\Framework\View\Element\AbstractBlock { public function setInputName(string $value) { return $this->setName($value); } public function setInputId(string $value) { return $this->setId($value); } protected function _toHtml(): string { $imageButton = $this->getLayout() ->createBlock(ImageButton::class) ->setData('id', $this->getId()) ->setData('name', $this->getName()); return $imageButton->toHtml(); } }

В перегруженном методе _toHtml мы рендерим блок Mr\ImageDynamicConfig\Block\Adminhtml\ImageButton, который даст нам шаблон с html-кодом Mr/ImageDynamicConfig/Block/Adminhtml//ImageButton.php

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Block\Adminhtml; class ImageButton extends \Magento\Backend\Block\Template { protected $_template = 'Mr_ImageDynamicConfig::config/array_serialize/swatch_image.phtml'; private $assetRepository; public function __construct( \Magento\Backend\Block\Template\Context $context, \Magento\Framework\View\Asset\Repository $assetRepository, array $data = [] ) { $this->assetRepository = $assetRepository; parent::__construct($context, $data); } public function getAssertRepository(): \Magento\Framework\View\Asset\Repository { return $this->assetRepository; } }

Нам нужен общедоступный метод getAssertRepository для отображения полного URL-адреса CSS-файла в шаблоне.

Mr/ImageDynamicConfig/view/adminhtml/templates/config/array_serialize/swatch_image.phtml

<Эphp /*** @var \Mr\ImageDynamicConfig\Block\Adminhtml\ImageButton $block */ $css = $block->getAssertRepository()->createAsset("Mr_ImageDynamicConfig::css/image_button.css"); ?> <link rel="stylesheet" type="text/css" media="all" href="<Эphp /* @escapeNotVerified */echo $css->getUrl() ?>"/> <div class="upload-file" data-id="<?=$block->getId()?>"> <div class="upload-file__block upload-file__block_first"> <img class="upload-file__block__img" id="swatch_image_image_<?= $block->getId() ?>" src=""> </div> <div class="upload-file__block"> <input class="upload-file__input" hidden type="file" name="<?= $block->getName() ?>" id="swatch_image_input_<?= $block->getId() ?>" value=""/> <label class="upload-file__label" for="swatch_image_input_<?= $block->getId() ?>"> <?= __("File") ?> </label> </div> <input class="upload-file__input" type="hidden" id="<?=$block->getId()?>"> </div> <script type="text/javascript"> require(["jquery"], function (jq) { jq(function () { const id = "<?=$block->getId()?>" const imageId = "swatch_image_image_<?=$block->getId()?>" const data = jq("#" + id).

val(); if (data) { jq("#" + imageId).

attr("src", data) jq("#" + imageId).

attr("value", data) } }); }); </script>

Этот шаблон отображает входные данные для загрузки и вывода загруженного изображения.

С одной стороны, очень странное решение сделать скрытый ввод:

<input class="upload-file__input" type="hidden" id="<?=$block->getId()?>">

а затем вставьте значение из него в тег img:

jq(function () { const id = "<?=$block->getId()?>" const imageId = "swatch_image_image_<?=$block->getId()?>" const data = jq("#" + id).

val(); if (data) { jq("#" + imageId).

attr("src", data) jq("#" + imageId).

attr("value", data) } });

Но когда Magento отображает форму в конфигурации, чтобы вставить туда значение, он пытается найти входные данные с идентификатором и записать это значение в значение.

Вот почему я сделал скрытый ввод и передал путь к изображению в исходный img с помощью jquery. Таким образом, мы рассмотрели frontend_model и способы отображения введенного изображения в динамическом массиве.

Теперь рассмотрим этап загрузки изображений.

Для этого используется backend_model, а в обычных случаях, когда нужно просто добавить в конфиг динамический массив, то мы его кидаем в backend_model Magento\Config\Model\Config\Backend\Serialized\ArraySerialized и это решает все наши проблемы, но ArraySerialized не работает с загрузкой и сохранением изображений и на основе этого делаем свой сериализатор массива Mr/ImageDynamicConfig/Model/Config/Backend/Serialized/ArraySerialized

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Model\Config\Backend\Serialized; use Magento\Framework\Serialize\Serializer\Json; use Mr\ImageDynamicConfig\Block\Adminhtml\System\Config\ImageFields; class ArraySerialized extends \Magento\Config\Model\Config\Backend\Serialized\ArraySerialized { private $imageUploaderFactory; private $imageConfig; public function __construct( \Magento\Framework\Model\Context $context, \Magento\Framework\Registry $registry, \Magento\Framework\App\Config\ScopeConfigInterface $config, \Magento\Framework\App\Cache\TypeListInterface $cacheTypeList, \Mr\ImageDynamicConfig\Model\Config\ImageConfig $imageConfig, \Mr\ImageDynamicConfig\Model\ImageUploaderFactory $imageUploaderFactory, \Magento\Framework\Model\ResourceModel\AbstractResource $resource = null, \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null, array $data = [], Json $serializer = null ) { $this->imageUploaderFactory = $imageUploaderFactory; $this->imageConfig = $imageConfig; parent::__construct( $context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data, $serializer ); } public function beforeSave(): ArraySerialized { $value = $this->getValue(); $value = $this->mapRows($value); $this->setValue($value); return parent::beforeSave(); } private function mapRows(array $rows): array { $iconUploader = $this->imageUploaderFactory->create([ 'path' => $this->getPath(), 'uploadDir' => $this->getUploadDir(), ]); $uploadedFiles = $iconUploader->upload(); $swatches = $this->imageConfig->getSwatches(); foreach ($rows as $id => $data) { if (isset($uploadedFiles[$id])) { $rows[$id][ImageFields::IMAGE_FIELD] = $uploadedFiles[$id]; continue; } if (!isset($swatches[$id])) { unset($swatches[$id]); } else { $rows[$id] = $this->matchRow($data, $swatches[$id]); } } return $rows; } private function matchRow(array $row, array $configTabIcon): array { foreach ($row as $fieldName => $value) { if (is_array($value) && $fieldName == ImageFields::IMAGE_FIELD) { $row[ImageFields::IMAGE_FIELD] = $configTabIcon[ImageFields::IMAGE_FIELD]; } } return $row; } private function getUploadDir(): string { $fieldConfig = $this->getFieldConfig(); if (!array_key_exists('upload_dir', $fieldConfig)) { throw new \Magento\Framework\Exception\LocalizedException( __('The base directory to upload file is not specified.') ); } if (is_array($fieldConfig['upload_dir'])) { $uploadDir = $fieldConfig['upload_dir']['value']; if (array_key_exists('scope_info', $fieldConfig['upload_dir']) && $fieldConfig['upload_dir']['scope_info'] ) { $uploadDir = $this->_appendScopeInfo($uploadDir); } if (array_key_exists('config', $fieldConfig['upload_dir'])) { $uploadDir = $this->getUploadDirPath($uploadDir); } } else { $uploadDir = (string)$fieldConfig['upload_dir']; } return $uploadDir; } }

Здесь мы уделим немного внимания методу mapRows, в строках 50-54 загружаем изображение, в строках 56-66 модифицируем данные из конфига, добавляем/заменяем изображение в массив конфига и добавляем/обновляем оставшиеся поля тоже Класс ImageUploader: Mr/ImageDynamicConfig/Model/ImageUploader.php

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Model; use Magento\MediaStorage\Model\File\Uploader; class ImageUploader { private $arrayFileModifier; private $uploaderFactory; private $uploadDir; private $allowExtensions; public function __construct( \Mr\ImageDynamicConfig\Model\ArrayFileModifier $arrayFileModifier, \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory, string $uploadDir, array $allowExtensions ) { $this->arrayFileModifier = $arrayFileModifier; $this->uploaderFactory = $uploaderFactory; $this->uploadDir = $uploadDir; $this->allowExtensions = $allowExtensions; } public function upload(): array { $result = []; $files = $this->arrayFileModifier->modify(); if (!$files) { return $result; } foreach ($files as $id => $file) { try { $uploader = $this->uploaderFactory->create(['fileId' => $id]); $uploader->setAllowedExtensions($this->allowExtensions); $uploader->setAllowRenameFiles(true); $uploader->addValidateCallback('size', $this, 'validateMaxSize'); $newFileName = $this->getNewFileName($uploader); $uploader->save($this->uploadDir, $newFileName); $result[$id] = $this->getFullFilPath($newFileName); } catch (\Exception $e) { throw new \Magento\Framework\Exception\LocalizedException(__('%1', $e->getMessage())); } } return $result; } private function getNewFileName(Uploader $uploader): string { return sprintf( '%s.%s', uniqid(), $uploader->getFileExtension() ); } private function getFullFilPath(string $filename): string { return sprintf( '/%s/%s', $this->uploadDir, $filename ); } }

В этом классе есть строка $files = $this-> arrayFileModifier-> modify(); Этот модификатор нужен нам для преобразования массива, пришедшего к нам из такого вида:

Magento 2. Добавление изображений в динамический массив

так, чтобы было понятно загрузчику:

Magento 2. Добавление изображений в динамический массив

передать идентификатор $uploader = $this-> uploaderFactory-> create(['fileId' => $id]); и загрузчик знал, с чем работать.

И последняя загадка — класс для работы с конфигом Г-н/ImageDynamicConfig/Модель/Config/ImageConfig

<Эphp declare(strict_types=1); namespace Mr\ImageDynamicConfig\Model\Config; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\Serialize\SerializerInterface; class ImageConfig { const XML_PATH_IMAGE_SERIALIZER = 'swatch/image_serializer/'; private $scopeConfig; private $serializer; public function __construct( SerializerInterface $serializer, ScopeConfigInterface $scopeConfig ) { $this->scopeConfig = $scopeConfig; $this->serializer = $serializer; } public function getSwatches(): array { $data = $this->scopeConfig->getValue(self::XML_PATH_IMAGE_SERIALIZER .

'image'); if (!$data) { return []; } return $this->serializer->unserialize($data); } }

И сам результат:

Magento 2. Добавление изображений в динамический массив



Magento 2. Добавление изображений в динамический массив

Эпилог Репозиторий модулей на GitHub Я надеюсь, что кто-то найдет эту статью интересной и/или полезной.

Если у вас есть замечания/предложения/вопросы, добро пожаловать в комментарии.

Спасибо за внимание.

Теги: #php #Разработка электронной коммерции #magento #magento2 #динамические строки изображений

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.