Magento 2’s Customer Attribute (And Address) Bizarreness

Adding customer attributes to be editable only for admin accounts, you would think would be a straightforward process. Well let me tell you that Magento 2 has other ideas!

Firstly, adding to forms. You’ll notice that just adding attributes to either adminhtml_customer, or adminhtml_customer_address does nothing in the admin area. For some ungodly reason, to get them to show, attributes must also be added to the customer_account_edit and customer_address_edit forms.

Great! attributes now appear in the admin area, but woe unto thee that thinks we’re done here!

Saving from the Admin Area

Hahaha, expecting them to save… just like that? Fool. Make sure you’ve also set the system key of your attribute to false, otherwise Magento will just ignore them.

They’ll need adding to an attribute set and group for this to work correctly; typically this is just the default of each for the entity type.

So now we should be done… right? RIGHT?! Well, young urchin, try updating a customer account information or address from the frontend of your store. What’s that? Hahaha, yes, Magento just obliterated your custom customer attribute values. Serves you right for being so optimistic.

Now, you’ll notice, that even though we’ve added the attributes to the customer_account_edit and customer_address_edit forms to get them to show in the admin area, which foolishly in my mind should have no effect there, that do not show in the frontend.

No, if you want them to appear for customers, you’ll have to do more than add the attribute to a form (guffaws). But, for this example, we don’t want them to show in the frontend and certainly don’t want the data to be sent into the flaming abyss by a customer’s saving of them.

The fix, in true Magento style, is to set the visible key to false when creating the attribute, which persists to the customer_eav_attribute‘s is_visible column. Yes… visible prevents Magento from trying to save the data from the frontend. Cue manic laughter from the Magento Devs as they revel in our pain.

Recap

customer_account_edit and customer_address_edit forms – shows the field in the admin area.
is_system – Set to false for Magento to save the value from the admin area.
Add to attribute set and group – Also required to save from the admin area.
visible – Prevents Magento from trying to save the values from the frontend’s customer account section.

I’m not 100% sure what the adminhtml_customer and adminhtml_customer_address forms are actually used for anymore, but I’ll keep them for a laugh, just in case the Magento devs change their minds.

A code snippet:

protected function createCustomerAttributes(
    \Magento\Eav\Setup\EavSetup $eavSetup,
    ModuleDataSetupInterface $setup
) {
    $attributes = [];

    $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);
    $customerEntity = $customerSetup->getEavConfig()->getEntityType(\Magento\Customer\Model\Customer::ENTITY);
    $attributeSetId = $customerEntity->getDefaultAttributeSetId();
    $attributeSet = $this->attributeSetFactory->create();
    $attributeGroupId = $attributeSet->getDefaultGroupId($attributeSetId);

    // ... Add other attributes here 

    $attributes['attribute_code'] = [
        'type'     => 'varchar',
        'label'    => 'Attribute label',
        'input'    => 'text',
        'visible'  => false,
        'system' => false,
        'required' => false,
        'user_defined' => true,
        'position' => 290
    ];

    foreach ($attributes as $code => $attribute) {

        $eavSetup->addAttribute(
            \Magento\Customer\Model\Customer::ENTITY,
            $code,
            $attribute
        );

        $createdAttribute = $this->eavConfig->getAttribute(
            \Magento\Customer\Model\Customer::ENTITY,
            $code
        );

        $createdAttribute->setData('used_in_forms', ['customer_account_edit', 'adminhtml_customer'])
            ->setData('attribute_set_id', $attributeSetId)
            ->setData('attribute_group_id', $attributeGroupId)
            ->save();
    }
}

Come on Magento 2, we need more hoops to jump through than that! It’s like it’s not even trying to be frustrating some days.