7

I was able to add a custom attribute called 'Username' to the Magento Customer model.

The install-function in InstallData.php looks like this:

public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
    $this->logger->info("Username data installed.");

    /** @var CustomerSetup $customerSetup */
    $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);

    $customerSetup->addAttribute(Customer::ENTITY, 'username', [
        'type' => 'varchar',
        'label' => 'Username',
        'input' => 'text',
        'required' => false,
        'visible' => true,
        'user_defined' => true,
        'unique' => true,
        'required' => true,
        'sort_order' => 1000,
        'position' => 1000,
        'system' => 0,
    ]);

    $attribute = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, 'username')
    ->addData([
        'attribute_set_id' => $attributeSetId,
        'attribute_group_id' => $attributeGroupId,
        'used_in_forms' => ['adminhtml_customer', 'checkout_register', 'customer_account_create', 'customer_account_edit', 'adminhtml_checkout'],
    ]);

    $attribute->save();


}

Everything works correctly, but the 'unique' => true does not work. I found out that this field is not used for customers and uniqueness must be programmed manually.

So I basically want to check the uniqueness of the attribute just before a customer is saved to the database (so just before account creation).

I found this code in app/code/Magento/Customer/Model/ResourceModel/Customer.php in _beforeSave().

    $connection = $this->getConnection();
    $bind = ['email' => $customer->getEmail()];

    $select = $connection->select()->from(
        $this->getEntityTable(),
        [$this->getEntityIdField()]
    )->where(
        'email = :email'
    );
    if ($customer->getSharingConfig()->isWebsiteScope()) {
        $bind['website_id'] = (int)$customer->getWebsiteId();
        $select->where('website_id = :website_id');
    }
    if ($customer->getId()) {
        $bind['entity_id'] = (int)$customer->getId();
        $select->where('entity_id != :entity_id');
    }

    $result = $connection->fetchOne($select, $bind);
    if ($result) {
        throw new AlreadyExistsException(
            __('A customer with the same email already exists in an associated website.')
        );
    }

It is exactly this behaviour for the email that I want to replicate for my username.

Does anyone have an idea how I do this?

Thank you!

4 Answers 4

7

You need to InstallData.php like below code :

public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
{
    $this->logger->info("Username data installed.");

    /** @var CustomerSetup $customerSetup */
    $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);

    $customerSetup->addAttribute(Customer::ENTITY, 'username', [
        'type' => 'varchar',
        'label' => 'Username',
        'input' => 'text',
        'backend' => \Vendor\Module\Model\Attribute\Backend\Username::class,
        'required' => false,
        'visible' => true,
        'user_defined' => true,
        'unique' => true,
        'required' => true,
        'sort_order' => 1000,
        'position' => 1000,
        'system' => 0,
    ]);

    $attribute = $customerSetup->getEavConfig()->getAttribute(Customer::ENTITY, 'username')
    ->addData([
        'attribute_set_id' => $attributeSetId,
        'attribute_group_id' => $attributeGroupId,
        'used_in_forms' => ['adminhtml_customer', 'checkout_register', 'customer_account_create', 'customer_account_edit', 'adminhtml_checkout'],
    ]);

    $attribute->save();


}

Please create Username.php file in \Vendor\Module\Model\Attribute\Backend and code like

<?php 
namespace Vendor\Module\Model\Attribute\Backend;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;

class Username extends \Magento\Eav\Model\Entity\Attribute\Backend\AbstractBackend
{
    /**
     * Generate and set unique Username to customer
     *
     * @param Customer $object
     * @return void
     */
    protected function checkUniqueUsername($object)
    {
        $attribute = $this->getAttribute();
        $entity = $attribute->getEntity();
        $attributeValue = $object->getData($attribute->getAttributeCode());
        $increment = null;
        while (!$entity->checkAttributeUniqueValue($attribute, $object)) {
            throw new NoSuchEntityException(__('Account with Username is already exist'));
        }
    }

    /**
     * Make username unique before save
     *
     * @param Customer $object
     * @return $this
     */
    public function beforeSave($object)
    {
        $this->checkUniqueUsername($object);
        return parent::beforeSave($object);
    }
}
1
  • $attributeValue and $increment variable values never used. Is your code correct? Commented Feb 9, 2022 at 2:32
1

You can use attribute's backend model. For a reference you can have a look at the product's attribute SKU backend model \Magento\Catalog\Model\Product\Attribute\Backend\Sku

1

You can create an event :

<customer_save_before>
                <observers>
                    <mymodule_customer_save_before>
                        <class>mymodule/observer</class>
                        <method>checkUniqueAttribute</method>
                    </mymodule_customer_save_before>
                </observers>
            </customer_save_before>

And in your observer :

/**
     * Check customer attribute which must be unique
     *
     * @param Varien_Event_Observer $observer
     * @return $this
     * @@see customer_save_before
     */
    public function checkUniqueAttribute(Varien_Event_Observer $observer)
    {
        /** @var Mage_Customer_Model_Customer $customer */
        $customer = $observer->getEvent()->getCustomer();
        foreach ($customer->getAttributes() as $attribute) {
            if ($attribute->getIsUnique()) {
                $collection = Mage::getModel('customer/customer')
                    ->getCollection()
                    ->addAttributeToFilter($attribute->getAttributeCode(), $customer->getData($attribute->getAttributeCode()))
                    ->addFieldToFilter('entity_id',   array('neq' => $customer->getId()));

                if ($collection->getSize()) {
                    Mage::throwException(sprintf(
                        'The value %s for %s is already used',
                        $customer->getData($attribute->getAttributeCode()),
                        $attribute->getStoreLabel()
                    ));
                }
            }
        }

        return $this;
1
  • Maxime Huran, Question is related to magento 2. Commented May 10, 2018 at 7:58
0

I think it's not really answering your question but here is a free extension that allow you to use username instead of email. Well exactly this extension allow you tu use both field as credential. (sorry for my english...)

https://www.magentocommerce.com/magento-connect/username-support-login-register-checkout-by-diglin.html

1
  • 1
    This is extension for M1, not M2 Commented May 19, 2017 at 11:30

Your Answer

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

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