Despite the fact that the whole Magento team puts huge efforts to persuade developers use best practices in developing for Magento, the Internet is still full of harmful information on how to create Magento 2 custom attributes using deprecated functionality or calling ObjectManager directly, what is even worse.

In this tutorial, I'm going to give you some tips on how to do it in a way not to break Magento core principles and get clean and maintainable code.

Registration of a new module

To begin with, let's create a basic Magento 2 module Meigee_ProductAttrbutes as follows:

1. Declare your module in [ YOUR_MAGENTO ]/app/Meigee/ProductAttrbutes/etc/module.xml

2. Then register it in [ YOUR_MAGENTO ]/app/Meigee/ProductAttrbutes/registration.php

Once you have registered your module, let's add a new product attribute using a setup script which is the best place for this purpose. Here's the basic structure of the setup script

Nothing surprising here. All setup scripts in Magento 2 must implement install data interface Magento\Framework\Setup\InstallDataInterface, which forces you to implement a public method install with two required parameters: Magento\Framework\Setup\ModuleDataSetupInterface and Magento\Framework\Setup\ModuleContextInterface

Creating a custom attribute

Ok, now we're ready to start adding product attributes. First of all, we need to create EavSetup object using a factory. To achieve this let's create a public method __construct and inject Magento\Eav\Setup\EavSetup class in it. By adding a Factory suffix to our class we're saying Magento we need a factory for this class.

Next, we need to get a new EavSetup instance and use addAttribute() method to add a new attribute. Let's take a look at this method closer.

Method addAttribute()

This method requires three paramaters.

First one is an entity type code. You can find all available entity types in the database table eav_entity_type. However, you can take a required type code from there but I encourage you to use a constant instead to ensure consistency between Magento and our code.

Magento 2 eav_entity_type table

There are several classes where you can find needed constant but, obviously, the best approach would be using a constant from a Data Transfer Object interface Magento\Catalog\Api\Data\ProductAttributeInterface::ENTITY_TYPE_CODE Why?

  1. Maintainability. This interface marked as @api which means this file won't be modified or removed in the near future
  2. Consistency. Magento introduced a layer of Service Contracts, so let's stick with best practices for API Design

The second required parameter is our attribute code which could be just a random string variable and the third parameter is an array of attribute properties. In my opinion, there are two major mistakes developers are prone to make when they create an attribute programmatically:

  1. Copy paste from all possible resources online
  2. Using wrong keys

As a result, we can find such ugly lists of properties:

What ugly in here you would probably ask? The list contains properties with default values and there is also one non-existing property. This makes our code messy. It can and must be cut to this:

As a matter of fact, this happens due to lack of experience and knowledge. So, how we can avoid these mistakes? Don't copy past properties, instead, go through the proofed list and copy only those that differ from what you need so you want to specify a different value.

You can find the complete list of attribute properties in so-called property mappers wich actually map your passed properties with appropriate fields in the database

\Magento\Eav\Model\Entity\Setup\PropertyMapper
\Magento\Catalog\Model\ResourceModel\Setup\PropertyMapper

At this point, we specified quite a few properties which is actually almost enough for a displaying another attribute. We specified:

  • label: a label for our attribute
  • user_defined: to be able to save attribute in the admin panel
  • visible_on_front: to have it visible on frontend
  • visible_in_advanced_search: to have it visible on the advanced search page

Adding Attribute to a default attribute group

We still need to specify a few more properties such as attribute set and group before we run our script. In order to find out the default group name we need to get the default attribute set ID and the default attribute group ID at first.

As you can see from the code above getting such information is quite simple 'cause the $eavSetup instance has such public methods. After required modifications our code would look like this:

Adding Attribute to a custom attribute group

When we work on custom projects it is quite often that creating a custom attribute group is required so let's see how to create another attribute as we did above but without assigning to a group. So, at first we're passing some properties into addAttribute() method

Then we're getting ID of our new custom attribute as follows

And the following code will add attribute group and then assign our custom attribute to that group

Here's the final code of InstallData class for creating two custom attributes and assigning the second one to a custom group

Ok, we're almost there. Now we need to run a following Magento 2 cli command in terminal and check if our two new attributes were added to the database.


bin/magento setup:uprade

Here they are in the table eav_attribute

Custom attribute in Magento 2

as well as successfully displayed in the admin panel on a product edit page:

Custom attribute in Magento 2

Custom attribute in a custom attribute group in Magento 2

Wrap it up

In this tutorial we have covered a few things such:

  • A better way of getting ENTITY_TYPE_CODE
  • Adding a custom attribute to default group
  • Adding a custom attribute to custom group

All the code was written on Magento CE 2.3.0 but it's quite simple so must be working with older versions as well. Feel free to ask your questions in comments below if anything.

Feel free to grab this boilerplate code for creating custom product attributes - Download on GitHub