Edit in GitHubLog an issue
Thanks to Adarsh Manickam for contributing this topic!

Add a custom text field attribute

This tutorial describes how a developer can create a custom text field attribute for the Customer entity using code. This will reflect in both the Customer Grid and the Customer Form in the Admin.

This Customer attribute will be used to save and view the customer's ID in an external system, as an example. It will be created as an EAV attribute in a data patch. The EAV model allows a developer to add custom functionality to the entities without modifying the core databases and schemas. Data patches are run just once, so this code will create the custom attribute and will never run again, which could cause issues.

Code#

Create the data patch class#

Create a data patch class called ExternalId under the \ExampleCorp\Customer\Setup\Patch\Data namespace. This makes the application execute the data patch automatically when bin/magento setup:upgrade is run. All data patches must implement the \Magento\Framework\Setup\Patch\DataPatchInterface interface.

Copied to your clipboard
1<?php
2
3namespace ExampleCorp\Customer\Setup\Patch\Data;
4
5use \Magento\Framework\Setup\Patch\DataPatchInterface;
6
7class ExternalId implements DataPatchInterface
8{
9 public function apply()
10 {
11 // will be implemented in the next steps.
12 }
13
14 public function getAliases()
15 {
16 // will be implemented in the next steps.
17 }
18
19 public function getDependencies()
20 {
21 // will be implemented in the next steps.
22 }
23}

Inject the dependencies#

The dependencies to the data patch are injected using constructor DI and are listed below:

  • \Magento\Framework\Setup\ModuleDataSetupInterface for initializing and ending the setup.

  • \Magento\Customer\Setup\CustomerSetupFactory for creating a model of \Magento\Customer\Setup\CustomerSetup which is required to add the custom attribute.

  • \Magento\Customer\Model\ResourceModel\Attribute aliased as AttributeResource for saving the attribute after adding custom data to it.

  • \Psr\Log\LoggerInterface for logging exceptions thrown during the execution.

    Copied to your clipboard
    1/**
    2 * Constructor
    3 *
    4 * @param ModuleDataSetupInterface $moduleDataSetup
    5 * @param CustomerSetupFactory $customerSetupFactory
    6 * @param AttributeResource $attributeResource
    7 * @param LoggerInterface $logger
    8 */
    9public function __construct(
    10 ModuleDataSetupInterface $moduleDataSetup,
    11 CustomerSetupFactory $customerSetupFactory,
    12 AttributeResource $attributeResource,
    13 LoggerInterface $logger
    14) {
    15 $this->moduleDataSetup = $moduleDataSetup;
    16 $this->customerSetup = $customerSetupFactory->create(['setup' => $moduleDataSetup]);
    17 $this->attributeResource = $attributeResource;
    18 $this->logger = $logger;
    19}

Implement the apply method#

There are five steps in developing a data patch. All the steps below are written inside the apply method.

  1. Starting and ending the setup execution. This turns off foreign key checks and sets the SQL mode.

    Copied to your clipboard
    1$this->moduleDataSetup->getConnection()->startSetup();
    2
    3/*
    4 Attribute creation code must be run between these two lines
    5 to ensure that the attribute is created smoothly.
    6 */
    7
    8$this->moduleDataSetup->getConnection()->endSetup();
  2. Add the text field customer attribute with the required settings.

    The third parameter for addAttribute is an array of settings required to configure the attribute. Passing an empty array uses all the default values for each possible setting. To keep the code to a minimum, just declare the settings needing to be overridden and the rest of the settings will be used from the defaults. The settings overrides can be done as described below.

    For creating a simple text field, it is not necessary to override the settings for backend (database field type) and input (frontend HTML input type), as they default to varchar and text respectively.

    The \Magento\Customer\Api\CustomerMetadataInterface interface contains constants like the customer entity's code and the default attribute set code, which can be referenced.

    Copied to your clipboard
    1$this->customerSetup->addAttribute(
    2 CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, // entity type code
    3 'externalcorp_external_id', // unique attribute code
    4 [
    5 'label' => 'External ID',
    6 'required' => 0,
    7 'position' => 200,
    8 'system' => 0,
    9 'user_defined' => 1,
    10 'is_used_in_grid' => 1,
    11 'is_visible_in_grid' => 1,
    12 'is_filterable_in_grid' => 1,
    13 'is_searchable_in_grid' => 1,
    14 ]
    15);
    Setting KeyDescription
    labelExternal ID - Label for displaying the attribute value
    required0 - Attribute will be an optional field in the customer form
    position200 - Sort order in the customer form
    system0 - Not a system-defined attribute
    user_defined1 - A user-defined attribute
    is_used_in_grid1 - Ready for use in the customer grid
    is_visible_in_grid1 - Visible in the customer grid
    is_filterable_in_grid1 - Filterable in the customer grid
    is_searchable_in_grid1 - Searchable in the customer grid
  3. Add attribute to an attribute set and group.

    There is only one attribute set and group for the customer entity. The default attribute set ID is a constant defined the CustomerMetadataInterface interface and setting the attribute group ID to null makes the application use the default attribute group ID for the customer entity.

    Copied to your clipboard
    1$this->customerSetup->addAttributeToSet(
    2 CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, // entity type code
    3 CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER, // attribute set ID
    4 null, // attribute group ID
    5 'externalcorp_external_id' // unique attribute code
    6);
  4. Make the attribute visible in the customer form.

    Copied to your clipboard
    1// Get the newly created attribute's model
    2$attribute = $this->customerSetup->getEavConfig()
    3 ->getAttribute(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'externalcorp_external_id');
    4
    5// Make attribute visible in Admin customer form
    6$attribute->setData('used_in_forms', [
    7 'adminhtml_customer'
    8]);
    9
    10// Save modified attribute model using its resource model
    11$this->attributeResource->save($attribute);
  5. Gracefully handle exceptions.

    Copied to your clipboard
    1try {
    2 // All the code inside the apply method goes into the try block.
    3} catch (Exception $exception) {
    4 $this->logger->err($exception->getMessage());
    5}

