Skip to content

Form construction

Forms can consist of multiple parts with each part utilizing almost everything you can imagine having on a form. We deliver most of the parts out of the box but also enable developers to come up with their own. These form elements may also have accessories, which are injectable templates that can be rendered (directly or conditionally) alongside them. In other words, a form part is a block within a template while an accessory shares the same structure but can be utilized across multiple parts.

Save services

  • Class: \Hyva\Checkout\Model\Form\AbstractEntityFormSaveService

Save Services are responsible for writing data to a specific source such as the database. Every form is obligated to include a Save Service.

Modifiers

  • Interface: \Hyva\Checkout\Model\Form\EntityFormModifierInterface

Forms can receive various modifications based on specific hooks triggered during both the initialization and submit handling processes. Modifiers necessitate a single apply() method where modification callbacks can be registered using the registerModificationListener() method. Additionally, existing modifiers can be removed using the unregisterModificationListener() method. Hooks are dispatched using the dispatchModificationHook() method.

Elements

  • Class: \Hyva\Checkout\Model\Form\AbstractEntityFormElement

Forms mainly consist of elements. Elements are nothing more than layout blocks with a template that automatically binds to an element object. The element object can consist of a number of basic classes that are provided with the checkout or your own custom injected objects. Elements cannot hold values themselves.

Elements don't inherently know how to be rendered. Therefore, we've developed a specific rendering mechanism that can be controlled via regular Layout XML. Check out the rendering page for comprehensive details on all possibilities.

Clickable (abstract)

  • Class: \Hyva\Checkout\Model\Form\EntityFormElement\Clickable

As the name partly suggests, a clickable is an abstraction layer for HTML elements that are clickable. This doesn't necessarily have to be a <button> element.

Button

  • Class: \Hyva\Checkout\Model\Form\EntityFormElement\Button

Compared to a clickable, a button is indeed of the type button by default but is also derived from the abstract Clickable. A button can simply function as a regular button, but can also serve as an abstraction layer, for example, for a submit button.

<?php

$form->createElement(Hyva\Checkout\Model\Form\EntityFormElement\Button::class, [
    'data' => [
        'label' => 'Open modal'
    ]
])

Submit

  • Class: \Hyva\Checkout\Model\Form\EntityFormElement\Submit

A submit button is again a derivation of a regular button. A submit button requires more specifications, such as a default "Submit" label, a different element type, and contains its own so-called layoutAlias to be able to designate a template very specifically for this type of form element.

<?php

$form->createElement(Hyva\Checkout\Model\Form\EntityFormElement\Submit::class, [
    'data' => [
        'label' => 'Submit form'
    ]
])

URL

  • Class: \Hyva\Checkout\Model\Form\EntityFormElement\Url

Similar to the submit button, a URL element also possesses its own layoutAlias, denoted by the url alias, enabling the application of a specific template. In addition to the alias, the URL element can contain a value accessed through the src attribute, which can be set using setValue(string $url) respectively.

<?php

$form->createElement(Hyva\Checkout\Model\Form\EntityFormElement\Url::class, [
    'data' => [
        'url' => 'https://hyva.io',
        'label' => 'Hyvä Themes'
    ]
])

Fields

  • Class: \Hyva\Checkout\Model\Form\EntityField\AbstractEntityField

Fields are an extension of elements specifically designed to hold values. Fields are more elaborate elements and contain a more specific set of capabilities.

By default, fields automatically fallback onto the text template if no specific template was defined. Each field can be fully customized according to its needs by assigning its own distinctive template via regular Layout XML. Check out the rendering page for comprehensive details on all possibilities.

Input

  • Class: \Hyva\Checkout\Model\Form\EntityField\AbstractEntityField

The input field element is a child of the abstraction layer and should be used in most common cases where a customer needs to fill in a regular text value. The input field can be used for multiple purposes, where a form modifier can adjust the input type if it needs to be changed to email or tel, for example.

Factories

Form elements or fields can be created dynamically by invoking methods such as $form->createField() or $form->createElement(). These methods utilize designated factories to seamlessly generate and provide the appropriate objects.

By default, when utilizing the Hyva\Checkout\Model\Form\AbstractEntityForm, fields are generated using the Hyva\Checkout\Model\Form\EntityFormFieldFactory, while elements are instantiated through Hyva\Checkout\Model\Form\EntityFormFactory. These factories are injected into the constructor via their predetermined names fields and elements.

Furthermore, custom factories can be injected into your form object, empowering you to fulfill diverse requirements simply by defining and subsequently accessing them through $form->getFactoryFor('my_custom_factory'). While the default field and element factories suffice for most scenarios, this flexibility allows for tailored solutions when necessary.

