How to add a custom image field to Catalog Category in Magento 2

Update: Magento 2.2 compatible version can be downloaded here. Note: tested in Magento version 2.2.2.

This article explains the steps needed to add a custom image field to categories in Magento 2. The code is tested on Magento version 2.1.2.

First, in the InstallData script of your custom module, insert a category image attribute to Catalog Category model. In this example the attribute name is ‘thumbnail’.
File: app/code/Vendor/Module/Setup/InstallData.php

    ...
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {                
        /** @var \Magento\Catalog\Setup\CategorySetup $categorySetup */
        $categorySetup = $this->categorySetupFactory->create(['setup' => $setup]);
 
        $categorySetup->addAttribute(
            \Magento\Catalog\Model\Category::ENTITY,
            'thumbnail',
            [
                'type' => 'varchar',
                'label' => 'Thumbnail',
                'input' => 'image',
                'backend' => 'Magento\Catalog\Model\Category\Attribute\Backend\Image',
                'required' => false,
                'sort_order' => 5,
                'global' => ScopedAttributeInterface::SCOPE_STORE,
                'group' => 'Content',
                'is_used_in_grid' => false,
                'is_visible_in_grid' => false,
                'is_filterable_in_grid' => false
            ]
        );
    }    
    ...

Then add the image field to Category form using category_form.xml. In this example, our thumbnail image field will appear after the default ‘Category Image’ field.
File: app/code/Vendor/Module/view/adminhtml/ui_component/category_form.xml

<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">    
    <fieldset name="content">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="label" xsi:type="string" translate="true">Content</item>
                <item name="collapsible" xsi:type="boolean">true</item>
                <item name="sortOrder" xsi:type="number">10</item>
            </item>
        </argument>        
        <field name="thumbnail">
            <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">Thumbnail Image</item>
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="formElement" xsi:type="string">fileUploader</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">41</item>
                    <item name="uploaderConfig" xsi:type="array">
                        <item name="url" xsi:type="url" path="your_module_admin_frontname/category_thumbnail/upload"/>
                    </item>
                </item>
            </argument>
        </field>        
    </fieldset>    
</form>

Then we create a backend controller action, which is specified in ‘uploaderConfig’ section in the above ‘category_form.xml’, to handle image upload.
File: app/code/Vendor/Module/Controller/Adminhtml/Category/Thumbnail/Upload.php

namespace Vendor\Module\Controller\Adminhtml\Category\Thumbnail;
 
use Magento\Framework\Controller\ResultFactory;
 
/**
 * Class Upload
 */
class Upload extends \Magento\Backend\App\Action
{
    /**
     * Image uploader
     *
     * @var \Magento\Catalog\Model\ImageUploader
     */
    protected $imageUploader;
 
    /**
     * 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
    ) {
        parent::__construct($context);
        $this->imageUploader = $imageUploader;
    }
 
    /**
     * Check admin permissions for this controller
     *
     * @return boolean
     */
    protected function _isAllowed()
    {
        return $this->_authorization->isAllowed('Magento_Catalog::categories');
    }
 
    /**
     * Upload file controller action
     *
     * @return \Magento\Framework\Controller\ResultInterface
     */
    public function execute()
    {
        try {           
            $result = $this->imageUploader->saveFileToTmpDir('thumbnail');
 
            $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) {
            $this->_objectManager->get('Psr\Log\LoggerInterface')->critical($e);
            $result = ['error' => $e->getMessage(), 'errorcode' => $e->getCode()];
        }
        return $this->resultFactory->create(ResultFactory::TYPE_JSON)->setData($result);
    }
}

To save the image details to database, a Plugin for Magento\Catalog\Controller\Adminhtml\Category\Save is created to append the image field details during category save action.
File: app/code/Vendor/Module/Controller/Adminhtml/Category/Save/Plugin.php

namespace Vendor\Module\Controller\Adminhtml\Category\Save;
 
class Plugin
{           
    //add thumnail field to $data for saving
    public function afterImagePreprocessing(\Magento\Catalog\Controller\Adminhtml\Category\Save $subject, $data)
    {
        if (isset($data['thumbnail']) && is_array($data['thumbnail'])) {
            if (!empty($data['thumbnail']['delete'])) {
                $data['thumbnail'] = null;
            } else {
                if (isset($data['thumbnail'][0]['name']) && isset($data['thumbnail'][0]['tmp_name'])) {
                    $data['thumbnail'] = $data['thumbnail'][0]['name'];
                } else {
                    unset($data['thumbnail']);
                }
            }
        }else{
            $data['thumbnail'] = null;
        }
 
        return $data;
    }
 
}

Finally, to retrieve and display the saved image in a category form we create another Plugin for Magento\Catalog\Model\Category\DataProvider
File: app/code/Vendor/Module/Model/Category/DataProvider/Plugin.php

namespace Vendor\Module\Model\Category\DataProvider;
 
class Plugin
{       
    protected $_storeManager;
 
    public function __construct(        
        \Magento\Store\Model\StoreManagerInterface $storeManager
    ) {       
        $this->_storeManager = $storeManager;    
    }
 
    //retrieve thumnail data for output
    public function afterGetData(\Magento\Catalog\Model\Category\DataProvider $subject, $result)
    {
        $category = $subject->getCurrentCategory();
        $categoryData = $result[$category->getId()];
 
        if (isset($categoryData['thumbnail'])) {
            unset($categoryData['thumbnail']);
            $categoryData['thumbnail'][0]['name'] = $category->getData('thumbnail');
            $categoryData['thumbnail'][0]['url'] = $this->getThumbnailUrl($category->getData('thumbnail'));
        }
 
        $result[$category->getId()] = $categoryData;
 
        return $result;
    }
 
    protected function getThumbnailUrl($imageName){
        $url = $this->_storeManager->getStore()->getBaseUrl(
                \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
            ) . 'catalog/category/' . $imageName;
        return $url;
    }
}

The complete working module for Magento 2.1.x can be downloaded here.

8 thoughts on “How to add a custom image field to Catalog Category in Magento 2”

  1. Hi,

    It does not work anymore on Magento 2.2. Images are not saved.
    Is there any way we can update the module to make it work?

    Thanks

  2. Thank you. But how do I get the saved image in the frontend now?
    Backendwise everything works fine but in the frontend the $category object does not have a thumbnail in it’s data array. $category->getImageUrl(‘thumbnail’); does not return anything either.

      1. Hi Van Bonkers, what did you exactly do? I have the same issue, Backend is working fine, but $category->getImageUrl(‘thumbnail’) is not returning anything. I run reindex in CLI.

  3. Hi,

    It does not work anymore on Magento 2.3.3. Images are not saved.
    Is there any way we can update the module to make it work?

    Thanks

Leave a Reply

Your email address will not be published. Required fields are marked *