Each address form in the checkout is based upon the EAV entity customer_address
. By adding a new EAV attribute to this entity, you can easily add your own form field to the checkout. On this page, you will see the steps involved for this.
First of all, you will need to add the EAV attribute to the database. This is usually done through a Patch
class, but you can also use a third party extension for this (like the Amasty Customer Attributes) extension. The following shows an example Patch
class that creates an example EAV attribute example
:
namespace YireoTraining\Example\Setup\Patch\Data;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Customer\Model\ResourceModel\Attribute as AttributeResourceModel;
class AddExampleFieldToCustomerAddress implements DataPatchInterface
{
public function __construct(
private EavSetupFactory $eavSetupFactory,
private ModuleDataSetupInterface $moduleDataSetup,
private EavConfig $eavConfig,
private AttributeResourceModel $attributeResourceModel
) {
}
public static function getDependencies()
{
return [];
}
public function getAliases()
{
return [];
}
public function apply()
{
$attributeCode = 'example';
$attributeLabel = 'Example';
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(
'customer_address',
$attributeCode,
[
'input' => 'text',
'is_visible_in_grid' => false,
'visible' => true,
'user_defined' => true,
'is_filterable_in_grid' => false,
'system' => false,
'label' => $attributeLabel,
'position' => 10,
'type' => 'varchar',
'is_used_in_grid' => false,
'required' => false,
]
);
$attribute = $this->eavConfig->getAttribute('customer_address', $attributeCode);
return $this;
}
}
Once this class has been created as part of a new module, running bin/magento setup:upgrade
should create this EAV-attribute as a row in the database table eav_attribute
(amongst others).
This example can be further extended by adding the attribute to frontend and backend forms. See the Yireo_LokiCheckoutCoc
module for a full example.
The above adds an attribute to the customer_address
entity. While attributes of this entity are automatically copied into a quote address, it doesn't save the quote address attribute value yet. To save a value to the database, we can either add a new column to the quote_address
table and sales_order_address
table, or we could save things via an extension attribute.
In this example, we simply extend the tables. This can be done with the following db_schema.xml
file:
<?xml version="1.0"?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="quote_address" resource="default">
<column xsi:type="varchar" name="example" nullable="true" length="128" comment="Example"/>
</table>
<table name="sales_order_address" resource="default">
<column xsi:type="varchar" name="example" nullable="true" length="128" comment="Example"/>
</table>
</schema>
After running bin/magento setup:upgrade
, the tables should be altered.
Next, let's create a new block for this EAV attribute via an XML layout file loki_checkout_block_shipping_address.xml
:
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:View/Layout:etc/page_configuration.xsd">
<body>
<referenceBlock name="loki-checkout.shipping.address.form">
<block
name="loki-checkout-example.shipping.address.example" as="example"
template="Yireo_LokiFieldComponents::form/field.phtml"/>
</referenceBlock>
</body>
</page>
Note that the PHTML template Yireo_LokiFieldComponents::form/field.phtml
is reused here. Normally, this is what you want: A lot of changes you might want to make to this field can be made via either the ViewModel or the XML layout.
To transform the block above into a full-blown LokiComponent, we need one more step. We need to add a new component to the etc/loki_components.xml
file. The name
of that component is identical to the name
of the block. The group
called shippingAddressFields
includes a definition for a Context
class, ViewModel
class and Repository
class which we can reuse here. These classes automatically match the name of the block (in our case example
) with the attribute code of your EAV attribute (in our case example
).
<?xml version="1.0" encoding="UTF-8" ?>
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Yireo_LokiComponents:etc/loki_components.xsd">
<component
name="loki-checkout-example.shipping.address.example"
group="shippingAddressFields"
/>
</components>
etc/loki_components.xml
of the core module Yireo_LokiCheckout
to see the definition of the group shippingAddressFields
.It might be that the original classes need to be modified. In this case, you would create your own classes, extending upon the original. This is actually done for all of the core component classes: Even though they don't have anything specific, they allow for an easier extensibility.
A more customized version could look the following:
<?xml version="1.0" encoding="UTF-8" ?>
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Yireo_LokiComponents:etc/loki_components.xsd">
<component
name="loki-checkout-example.shipping.address.example"
group="shippingAddressFields"
viewModel="Yireo\LokiCheckoutExample\Component\Checkout\Address\Example\ExampleViewModel"
repository="Yireo\LokiCheckoutExample\Component\Checkout\Address\Example\ExampleRepository"
/>
</components>
You could also add <target/>
elements, so that other HTML elements are refreshed when your component is being updated. You could add validators and filters as well.