Implement rest of the interface#

This data patch does not have any other patch as a dependency, and this data patch was not renamed earlier, so both getDependencies and getAliases can return an empty array.

Copied to your clipboard
1public static function getDependencies(): array
2{
3 return [];
4}
5
6public function getAliases(): array
7{
8 return [];
9}

Execute the data patch#

Run bin/magento setup:upgrade from the project root to execute the newly added data patch.

  • The attribute is created in the customer form under the Account Information section.

    Custom attribute in the customer form

  • The attribute is displayed in the customer grid.

    Custom attribute in the customer grid

Code reference#

Copied to your clipboard
1<?php declare(strict_types=1);
2
3namespace ExampleCorp\Customer\Setup\Patch\Data;
4
5use Exception;
6use Psr\Log\LoggerInterface;
7use Magento\Customer\Api\CustomerMetadataInterface;
8use Magento\Customer\Model\ResourceModel\Attribute as AttributeResource;
9use Magento\Customer\Setup\CustomerSetup;
10use Magento\Customer\Setup\CustomerSetupFactory;
11use Magento\Framework\Setup\ModuleDataSetupInterface;
12use Magento\Framework\Setup\Patch\DataPatchInterface;
13
14/**
15 * Creates a customer attribute for managing a customer's external system ID
16 */
17class ExternalId implements DataPatchInterface
18{
19 /**
20 * @var ModuleDataSetupInterface
21 */
22 private $moduleDataSetup;
23
24 /**
25 * @var CustomerSetup
26 */
27 private $customerSetup;
28
29 /**
30 * @var AttributeResource
31 */
32 private $attributeResource;
33
34 /**
35 * @var LoggerInterface
36 */
37 private $logger;
38
39 /**
40 * Constructor
41 *
42 * @param ModuleDataSetupInterface $moduleDataSetup
43 * @param CustomerSetupFactory $customerSetupFactory
44 * @param AttributeResource $attributeResource
45 * @param LoggerInterface $logger
46 */
47 public function __construct(
48 ModuleDataSetupInterface $moduleDataSetup,
49 CustomerSetupFactory $customerSetupFactory,
50 AttributeResource $attributeResource,
51 LoggerInterface $logger
52 ) {
53 $this->moduleDataSetup = $moduleDataSetup;
54 $this->customerSetup = $customerSetupFactory->create(['setup' => $moduleDataSetup]);
55 $this->attributeResource = $attributeResource;
56 $this->logger = $logger;
57 }
58
59 /**
60 * Get array of patches that have to be executed prior to this.
61 *
62 * Example of implementation:
63 *
64 * [
65 * \Vendor_Name\Module_Name\Setup\Patch\Patch1::class,
66 * \Vendor_Name\Module_Name\Setup\Patch\Patch2::class
67 * ]
68 *
69 * @return string[]
70 */
71 public static function getDependencies(): array
72 {
73 return [];
74 }
75
76 /**
77 * Get aliases (previous names) for the patch.
78 *
79 * @return string[]
80 */
81 public function getAliases(): array
82 {
83 return [];
84 }
85
86 /**
87 * Run code inside patch
88 */
89 public function apply()
90 {
91 // Start setup
92 $this->moduleDataSetup->getConnection()->startSetup();
93
94 try {
95 // Add customer attribute with settings
96 $this->customerSetup->addAttribute(
97 CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
98 'externalcorp_external_id',
99 [
100 'label' => 'External ID',
101 'required' => 0,
102 'position' => 100,
103 'system' => 0,
104 'user_defined' => 1,
105 'is_used_in_grid' => 1,
106 'is_visible_in_grid' => 1,
107 'is_filterable_in_grid' => 1,
108 'is_searchable_in_grid' => 1,
109 ]
110 );
111
112 // Add attribute to default attribute set and group
113 $this->customerSetup->addAttributeToSet(
114 CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER,
115 CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER,
116 null,
117 'externalcorp_external_id'
118 );
119
120 // Get the newly created attribute's model
121 $attribute = $this->customerSetup->getEavConfig()
122 ->getAttribute(CustomerMetadataInterface::ENTITY_TYPE_CUSTOMER, 'externalcorp_external_id');
123
124 // Make attribute visible in Admin customer form
125 $attribute->setData('used_in_forms', [
126 'adminhtml_customer'
127 ]);
128
129 // Save attribute using its resource model
130 $this->attributeResource->save($attribute);
131 } catch (Exception $e) {
132 $this->logger->err($e->getMessage());
133 }
134
135 // End setup
136 $this->moduleDataSetup->getConnection()->endSetup();
137 }
138}
Was this helpful?
  • Privacy
  • Terms of Use
  • Do not sell my personal information
  • AdChoices
Copyright © 2022 Adobe. All rights reserved.