Magento 2 Customer Attributes

Introduction

Adding a customer attribute programmatically in Magento 2 can be overwhelming to start with, with all its various settings, and trying to get just the right combination of the settings to make an attribute appear on the Admin, being able to save it, and render the saved value on the Admin grid; let alone customize its behavior. This is surely not documented enough for a developer to feel comfortable with.

There are many possible values for each attribute setting and I'm going to try to guide you through what's necessary with scenario based examples. Yes, there are multiple Stack Overflow answers and personal blogs for the very same topic, but here's my take on it.

We will be using Magento Data Patches for this. I have tested this code on the current latest version - Magento 2.4.2-p1 and I'll try my best to make the code as clean as possible following the Magento best practices. :)

Simple Text Field

For our first task, let's create a simple text field as our customer attribute. This field will hold the ID of the customer from an external system.

To achieve this, we have main four steps to complete:

  • create the attribute with our desired settings
  • add the attribute to an attribute set and group
  • add additional data to the attribute model (form visibility)
  • save the modified attribute model

Prerequisites

  • Create the data patch class under \Manick\Customer\Setup\Patch\Data implementing the \Magento\Framework\Setup\Patch\DataPatchInterface. For now, implement the interface stubs as empty methods. This class must be placed under the module's Setup\Patch\Data namespace to be auto picked up by Magento when we run bin/magento setup:upgrade. Note - data patches are applied only once.
class ExternalId implements DataPatchInterface {}
  • Inject all necessary dependencies in the constructor as follows
public function __construct(
    \Magento\Framework\Setup\ModuleDataSetupInterface $moduleDataSetup,
    \Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory,
    \Magento\Customer\Model\ResourceModel\Attribute $attributeResource,
    \Psr\Log\LoggerInterface $logger
) {
    $this->moduleDataSetup = $moduleDataSetup;
    $this->customerSetup = $customerSetupFactory->create(['setup' => $moduleDataSetup]);
    $this->attributeResource = $attributeResource;
    $this->logger = $logger;
}
  • Start and end the setup process inside the apply method. This performs a couple of prerequisites in MySQL, like turning off foreign key checks and modifying the SQL mode.
public function apply()
{
    $this->moduleDataSetup->getConnection()->startSetup();

    ... insert patch code from the below steps here ...

    $this->moduleDataSetup->getConnection()->endSetup();
}
  • We will be using constants from the \Magento\Customer\Api\CustomerMetadataInterface class, so make sure to import that.

Create the attribute

$this->customerSetup->addAttribute(
    CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
    'manick_external_id',
    [
        'label' => 'External ID',
        'required' => 0,
        'position' => 100,
        'system' => 0,
        'user_defined' => 1,
        'is_used_in_grid' => 1,
        'is_visible_in_grid' => 1,
        'is_filterable_in_grid' => 1,
        'is_searchable_in_grid' => 1,
    ]
);

The arguments passed to addAttribute are - entity type ID, attribute code, and attribute settings.

Now let's go over the attribute settings here. By default, an attribute is created as a text HTML input for the browser and as a varchar database value type. Since we want a simple text field, we do not need to specify the already default value. However, we need to provide the following settings:

setting meaning value
label user facing label string
required is the attribute a required field? 0 or 1
position sort order for the form it appears in number
system is the attribute system-defined? this should always be 0 for custom attributes 0 or 1
user_defined is the attribute user-defined? this should always be 1 for custom attributes 0 or 1
is_used_in_grid is the attribute used in the customer grid? this needs to be 1 for any of the below values to take effect 0 or 1
is_visible_in_grid is the attribute visible in the customer grid as a column? 0 or 1
is_filterable_in_grid is the attribute filterable in the customer grid under the filter settings? 0 or 1
is_searchable_in_grid is the attribute searchable in the customer grid using the search box? 0 or 1

Add the attribute to set & group

$this->customerSetup->addAttributeToSet(
    CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
    CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
    null,
    'manick_external_id'
);

The arguments passed to addAttributeToSet are - entity type ID, attribute set ID, attribute group ID, and attribute code.

There is only one attribute set and group for the customer entity. Setting the group ID as null, allows Magento to pick up the default attribute group ID for the customer entity's only attribute set.

Display the attribute in forms

$attribute = $this->customerSetup->getEavConfig()->getAttribute(
    CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
    'manick_external_id'
);

$attribute->setData('used_in_forms', [
    'adminhtml_customer'
]);

This fetches the newly created attribute's model, and makes the attribute visible in the Magento Admin's customer edit page under the Account Information section.

Save the attribute

Finally, let's save our modified attribute model using its resource model.

$this->attributeResource->save($attribute);

Finishing touches

Before we finish, let's implement the other two method stubs:

public static function getDependencies(): array
{
    return [];
}

public function getAliases(): array
{
    return [];
}

The getDependencies method should return a list of patches which should be run before this patch, if any dependencies exist. The getAliases method should return the list of previous patch names in case this was renamed so that Magento knows not to run this again since patches are run only once.

Now, let's run bin/magento setup:upgrade and see our new customer attribute in the Magento Admin.

  • Magento Admin's customer form
    customer attribute form field

  • Magento Admin's customer grid
    customer attribute grid column

Please refer to my GitHub gist for the full data patch code.

21