Magento / Magento2 / PHP

How to Create Custom Image Uploader Attribute in Category with Drag & Drop Option

Create Custom Image Uploader Attribute in Category with Drag & Drop Option

Create registration.php file inside app/code/Webcreta/CustomImage folder

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

Create module.xml file inside app/code/Webcreta/CustomImage/etc folder

<?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="Webcreta_CustomImage" setup_version="1.0.0" />
</config>

Create InstallData.php file inside app/code/Webcreta/CustomImage/Setup folder

<?php
  namespace Webcreta\CustomImage\Setup;

  use Magento\Eav\Setup\EavSetup;
  use Magento\Eav\Setup\EavSetupFactory;
  use Magento\Framework\Setup\InstallDataInterface;
  use Magento\Framework\Setup\ModuleContextInterface;
  use Magento\Framework\Setup\ModuleDataSetupInterface;
  use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;

  /**
    * @codeCoverageIgnore
  */
  class InstallData implements InstallDataInterface
  {
    /**
      * EAV setup factory.
      *
      * @var EavSetupFactory
    */
    private $_eavSetupFactory;
    protected $categorySetupFactory;

    /**
      * Init.
      *
      * @param EavSetupFactory $eavSetupFactory
    */
    public function __construct(EavSetupFactory $eavSetupFactory, \Magento\Catalog\Setup\CategorySetupFactory $categorySetupFactory)
    {
      $this->_eavSetupFactory = $eavSetupFactory;
      $this->categorySetupFactory = $categorySetupFactory;
    }

    /**
      * {@inheritdoc}
      *
      * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
    */
    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
      /** @var EavSetup $eavSetup */
      $eavSetup = $this->_eavSetupFactory->create(['setup' => $setup]);
      $setup = $this->categorySetupFactory->create(['setup' => $setup]);
      $setup->addAttribute(
            \Magento\Catalog\Model\Category::ENTITY, 'custom_image', [
      'type' => 'varchar',
      'label' => 'Custom Image',
      'input' => 'image',
      'backend' => 'Magento\Catalog\Model\Category\Attribute\Backend\Image',
      'required' => false,
      'sort_order' => 9,
      'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
      'group' => 'General Information',
            ]
      );
    }
  }

Create a category_form.xml file inside app/code/Webcreta/CustomImage/view/adminhtml/ui_component folder

<?xml version="1.0" encoding="UTF-8"?>
  <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <fieldset name="content">
      <field name="custom_image">
        <argument name="data" xsi:type="array">
          <item name="config" xsi:type="array">
            <item name="dataType" xsi:type="string">string</item>
            <item name="source" xsi:type="string">category</item>
            <item name="label" xsi:type="string" translate="true">Custom Image</item>
            <item name="visible" xsi:type="boolean">true</item>
            <item name="formElement" xsi:type="string">imageUploader</item>
            <item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
            <item name="previewTmpl" xsi:type="string">Magento_Catalog/image-preview</item>
            <item name="required" xsi:type="boolean">false</item>
            <item name="sortOrder" xsi:type="number">40</item>
            <item name="uploaderConfig" xsi:type="array">
              <item name="url" xsi:type="url" path="customimage/category_image/upload"/>
            </item>
          </item>
        </argument>
      </field>
    </fieldset>
  </form>

Create di.xml file inside app/code/Webcreta/CustomImage/etc folder

<?xml version="1.0"?>
  <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Webcreta\CustomImage\Controller\Adminhtml\Category\Image\Upload">
      <arguments>
        <argument name="imageUploader" xsi:type="object">Magento\Catalog\CategoryImageUpload</argument>
      </arguments>
    </type>
    <virtualType name="Magento\Catalog\CategoryImageUpload" type="Magento\Catalog\Model\ImageUploader">
      <arguments>
        <argument name="baseTmpPath" xsi:type="string">catalog/tmp/category</argument>
        <argument name="basePath" xsi:type="string">catalog/category</argument>
        <argument name="allowedExtensions" xsi:type="array">
          <item name="jpg" xsi:type="string">jpg</item>
          <item name="jpeg" xsi:type="string">jpeg</item>
          <item name="gif" xsi:type="string">gif</item>
          <item name="png" xsi:type="string">png</item>
        </argument>
      </arguments>
    </virtualType>
    <preference for="Magento\Catalog\Model\Category\DataProvider" type="Webcreta\CustomImage\Model\Category\DataProvider" />
  </config>

create routes.xml file inside app/code/Webcreta/CustomImage/etc/adminhtml folder

  <?xml version="1.0"?>
  <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="admin">
      <route id="customimage" frontName="customimage">
        <module name="Webcreta_CustomImage" before="Magento_Backend" />
      </route>
    </router>
  </config>

