Whilst Magento provides a default image attribute assigned to categories, it is common for developers to want to add additional image attributes to categories. For example, to create a thumbnail image that might appear on the home page alongside other thumbnail images and link to the category pages. To add category thumbnail images in Magento 2, follow the steps below.
Firstly, create the directories for a custom module along with the module’s registration.php
and module.xml
files.
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, '[Vendor]_[Module]', __DIR__ );
<?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="[Vendor]_[Module]" setup_version="1.0.0" /> </config>
Run the enable module and database upgrade commands within your Magento 2 root directory.
$ /path/to/your/php bin/magento module:enable [Vendor]_[Module] $ /path/to/your/php bin/magento setup:upgrade
Add an InstallData.php
class which will be responsible for adding the category attribute.
In this example, the attribute code used will be thumbnail_image
. Note the thumbnail image attribute’s backend
type: Magento\Catalog\Model\Category\Attribute\Backend\Image
.
<?php namespace [Vendor]\[Module]\Setup; use Magento\Catalog\Setup\CategorySetupFactory; 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; /** * @codeCoverageIgnore */ class InstallData implements InstallDataInterface { /** * EAV setup factory. * * @var EavSetupFactory */ private $_eavSetupFactory; /** * @var CategorySetupFactory */ protected $categorySetupFactory; /** * InstallData constructor. * * @param EavSetupFactory $eavSetupFactory * @param CategorySetupFactory $categorySetupFactory */ public function __construct( EavSetupFactory $eavSetupFactory, CategorySetupFactory $categorySetupFactory ) { $this->_eavSetupFactory = $eavSetupFactory; $this->categorySetupFactory = $categorySetupFactory; } /** * {@inheritdoc} * * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function install( ModuleDataSetupInterface $setup, ModuleContextInterface $context ) { /** @var EavSetup $eavSetup */ $setup = $this->categorySetupFactory->create(['setup' => $setup]); $setup->addAttribute( \Magento\Catalog\Model\Category::ENTITY, 'thumbnail_image', [ 'type' => 'varchar', 'label' => 'Thumbnail 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', ] ); } }
In the Categories section within the admin, the fields are rendered using a form UI component. Therefore in order to add a custom attribute to this form, create a category_form.xml
file and define the custom attribute field.
Ensure that the field_name
value matches the attribute code of your custom attribute.
<?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="thumbnail_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">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">40</item> <item name="uploaderConfig" xsi:type="array"> <item name="url" xsi:type="url" path="[module]/thumbnail/upload"/> </item> </item> </argument> </field> </fieldset> </form>
Note the path
defined within the uplaoderConfig
item. This will be the URL that will handle the upload of the thumbnail image attribute.
Define the route to handle the upload within a routes.xml
file.
<?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="[module]" frontName="[module]"> <module name="[Vendor]_[Module" before="Magento_Backend" /> </route> </router> </config>
And proceed with adding the controller class.
<?php namespace Vendor\Module\Controller\Adminhtml\Thumbnail; use Magento\Framework\Controller\ResultFactory; 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; } /** * Upload file controller action * * @return \Magento\Framework\Controller\ResultInterface */ public function execute() { try { $result = $this->imageUploader->saveFileToTmpDir('thumbnail_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); } }
Now create the module’s di.xml
file and define the imageUploader
class to use to handle the upload. In addition, add arguments to the imageUploader
to define a baseTmpPath
, basePath
and the image allowedExtensions
.
<?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="[Vendor]\[Module]\Controller\Adminhtml\Thumbnail\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> </config>
Within the Magento\Catalog\Model\Category\DataProvider
class, a getFieldsMap()
method exists that returns the fields associated to a category.
<?php namespace Magento\Catalog\Model\Category; .... class DataProvider .... protected function getFieldsMap() { return [ 'general' => [ 'parent', 'path', 'is_active', 'include_in_menu', 'name', ], 'content' => [ 'image', 'description', 'landing_page', ], 'display_settings' => [ 'display_mode', 'is_anchor', 'available_sort_by', 'use_config.available_sort_by', 'default_sort_by', 'use_config.default_sort_by', 'filter_price_range', 'use_config.filter_price_range', ], 'search_engine_optimization' => [ 'url_key', 'url_key_create_redirect', 'url_key_group', 'meta_title', 'meta_keywords', 'meta_description', ], 'assign_products' => [ ], 'design' => [ 'custom_use_parent_settings', 'custom_apply_to_products', 'custom_design', 'page_layout', 'custom_layout_update', ], 'schedule_design_update' => [ 'custom_design_from', 'custom_design_to', ], 'category_view_optimization' => [ ], 'category_permissions' => [ ], ]; } .... }
This method will need to be overridden in order to the thumbnail_image
field.
Edit the custom module’s di.xml
file and add a <preference>
for this class.
<?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="[Vendor]\[Module]\Controller\Adminhtml\Thumbnail\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="[Vendor]\[Module]\Model\Category\DataProvider" /> </config>
Add the overriding class, and implement the getFieldsMap()
method that adds the thumbnail_image
to the array.
<?php namespace [Vendor]\[Module]\Model\Category; use Magento\Catalog\Model\Category\DataProvider as CategoryDataProvider; class DataProvider extends CategoryDataProvider { /** * @return array */ protected function getFieldsMap() { $fields = parent::getFieldsMap(); $fields['content'][] = 'thumbnail_image'; return $fields; } }
Refresh the cache and you should notice that the thumbnail image attribute has been added to the category form.
Note: This article is based on Magento Open Source version 2.2.