21
Magento 2 Customer Attributes
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. :)
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
- 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'sSetup\Patch\Data
namespace to be auto picked up by Magento when we runbin/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.
$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
|
$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.
$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.
Finally, let's save our modified attribute model using its resource model.
$this->attributeResource->save($attribute);
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.
Please refer to my GitHub gist for the full data patch code.
21