Adding extension attributes

Adding an extension attribute to the LokiCheckout is just as straight-forward as adding an EAV attribute. It is just that your database storage is not running via EAV, but via a customized extension attribute of your choice.

Prerequisites

The following example assumes that you have already added your own etc/extension_attributes.xml file to define a new extension attribute example for the \Magento\Quote\Api\Data\AddressInterface class. Also, we assume that you have found your way to load and save this extension attribute into the quote address, for instance, by adding a DI interceptor plugin to all methods of the \Magento\Quote\Api\CartRepositoryInterface class.

Creating a new block

Let's create a new block for this extension 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.

Defining your component

Within a file etc/loki_components.xml, we are defining our block as a component, including a ViewModel and a repository. The ViewModel is often quite generic, so we simply reuse a common class here. The repository is where the loading and saving of a field occurs - which is specific to our own extension attribute.

<?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="checkout"
        viewModel="Yireo\LokiCheckout\Component\Base\Generic\CheckoutViewModel"
        repository="Yireo\LokiCheckoutExample\Component\Checkout\Address\Example\ExampleRepository"
    />
</components>

Note that there is a group checkout used here (it's definition can be found in the etc/loki_components.xml file of the core module Yireo_LokiCheckout) even though that group doesn't do much. You could also skip the group and add your own context instead.

Adding a repository class

The repository class Yireo\LokiCheckoutExample\Component\Checkout\Address\Example\ExampleRepository simply fetches the extension attribute from the quote and sets it back into the quote. Instead of creating the repository from scratch, you can fetch the quote and cart repository from the context.

namespace Yireo\LokiCheckoutExample\Component\Checkout\Example;

use Yireo\LokiFieldComponents\Component\Base\Field\FieldRepository;
use Yireo\LokiCheckout\Component\Base\Generic\CheckoutContext;

/**
 * @method CheckoutContext getContext()
 */
class ExampleRepository extends FieldRepository
{
    public function getValue(): mixed
    {
        return $this->getContext()->getQuote()->getExtensionAttributes()->getExample();
    }

    public function saveValue(mixed $value): void
    {
        $this->getContext()->getQuote()->getExtensionAttributes()->setExample($value);

        $cartRepository = $this->getContext()->getCartRepository();
        $cartRepository->save($quote);
    }
}

Done

Every time that your field is now modified, it should trigger an AJAX call, which is then calling upon your repository class, which then saves things to the database. It should just work :)

Last modified: April 1, 2025