Browsed by
Tag: category

How to Create a Category Image Attribute in Magento 2

How to Create a Category Image Attribute in Magento 2

Creating a custom Category Attribute with an image upload is quite common feature requirement in our shops.
So this blog-post will be about the steps you have to take to create a custom category attribute with an image upload in Magento 2.
The post turned out to be quite long, but I wanted to provide a complete description of all steps necessary. So stay with us if your interested 🙂

Our Module is called Dev98_CategoryAttributes and the image attribute will be called dev98_icon.

Creating the Attribute

In our projects we are creating the attributes using Install- or UpdateData classes, except when the attributes are explicitly managed manually.
So to create thedev98_icon we might write something like this.

After running the setup:upgrade command to upgrade the application database and schema, we have created the attribute and should be able to store information for this attribute.

Extending the Category Form

To get an file upload for our attribute we need to extend the ui-component for the category form.
The category form is defined in a category_form.xml, which can be extend by adding the following code to the file
Dev98/CategoryAttributes/view/adminhtml/ui_component/category_form.xml

The above code block defines a new fieldset dev98_attributes which will contain one fielddev98_icon.
There is one parameter for the field dev98_icon I want to point out, which is theuploaderConfig array.
In this array we have a parameter called url which defines the ActionController which is responsible for handling the image upload.
As you can see we have provided a custom ActionController for handling the upload, because after taking a closer look to the Controller Magento uses for it’s category image upload, you will see why.
The file is \Magento\Catalog\Controller\Adminhtml\Category\Image\Upload

The Magento ActionController for handling the image upload is hardwired against the attribute image. There is no reasonable way we can reuse this code, so we will create our own in the next step.

Image Upload ActionController

Basically we have copied the code from Magento and refactored it to be more extensible:

I left out the constructor and the _isAllowed method for a better readability.

Our ActionController expects one argument attribute_code and hands that to the imageUploader which will handle the upload of the image.
Now we have a reusable ActionController if we have to provide more custom category images.
And as it turned out in one of our projects, we did need more than one.
Furthermore we might extract that code into a vendor module.

Current Status

√ Create the Attribute
√ Create category_form field
√ Create the Controller handling the upload

We have already done quite some work but we are not done yet.

If you try running the code created so far you might see an Image Upload and the Image will be uploaded to the tmp folder media/catalog/category/dev98/tmp/.
But it will remain there and there will be nothing saved to our dev98_icon attribute for this category.

So why is that?

The data for our dev98_icon field in the request is provided as an array. And as there is nothing changed regarding that fact, we end up with nothing being saved to our attribute.
Futhermore, we need to handle the move from the tmp directory to the final image destination ourselves.
That is OK in my opinion as there might be an error during the category save and we only want the image to be moved to its final destination when the save was successful.

Category Save Observers

To do so we need to observe the following two events:

  • catalog_category_prepare_save
  • catalog_category_save_after

In our Observer for catalog_category_prepare_save we will convert the attribute image array to a string and in the catalog_category_save_after we will move the image from the tmp folder to its destination.

Dev98\CategoryAttributes\Observer\Category\CategoryImageDataPrepare

The execute method looks rather complicated, but what it basically does is to set the image-name to the category model and mark the category as  is_uploaded so later-on we can detect we have to move an image. This code is mostly copied from the Magento Core, because as it turned out this part is also not reusable – yet.

Current Status 2

√ Create the Attribute
√ Create category_form field
√ Create the Controller handling the upload
√ Observe the CategoryPrepareSave to get image name saved
√ Observe the CategoryAfterSave to get image moved to destination

But now we are done you might think.

Well. No. There are still further steps to take to get this working completely.

At this moment your image upload and saving will be working, but after refreshing the page you will see there is still something wrong as the image is either not shown at all or a placeholder image is shown.

Plugin Category DataProvider

After some debugging you will find out that there is class called  \Magento\Catalog\Model\Category\DataProvider which is used to provide the category data for the form.
In this class we will find a method getData which does the image preparation for the standard image, again in a non extensible way:

Next up we will create a Plugin that integrates after the  getData method and does the preparation for our custom attributes.

Let’s create the di.xml with the Plugin class called
Dev98\CategoryAttributes\Plugin\Category\CategoryDataProviderPlugin

And finally our Plugin class itself:

As you can see we extracted the url generation of the icon to a CategoryUrlRepository as this funcationality is need within more classes and in other modules as well.

So last but not least here is the CategoryUrlRepository:

That was the final class we needed to create to get this image upload working through out.

Full Checklist

Here is a short Summary of the classes and files created.

√ Create the Attribute
√ Create category_form field
√ Create the Controller handling the upload
√ Observe the CategoryPrepareSave to get image name saved
√ Observe the CategoryAfterSave to get image moved to destination
√ Create Plugin for Category\DataProvider to prepare Image Data for Frontend

Final Thoughts

If you have read this far, thanks for bearing with us on this one.
We really spend some time debugging the different issues we had while creating the image upload.
Multiple times we weren’t quite sure whether the error was in the javascript part or server-side.
Sometimes we had to even debug javascript code to find out what was wrong with the implementation on the server-side, as there where errors that were not shown or the data was not provided as needed.

But it was worth the effort and we know have a good glimpse of how the Category Admin is implemented.

The code is still not perfect and might need some further polishing, but it should give you a good starting point.
Right now I am thinking about putting together a public module on github to share this implementation.

If you are interested in the full code of the module please let me know.
And if there is an easier way to achieve these please let me know as well.

 

Magento1 since 2008 / Magento2 since 2015
Passionate Road Bike Rider (~3.500km/yr)
Loves building software with a elaborate architecture and design
3x Magento Certified
Software Developer >10 years
Head of Magento Development @ netz98