1

I am trying to create a customer attribute but it gives the following error whenever you try to run the setup:upgrade

Attribute with the same code already exists

I see that attribute with such code is already created despite of errors. After x-debugging, I noticed that $attribute->save() is trying to create an attribute instead of updating.

I am using Magento EE 2.1.1. And the module version is changed from 1.0.0 to 1.0.1 for setup upgrade.

File: MagePsycho/Customer/Setup/UpgradeData.php

<?php

namespace MagePsycho\Customer\Setup;

use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Eav\Model\Entity\Attribute\SetFactory as AttributeSetFactory;

/**
 * @codeCoverageIgnore
 */
class UpgradeData implements UpgradeDataInterface
{
    const CUSTOMER_ATTRIBUTE_TWITTER_HANDLE = 'mp_twitter_handle';

    /**
     * @var CustomerSetupFactory
     */
    private $customerSetupFactory;

    /**
     * @var AttributeSetFactory
     */
    private $attributeSetFactory;

    public function __construct(
        CustomerSetupFactory $customerSetupFactory,
        AttributeSetFactory $attributeSetFactory
    ) {
        $this->customerSetupFactory = $customerSetupFactory;
        $this->attributeSetFactory  = $attributeSetFactory;
    }

    /**
     * {@inheritdoc}
     * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
     */
    public function upgrade( ModuleDataSetupInterface $setup, ModuleContextInterface $context )
    {
        $installer = $setup;

        $installer->startSetup();

        if ($context->getVersion()
            && version_compare($context->getVersion(), '1.0.1') < 0) {
            $this->_execUpgrade101($setup);
        }

        $installer->endSetup();
    }

    protected function _execUpgrade101(ModuleDataSetupInterface $setup)
    {
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);
        $attributesInfo = [
            self::CUSTOMER_ATTRIBUTE_TWITTER_HANDLE => [
                'label'     => 'Twitter Handle',
                'type'      => 'varchar',
                'input'     => 'text',
                'source'    => '',
                'required'  => false,
                'visible'   => true,
                'system'    => false,
                'user_defined'  => true,
                'backend'       => '',
                'position'  => 333,
            ]
        ];

        $customerEntity = $customerSetup->getEavConfig()->getEntityType('customer');
        $attributeSetId = $customerEntity->getDefaultAttributeSetId();

        /** @var $attributeSet AttributeSet */
        $attributeSet       = $this->attributeSetFactory->create();
        $attributeGroupId   = $attributeSet->getDefaultGroupId($attributeSetId);

        foreach ($attributesInfo as $attributeCode => $attributeParams) {
            $customerSetup->addAttribute(Customer::ENTITY, $attributeCode, $attributeParams);
        }

        $attribute = $customerSetup->getEavConfig()
            ->getAttribute(Customer::ENTITY, self::CUSTOMER_ATTRIBUTE_TWITTER_HANDLE);
        $attribute->addData([
            'attribute_set_id'      => $attributeSetId,
            'attribute_group_id'    => $attributeGroupId,
            'used_in_forms'         => [
                'adminhtml_customer',
                'adminhtml_checkout'
            ],
        ]);
        $attribute->save();
    }
}

What's wrong with the code?

1
  • i will check the issue at night :)
    – Amit Bera
    Apr 19, 2017 at 16:32

2 Answers 2

2

If you:

  1. Call getAttribute() on Magento 2 Eav Config model for particular entity type,
  2. then add new attribute for that entity type,
  3. then call getAttribute() for that new attribute within same request,

you will not get correct data in response, what makes save() call on such malformed attribute object break with "Attribute with the same code already exists" error. Reason for this is caching inside Eav Config model:

https://github.com/magento/magento2/blob/2.1.11/app/code/Magento/Eav/Model/Config.php#L370-L372

In order to work around this cache, and to avoid "Attribute with the same code already exists" error, one should call clear() method on Eav Config model before second getAttribute() call for entity type in question:

    $attribute = $customerSetup->getEavConfig()
        ->clear() // WILL MAKE getAttribute() REINITIALIZE ATTRIBUTE DATA
        ->getAttribute(Customer::ENTITY, self::CUSTOMER_ATTRIBUTE_TWITTER_HANDLE);
    $attribute->addData([
        'attribute_set_id'      => $attributeSetId,
        'attribute_group_id'    => $attributeGroupId,
        'used_in_forms'         => [
            'adminhtml_customer',
            'adminhtml_checkout'
        ],
    ]);
    $attribute->save();
}

Clearing EavConfig should probably be done automatically within addAttribute call in order to cover this edge case, but sadly it is not at this moment so one must do this manually.

1
  • Yeah ->clear() did the trick. Hats off @Marko
    – MagePsycho
    May 1, 2018 at 11:24
0
  1. Make sure you do not have attribute already created. Check on the db. select * from eav_attribute where attribute_code='mp_twitter_handle';
  2. If the attribute does not exist and you are getting the same error. Try following.


    protected function _execUpgrade101(ModuleDataSetupInterface $setup)
    {
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);

        $customerEntity = $customerSetup->getEavConfig()->getEntityType('customer');
        $attributeSetId = $customerEntity->getDefaultAttributeSetId();

        /** @var $attributeSet AttributeSet */
        $attributeSet = $this->attributeSetFactory->create();
        $attributeGroupId = $attributeSet->getDefaultGroupId($attributeSetId);

        $attributesInfo = [
            self::CUSTOMER_ATTRIBUTE_TWITTER_HANDLE => [
                'label' => 'Twitter Handle',
                'type' => 'varchar',
                'input' => 'text',
                'source' => '',
                'required' => false,
                'visible' => true,
                'system' => false,
                'user_defined' => true,
                'backend' => '',
                'position' => 333,
                'attribute_set_id' => $attributeSetId,
                'attribute_group_id' => $attributeGroupId,
                'used_in_forms' => [
                    'adminhtml_customer',
                    'adminhtml_checkout'
                ],
            ]
        ];



        foreach ($attributesInfo as $attributeCode => $attributeParams) {
            $customerSetup->addAttribute(Customer::ENTITY, $attributeCode, $attributeParams);
        }
    }
3
  • Did you test this code? Are you sure those params for form and attribute set works in a single array?
    – MagePsycho
    Apr 19, 2017 at 16:04
  • @MagePsycho, no I haven't had a chance to check the code. My point was, the attribute create and retrieve happening in the same TA which might cause it not to retrieve the attribute in $attribute = $customerSetup->getEavConfig() ->getAttribute(Customer::ENTITY, self::CUSTOMER_ATTRIBUTE_TWITTER_HANDLE); which cause it to create new attribute during save() Apr 19, 2017 at 17:13
  • This won't create entry in customer_form_attribute and eav_entity_attribute tables.
    – MagePsycho
    Apr 20, 2017 at 7:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.