Skip to content

Implementing EntityFormModifierInterface

The EntityFormModifierInterface provides the primary method for customizing Hyvä Checkout address forms. Form modifiers allow you to add, remove, and manipulate form elements in shipping and billing address forms without conflicting with other modules.

Most Hyvä Checkout form customizations begin by implementing the EntityFormModifierInterface. This interface uses an event-driven approach where you register callbacks that execute at specific points in the form lifecycle.

The EntityFormModifierInterface Structure

The EntityFormModifierInterface defines a single method that registers your customization callbacks with the form instance.

<?php
namespace Hyva\Checkout\Model\Form;

interface EntityFormModifierInterface
{
    public function apply(EntityFormInterface $form): EntityFormInterface;
}

The apply method receives a form instance and returns it after registering your modification callbacks. Hyvä Checkout calls this method during form initialization, giving your modifier the opportunity to hook into form lifecycle events.

Registering Modification Callbacks

Form modifiers register callbacks as "modification listeners" that execute at specific form lifecycle events. The registration happens in your apply method implementation.

Each modification listener registration requires three components:

  1. Identifier string - A unique identifier for your callback, used to reference or remove the listener later
  2. Hook name - The form lifecycle event when the callback should execute
  3. Callback - Any PHP callable that implements your customization logic

Basic Modifier Implementation Example

This example demonstrates a complete form modifier implementation. The modifier registers a callback that executes during the form:build lifecycle event.

<?php

namespace Hyva\Example\Model\FormModifier;

use Hyva\Checkout\Model\Form\EntityFormInterface;
use Hyva\Checkout\Model\Form\EntityFormModifierInterface;

class ExampleBillingAddressModifier implements EntityFormModifierInterface
{
    public function apply(EntityFormInterface $form): EntityFormInterface
    {
        // Register a callback for the form:build event
        $form->registerModificationListener(
            'myCallbackIdentifier',        // unique identifier for this callback
            'form:build',                  // the form lifecycle hook name
            [$this, 'applyMyModification'] // the callback method
        );

        return $form;
    }

    public function applyMyModification(EntityFormInterface $form)
    {
        // Your customization logic executes here when the form:build event fires
        // Example: modify field properties, add/remove fields, etc.
    }
}

The registerModificationListener method adds your callback to the form's event system. When the specified hook event occurs, Hyvä Checkout invokes your callback with arguments appropriate to that hook.

Helper Methods for Field Modifications (Since 1.1.21)

Hyvä Checkout version 1.1.21 introduced convenience methods that simplify common field modification patterns. These helper methods automatically check for field existence before applying modifications.

Recommended Approach for Field Modifications

Use modifyField(), modifyFields(), modifyElement(), or modifyElements() instead of manually checking field existence. These methods eliminate boilerplate code and prevent errors when fields don't exist.

The helper methods accept a callback that receives the field or element only if it exists in the form:

public function applyMyModification(EntityFormInterface $form)
{
    // Modify a single field if it exists
    $form->modifyField('telephone', function ($telephoneField) {
        $telephoneField->setLabel('Phone Number');
    });

    // Modify multiple fields
    $form->modifyFields(['firstname', 'lastname'], function ($field) {
        $field->addValidationRule('alpha-spaces-only');
    });
}

These methods prevent the need to write repetitive existence checks before every field modification.

Registering Your Form Modifier with Dependency Injection

Form modifiers are registered through Magento's dependency injection system in etc/frontend/di.xml. The registration specifies which form (shipping or billing address) receives the modifier.

This example registers a modifier for the billing address form:

etc/frontend/di.xml
<type name="Hyva\Checkout\Model\Form\EntityForm\EavAttributeBillingAddressForm">
    <arguments>
        <argument name="entityFormModifiers" xsi:type="array">
            <item name="hyva_example" xsi:type="object" sortOrder="1000">
                Hyva\Example\Model\FormModifier\ExampleBillingAddressModifier
            </item>
        </argument>
    </arguments>
</type>

For shipping address modifications, use EavAttributeShippingAddressForm as the type name instead.

When Hyvä Checkout initializes the billing address form, it calls the apply method of all registered modifiers, allowing your callbacks to register with the form's event system.

Understanding sortOrder Priority

The sortOrder attribute controls the execution order of multiple form modifiers. Modifiers execute in ascending sortOrder, meaning lower values execute first.

<item name="early_modifier" xsi:type="object" sortOrder="500">
    <!-- Executes first -->
</item>
<item name="late_modifier" xsi:type="object" sortOrder="1500">
    <!-- Executes last, can override earlier modifiers -->
</item>

Execution order matters when multiple modifiers manipulate the same field properties. Later modifiers can override changes made by earlier modifiers, giving higher sortOrder values higher effective priority.

Default sortOrder Recommendation

Use sortOrder="1000" as your default value. Hyvä Checkout core modifiers use sortOrder values in the 0-999 range, so 1000 ensures your customizations execute after core modifications.

Every item in the entityFormModifiers array should include a sortOrder attribute for reliable ordering. Magento's DI configuration requires explicit sortOrder values to guarantee consistent array ordering.

Resolving Modifier Conflicts

When two modifiers conflict (both setting the same property to different values), assign a higher sortOrder to the modifier that should take precedence:

<!-- Original modifier from third-party module -->
<item name="thirdparty_modifier" xsi:type="object" sortOrder="1000">
    Vendor\Module\Model\FormModifier
</item>

<!-- Your modifier that should override the third-party changes -->
<item name="your_override_modifier" xsi:type="object" sortOrder="1100">
    YourVendor\YourModule\Model\OverrideModifier
</item>

The modifier with sortOrder="1100" executes after the one with sortOrder="1000", allowing it to override any conflicting changes.

Available Form Modification Hooks

Different hook names trigger callbacks at different points in the form lifecycle. The hook name determines when your callback executes and what arguments it receives.

See the Form Modification Hooks reference for a complete table of available hooks, their timing, and callback signatures.