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?
Best Answer
If you:
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:
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.