<!-- Inject global custom factory. -->
<type name="Hyva\Checkout\Model\Form\AbstractEntityForm">
    <arguments>
        <argument name="factories" xsi:type="array">
            <item name="my_example_factory" sortOrder="30" xsi:type="object">My\Example\Model\Form\EntityFormFactory\MyExampleFactory</item>
        </argument>
    </arguments>
</type>
$form->getFactoryFor('my_example')->create(...$args);

Elements factory

This factory assumes the role of generating form element objects. To instruct the create() method on what to produce, it's imperative to specify requirements. For an element, this entails providing a $name such as "button" or "promotional_banner".

These mappings are defined within etc/frontend/di.xml:

<type name="Hyva\Checkout\Model\Form\EntityFormFactory">
    <arguments>
        <argument name="elements" xsi:type="array">
            <item name="promotional_banner" xsi:type="string">My\Example\Model\Form\EntityFormElement\PromotionalBanner</item>
        </argument>
    </arguments>
</type>

$form->createElement('promotional_banner', $arguments ?? []);

Direct object assignment

Mapping elements via di.xml is preferable for enhancing the reusability of elements across multiple forms. However, you also have the option to directly assign the object to the method, like $form->createElement(PromotionalBanner::class).

However, this necessitates some additional options where the default element renderer will search for either a layout alias or the ID data value. As an extension, this can be accomplished simply through the following two examples.

  1. By layout alias:

    <?php
    
    namespace My\Example\Model\HyvaCheckout\Form\EntityFormElement;
    
    class PromotionalBanner extends \Hyva\Checkout\Model\Form\AbstractEntityFormElement
    {
        public function getLayoutAlias()
        {
            // The renderer will search for "{form_namespace}.promotional_banner" or "promotional_banner".
            return 'promotional_banner';
        }
    }
    

  2. By element ID:

    <?php
    
    // The renderer will search for "{form_namespace}.{element_id}" or "{element_id}".
    $form->addElement(
        $form->createElement(My\Example\Model\HyvaCheckout\Form\EntityFormElement\PromotionalBanner::class, [
            'data' => [
                'id' => 'promotional_banner'
            ]
        ])
    );
    

More details about rendering can be found here

Fields factory

The field factory operates on the fundamental principle of constructing elements within a given field. While its core concept remains constant, slight variations emerge in the parameters required for the create() method and the mapping of distinct field objects to the factory.

A distinction lies in the resolution process for determining the appropriate object. This parallels the method used to identify the correct render template, as detailed in the documentation on rendering fields. A fallback mechanism is employed to determine which object should be utilized. In cases where injecting a different object for a specific form is feasible, this mechanism ensures continuity by retaining the globally assigned object for all other forms.

Level Alias Since
6 {form_namespace}.{field_name}.{input_type} 1.1.0
5 {form_namespace}.{field_name} 1.1.0
4 {field_name}.{input_type} 1.1.0
3 {field_name} 1.1.0
2 {form_namespace}.{input_type} 1.1.0
1 {input_type} 1.1.0
What is the reason for not having a fallback for elements?

Elements can possess an infinite array of distinctive name options and do not necessitate a specific type. Conversely, types adhere to a predetermined range of HTML input types for selection. Consequently, the capacity to modify objects without the need to alter all instances is only essential for fields.

These mappings are defined within etc/frontend/di.xml:

<type name="Hyva\Checkout\Model\Form\EntityFormFactory">
    <arguments>
        <argument name="elements" xsi:type="array">
            <!-- {field_name} -->
            <item name="delivery_date" xsi:type="string">My\Example\Model\Form\EntityFormField\DeliveryDate</item>
            <!-- {form_namespace}.{field_name} -->
            <item name="example_form.delivery_date" xsi:type="string">My\Example\Model\Form\EntityFormField\ExampleForm\DeliveryDate</item>
        </argument>
    </arguments>
</type>

$form->createField('delivery_date', 'date' $arguments ?? []);

System elements

Hyvä Checkout comes with multiple fields and elements out of the box where some are specifically aimed to handle EAV attributes.

Name Factory Abstraction HTML Element Input Type EAV
submit Element Button Button N/A No
id Field AbstractEntityField Input Hidden No
save Field AbstractEntityField Input Checkbox No
telephone Field EavAttributeField Input Tel Yes
street Field EavAttributeField Input Text Yes
region Field EavAttributeField Input, Select Text, Select Yes
country_id Field EavAttributeField Select Select Yes
postcode Field EavAttributeField Input Text Yes
prefix Field EavAttributeField Select N/A Yes
gender Field EavAttributeField Select N/A Yes

We are exploring the option of renaming the EAV attributes for addresses to include an eav_ prefix in their names, particularly where the current names are too general. We aim to accomplish this without causing any backward incompatible changes, most likely by introducing a dedicated EAV field factory.