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.