Create Upload.php file inside app/code/Webcreta/CustomImage/Controller/Adminhtml/Category/Image folder

<?php
  namespace Webcreta\CustomImage\Controller\Adminhtml\Category\Image;

  use Magento\Framework\Controller\ResultFactory;

  /**
    * Agorae Adminhtml Category Image Upload Controller
  */
  class Upload extends \Magento\Backend\App\Action
  {
    /**
      * Image uploader
      *
      * @var \Magento\Catalog\Model\ImageUploader
    */
    protected $imageUploader;

    /**
      * Uploader factory
      *
      * @var \Magento\MediaStorage\Model\File\UploaderFactory
    */
    private $uploaderFactory;

    /**
      * Media directory object (writable).
      *
      * @var \Magento\Framework\Filesystem\Directory\WriteInterface
    */
    protected $mediaDirectory;

    /**
      * Store manager
      *
      * @var \Magento\Store\Model\StoreManagerInterface
    */
    protected $storeManager;

    /**
      * Core file storage database
      *
      * @var \Magento\MediaStorage\Helper\File\Storage\Database
    */
    protected $coreFileStorageDatabase;

    /**
      * @var \Psr\Log\LoggerInterface
    */
    protected $logger;

    /**
      * Upload constructor.
      *
      * @param \Magento\Backend\App\Action\Context $context
      * @param \Magento\Catalog\Model\ImageUploader $imageUploader
    */
    public function __construct(

        \Magento\Backend\App\Action\Context $context,
        \Magento\Catalog\Model\ImageUploader $imageUploader,
        \Magento\MediaStorage\Model\File\UploaderFactory $uploaderFactory,
        \Magento\Framework\Filesystem $filesystem,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\MediaStorage\Helper\File\Storage\Database $coreFileStorageDatabase,
        \Psr\Log\LoggerInterface $logger
    ) {
      parent::__construct($context);
      $this->imageUploader = $imageUploader;
      $this->uploaderFactory = $uploaderFactory;
      $this->mediaDirectory = $filesystem->getDirectoryWrite(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA);
      $this->storeManager = $storeManager;
      $this->coreFileStorageDatabase = $coreFileStorageDatabase;
      $this->logger = $logger;
    }

    /**
      * Check admin permissions for this controller
      *
      * @return boolean
    */
    protected function _isAllowed()
    {
      return $this->_authorization->isAllowed('Webcreta_CustomImage::category');
    }

    /**
      * Upload file controller action
      *
      * @return \Magento\Framework\Controller\ResultInterface
    */
    public function execute()
    {
      try {
        $result = $this->imageUploader->saveFileToTmpDir('custom_image');

        $result['cookie'] = [
                'name' => $this->_getSession()->getName(),
                'value' => $this->_getSession()->getSessionId(),
                'lifetime' => $this->_getSession()->getCookieLifetime(),
                'path' => $this->_getSession()->getCookiePath(),
                'domain' => $this->_getSession()->getCookieDomain(),
        ];
        } catch (\Exception $e) {
        $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
      }
      return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
    }
  }

Create DataProvider.php file inside app/code/Webcreta/CustomImage/Model/Category folder

<?php
  namespace Webcreta\CustomImage\Model\Category;

  class DataProvider extends \Magento\Catalog\Model\Category\DataProvider
  {
    protected function getFieldsMap()
    {
      $fields = parent::getFieldsMap();
      $fields['content'][] = 'custom_image'; // custom image field

      return $fields;
    }
  }

Create di.xml file inside app/code/Webcreta/CustomImage/etc/adminhtml folder

<?xml version="1.0"?>
  <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Controller\Adminhtml\Category\Save">
      <plugin name="webcreta_image_preprocessing" type="Webcreta\CustomImage\Plugin\Catalog\Controller\Adminhtml\Category\SavePlugin" />
    </type>
  </config>

Create SavePlugin.php file inside app/code/Webcreta/CustomImage/Plugin/Catalog/Controller/Adminhtml/Category folder

<?php
  namespace Webcreta\CustomImage\Plugin\Catalog\Controller\Adminhtml\Category;

  use Magento\Catalog\Controller\Adminhtml\Category\Save as SaveController;

  class SavePlugin
  {
    /**
     * Add additional images
     *
     * @param SaveController $subject
     * @param array $data
     * @return array
     */
    public function beforeImagePreprocessing(SaveController $subject, $data)
    {
      foreach ($this->getAdditionalImages() as $imageType) {
        if (empty($data[$imageType])) {
          unset($data[$imageType]);
          $data[$imageType]['delete'] = true;
        }
      }

      return [$data];
    }

    /**
     * Get additional Images
     *
     * @return array
     */
    protected function getAdditionalImages() {
      return ['custom_image'];
    }
  }