I created a module to add custom attributes to customer and display one of them on registration form. I am using Magento 2.2.
Adding the attribute via InstallData works properly. However I am not able to display one attribute on the registration form.
Here is what I did:
1.)
I copied the corresponding block from
vendor\magento\module-customer\Block\Widget\Name.php
to
\app\code\MyNamespace\CustomerAttributes\Block\Name.php
and commented out "$this->setTemplate('widget/name.phtml');" since selecting the template didn't work this, even though I tried, changed path, tried again ...
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ namespace MyNamespace\CustomerAttributes\Plugin\Block; use Magento\Customer\Api\AddressMetadataInterface; use Magento\Customer\Api\CustomerMetadataInterface; use Magento\Customer\Api\Data\CustomerInterface; use Magento\Customer\Helper\Address as AddressHelper; use Magento\Customer\Model\Options; use Magento\Framework\View\Element\Template\Context; use Magento\Customer\Block\Widget\AbstractWidget; /** * Widget for showing customer name. * * @method CustomerInterface getObject() * @method Name setObject(CustomerInterface $customer) * * @SuppressWarnings(PHPMD.DepthOfInheritance) */ class Name extends AbstractWidget { /** * @var AddressMetadataInterface */ protected $addressMetadata; /** * @var Options */ protected $options; /** * @param Context $context * @param AddressHelper $addressHelper * @param CustomerMetadataInterface $customerMetadata * @param Options $options * @param AddressMetadataInterface $addressMetadata * @param array $data */ public function __construct( Context $context, AddressHelper $addressHelper, CustomerMetadataInterface $customerMetadata, Options $options, AddressMetadataInterface $addressMetadata, array $data = [] ) { $this->options = $options; parent::__construct($context, $addressHelper, $customerMetadata, $data); $this->addressMetadata = $addressMetadata; $this->_isScopePrivate = true; } /** * @return void */ public function _construct() { parent::_construct(); // default template location //$this->setTemplate('widget/name.phtml'); } /** * Can show config value * * @param string $key * @return bool */ protected function _showConfig($key) { return (bool)$this->getConfig($key); } /** * Can show prefix * * @return bool */ public function showPrefix() { return $this->_isAttributeVisible('prefix'); } /** * Define if prefix attribute is required * * @return bool */ public function isPrefixRequired() { return $this->_isAttributeRequired('prefix'); } /** * Retrieve name prefix drop-down options * * @return array|bool */ public function getPrefixOptions() { $prefixOptions = $this->options->getNamePrefixOptions(); if ($this->getObject() && !empty($prefixOptions)) { $oldPrefix = $this->escapeHtml(trim($this->getObject()->getPrefix())); $prefixOptions[$oldPrefix] = $oldPrefix; } return $prefixOptions; } /** * Define if middle name attribute can be shown * * @return bool */ public function showMiddlename() { return $this->_isAttributeVisible('middlename'); } /** * Define if middlename attribute is required * * @return bool */ public function isMiddlenameRequired() { return $this->_isAttributeRequired('middlename'); } /** * Define if suffix attribute can be shown * * @return bool */ public function showSuffix() { return $this->_isAttributeVisible('suffix'); } /** * Define if suffix attribute is required * * @return bool */ public function isSuffixRequired() { return $this->_isAttributeRequired('suffix'); } /** * Retrieve name suffix drop-down options * * @return array|bool */ public function getSuffixOptions() { $suffixOptions = $this->options->getNameSuffixOptions(); if ($this->getObject() && !empty($suffixOptions)) { $oldSuffix = $this->escapeHtml(trim($this->getObject()->getSuffix())); $suffixOptions[$oldSuffix] = $oldSuffix; } return $suffixOptions; } /** * Class name getter * * @return string */ public function getClassName() { if (!$this->hasData('class_name')) { $this->setData('class_name', 'customer-name'); } return $this->getData('class_name'); } /** * Container class name getter * * @return string */ public function getContainerClassName() { $class = $this->getClassName(); $class .= $this->showPrefix() ? '-prefix' : ''; $class .= $this->showMiddlename() ? '-middlename' : ''; $class .= $this->showSuffix() ? '-suffix' : ''; return $class; } /** * {@inheritdoc} */ protected function _getAttribute($attributeCode) { if ($this->getForceUseCustomerAttributes() || $this->getObject() instanceof CustomerInterface) { return parent::_getAttribute($attributeCode); } try { $attribute = $this->addressMetadata->getAttributeMetadata($attributeCode); } catch (\Magento\Framework\Exception\NoSuchEntityException $e) { return null; } if ($this->getForceUseCustomerRequiredAttributes() && $attribute && !$attribute->isRequired()) { $customerAttribute = parent::_getAttribute($attributeCode); if ($customerAttribute && $customerAttribute->isRequired()) { $attribute = $customerAttribute; } } return $attribute; } /** * Retrieve store attribute label * * @param string $attributeCode * @return string */ public function getStoreLabel($attributeCode) { $attribute = $this->_getAttribute($attributeCode); return $attribute ? __($attribute->getStoreLabel()) : ''; } /** * Get string with frontend validation classes for attribute * * @param string $attributeCode * @return string */ public function getAttributeValidationClass($attributeCode) { return $this->_addressHelper->getAttributeValidationClass($attributeCode); } /** * @param string $attributeCode * @return bool */ private function _isAttributeRequired($attributeCode) { $attributeMetadata = $this->_getAttribute($attributeCode); return $attributeMetadata ? (bool)$attributeMetadata->isRequired() : false; } /** * @param string $attributeCode * @return bool */ private function _isAttributeVisible($attributeCode) { $attributeMetadata = $this->_getAttribute($attributeCode); return $attributeMetadata ? (bool)$attributeMetadata->isVisible() : false; } }
2.)
I created
app\code\MyNamespace\CustomerAttributes\view\frontend\layout\name_and_company.xml
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd"> <referenceBlock name="content"> <block template="nameandcompany.phtml" class="MyNamespace\CustomAttributes\Block\Name" name="myNamespace_nameandcompany"/> </referenceBlock> </page>
3.)
I copied the phtml I want to edit from
\vendor\magento\module-customer\view\frontend\templates\widget\name.phtml
to
\app\code\MyNamespace\CustomerAttributes\view\frontend\templates\nameandcompany.xml
<?php /** * Copyright © Magento, Inc. All rights reserved. * See COPYING.txt for license details. */ // @codingStandardsIgnoreFile /** @var \Magento\Customer\Block\Widget\Name $block */ /* <?= $block->getLayout()->createBlock('Magento\Customer\Block\Widget\Name') ->setObject($block->getAddress()) ->toHtml() ?> For checkout/onepage/shipping.phtml: <?= $block->getLayout()->createBlock('Magento\Customer\Block\Widget\Name') ->setObject($block->getAddress()) ->setFieldIdFormat('shipping:%s') ->setFieldNameFormat('shipping[%s]') ->toHtml() ?> */ $prefix = $block->showPrefix(); $middle = $block->showMiddlename(); $suffix = $block->showSuffix(); ?> <?php if (($prefix || $middle || $suffix) && !$block->getNoWrap()): ?> <div class="field required fullname <?= $block->escapeHtmlAttr($block->getContainerClassName()) ?>"> <label for="<?= $block->escapeHtmlAttr($block->getFieldId('firstname')) ?>" class="label"> <span><?= $block->escapeHtml(__('Name')) ?></span> </label> <div class="control"> <fieldset class="fieldset fieldset-fullname"> <div class="fields"> <?php endif; ?> <?php if ($prefix): ?> <div class="field field-name-prefix<?php if ($block->isPrefixRequired()) echo ' required' ?>"> <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId('prefix')) ?>"> <span><?= $block->escapeHtml($block->getStoreLabel('prefix')) ?></span> </label> <div class="control"> <?php if ($block->getPrefixOptions() === false): ?> <input type="text" id="<?= $block->escapeHtmlAttr($block->getFieldId('prefix')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('prefix')) ?>" value="<?= $block->escapeHtmlAttr($block->getObject()->getPrefix()) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('prefix')) ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass('prefix')) ?>" <?php if ($block->isPrefixRequired()) echo ' data-validate="{required:true}"' ?>> <?php else: ?> <select id="<?= $block->escapeHtmlAttr($block->getFieldId('prefix')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('prefix')) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('prefix')) ?>" class="<?= $block->escapeHtmlAttr($block->getAttributeValidationClass('prefix')) ?>" <?php if ($block->isPrefixRequired()) echo ' data-validate="{required:true}"' ?> > <?php foreach ($block->getPrefixOptions() as $_option): ?> <option value="<?= $block->escapeHtmlAttr($_option) ?>"<?php if ($block->getObject()->getPrefix() == $_option): ?> selected="selected"<?php endif; ?>> <?= $block->escapeHtml(__($_option)) ?> </option> <?php endforeach; ?> </select> <?php endif; ?> </div> </div> <?php endif; ?> <div class="field field-name-firstname required"> <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId('firstname')) ?>"> <span><?= $block->escapeHtml($block->getStoreLabel('firstname')) ?></span> </label> <div class="control"> <input type="text" id="<?= $block->escapeHtmlAttr($block->getFieldId('firstname')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('firstname')) ?>" value="<?= $block->escapeHtmlAttr($block->getObject()->getFirstname()) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('firstname')) ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass('firstname')) ?>" <?php if ($block->getAttributeValidationClass('firstname') == 'required-entry') echo ' data-validate="{required:true}"' ?>> </div> </div> <?php if ($middle): ?> <?php $isMiddlenameRequired = $block->isMiddlenameRequired(); ?> <div class="field field-name-middlename<?= $isMiddlenameRequired ? ' required' : '' ?>"> <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId('middlename')) ?>"> <span><?= $block->escapeHtml($block->getStoreLabel('middlename')) ?></span> </label> <div class="control"> <input type="text" id="<?= $block->escapeHtmlAttr($block->getFieldId('middlename')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('middlename')) ?>" value="<?= $block->escapeHtmlAttr($block->getObject()->getMiddlename()) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('middlename')) ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass('middlename')) ?>" <?= $isMiddlenameRequired ? ' data-validate="{required:true}"' : '' ?>> </div> </div> <?php endif; ?> <div class="field field-name-lastname required"> <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId('lastname')) ?>"> <span><?= $block->escapeHtml($block->getStoreLabel('lastname')) ?></span> </label> <div class="control"> <input type="text" id="<?= $block->escapeHtmlAttr($block->getFieldId('lastname')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('lastname')) ?>" value="<?= $block->escapeHtmlAttr($block->getObject()->getLastname()) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('lastname')) ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass('lastname')) ?>" <?php if ($block->getAttributeValidationClass('lastname') == 'required-entry') echo ' data-validate="{required:true}"' ?>> </div> </div> <?php if ($suffix): ?> <div class="field field-name-suffix<?php if ($block->isSuffixRequired()) echo ' required' ?>"> <label class="label" for="<?= $block->escapeHtmlAttr($block->getFieldId('suffix')) ?>"> <span><?= $block->escapeHtml($block->getStoreLabel('suffix')) ?></span> </label> <div class="control"> <?php if ($block->getSuffixOptions() === false): ?> <input type="text" id="<?= $block->escapeHtmlAttr($block->getFieldId('suffix')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('suffix')) ?>" value="<?= $block->escapeHtmlAttr($block->getObject()->getSuffix()) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('suffix')) ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAttributeValidationClass('suffix')) ?>" <?php if ($block->isSuffixRequired()) echo ' data-validate="{required:true}"' ?>> <?php else: ?> <select id="<?= $block->escapeHtmlAttr($block->getFieldId('suffix')) ?>" name="<?= $block->escapeHtmlAttr($block->getFieldName('suffix')) ?>" title="<?= $block->escapeHtmlAttr($block->getStoreLabel('suffix')) ?>" class="<?= $block->escapeHtmlAttr($block->getAttributeValidationClass('suffix')) ?>" <?php if ($block->isSuffixRequired()) echo ' data-validate="{required:true}"' ?>> <?php foreach ($block->getSuffixOptions() as $_option): ?> <option value="<?= $block->escapeHtmlAttr($_option) ?>"<?php if ($block->getObject()->getSuffix() == $_option): ?> selected="selected"<?php endif; ?>> <?= $block->escapeHtml(__($_option)) ?> </option> <?php endforeach; ?> </select> <?php endif; ?> </div> </div> <?php endif; ?> <?php if (($prefix || $middle || $suffix) && !$block->getNoWrap()): ?> </div> </fieldset> </div> </div> <?php endif; ?>
So far I didn't edit the phtml. I just wanted to see whether it's working.
I exceuted
php magento cache:clean
php magento setup:upgrade
php magento setup:di:compile
But the registration page does not get rendered completetly. This is the entire html source code:
<form class="form create account form-create-account" action="https://www.mydomain.com/customer/account/createpost/" method="post" id="form-validate" enctype="multipart/form-data" autocomplete="off"> <input name="form_key" type="hidden" value="TVEgWcjFb03Nzz1L" /> <fieldset class="fieldset create info"> <legend class="legend"><span>Personal Information</span></legend><br> <input type="hidden" name="success_url" value=""> <input type="hidden" name="error_url" value="">
It seems to render until it needs the copied phtml. What am I doing wrong?
Hi @Damian Culotta,
here it is:
<?php namespace MyNamespace\CustomerAttributes\Setup; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; use Magento\Framework\Setup\InstallDataInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Eav\Model\AttributeRepository; class InstallData implements InstallDataInterface { private $eavSetupFactory; private $attributeRepository; public function __construct(EavSetupFactory $eavSetupFactory, AttributeRepository $attributeRepository) { $this->eavSetupFactory = $eavSetupFactory; $this->attributeRepository = $attributeRepository; } public function install( ModuleDataSetupInterface $setup, ModuleContextInterface $context ) { $setup->startSetup(); $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); // add customer_company to customer $eavSetup->removeAttribute(\Magento\Customer\Model\Customer::ENTITY, 'company_name'); $eavSetup->addAttribute( \Magento\Customer\Model\Customer::ENTITY, 'company_name', [ 'type' => 'text', 'label' => 'Company Name', 'input' => 'text', 'required' => true, 'visible' => true, 'filterable_in_search' => true, 'is_visible_on_front' => 0, 'filterable' => true, 'system' => 0, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, 'position' => 200, 'sort_order' => 200 ] ); $companyNameAttribute = $this->attributeRepository->get('customer', 'company_name'); $setup->getConnection() ->insertOnDuplicate( $setup->getTable('customer_form_attribute'), [ ['form_code' => 'adminhtml_customer', 'attribute_id' => $companyNameAttribute->getId()], ['form_code' => 'customer_account_create', 'attribute_id' => $companyNameAttribute->getId()], ['form_code' => 'customer_account_edit', 'attribute_id' => $companyNameAttribute->getId()], ] ); $companyNameAttribute->save(); // add erp_customer_id to customer $eavSetup->removeAttribute(\Magento\Customer\Model\Customer::ENTITY, 'erp_customer_id'); $eavSetup->addAttribute( \Magento\Customer\Model\Customer::ENTITY, 'erp_customer_id', [ 'type' => 'text', 'label' => 'Company ID', 'input' => 'text', 'required' => false, 'visible' => true, 'filterable_in_search' => true, 'is_visible_on_front' => 0, 'filterable' => true, 'system' => 0, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, 'position' => 210, 'sort_order' => 210 ] ); $erpCustomerIdAttribute = $this->attributeRepository->get('customer', 'erp_customer_id'); $setup->getConnection() ->insertOnDuplicate( $setup->getTable('customer_form_attribute'), [ ['form_code' => 'adminhtml_customer', 'attribute_id' => $erpCustomerIdAttribute->getId()], ] ); $erpCustomerIdAttribute->save(); // add erp_customer_classification to customer $eavSetup->removeAttribute(\Magento\Customer\Model\Customer::ENTITY, 'erp_customer_classification'); $eavSetup->addAttribute( \Magento\Customer\Model\Customer::ENTITY, 'erp_customer_classification', [ 'type' => 'text', 'label' => 'Customer Classification', 'input' => 'text', 'required' => false, 'visible' => true, 'filterable_in_search' => true, 'is_visible_on_front' => 0, 'filterable' => true, 'system' => 0, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL, 'position' => 220, 'sort_order' => 220 ] ); $erpCustomerClassificationAttribute = $this->attributeRepository->get('customer', 'erp_customer_classification'); $setup->getConnection() ->insertOnDuplicate( $setup->getTable('customer_form_attribute'), [ ['form_code' => 'adminhtml_customer', 'attribute_id' => $erpCustomerClassificationAttribute->getId()], ] ); $erpCustomerClassificationAttribute->save(); $setup->endSetup(); } }
I added three attributes, but I only want to add the attribute company_name to the registration form.
Hi @Jens_Noma,
Can you check adding something like this to your attribute installer?
/** @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); //Add custom attribute $customerSetup->addAttribute(\Magento\Customer\Model\Customer::ENTITY, 'your_new_attribute', [ 'type' => 'varchar', 'label' => 'Your New Attribute', 'input' => 'text', 'required' => false, 'visible' => true, 'user_defined' => true, 'sort_order' => 20, 'position' => 20, 'system' => 0 ]); $attribute = $customerSetup->getEavConfig()->getAttribute(\Magento\Customer\Model\Customer::ENTITY, 'your_new_attribute') ->addData([ 'attribute_set_id' => $attributeSetId, 'attribute_group_id' => $attributeGroupId, 'used_in_forms' => ['adminhtml_customer'], ]); $attribute->save();
I was able to use the factories becasue my __constructor has:
public function __construct(\Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory, \Magento\Eav\Model\Entity\Attribute\SetFactory $attributeSetFactory) { $this->customerSetupFactory = $customerSetupFactory; $this->attributeSetFactory = $attributeSetFactory; }
Instead this value:
'used_in_forms' => ['adminhtml_customer'],
Change that for:
'customer_account_create', 'customer_account_edit'
Or you can use the 3 values to show the attribute on all forms.