Form construction
The Hyvä Checkout Form API enables developers to construct interactive forms from reusable components including fields, buttons, elements, and accessories. Forms built with the Hyvä Checkout Form API consist of multiple parts, where each part can be customized or extended through dedicated classes, factories, and modifiers. This construction approach allows third-party modules to modify forms without template overrides.
Form elements are layout blocks with phtml templates that are bound to a form element object, while accessories are injectable templates that render alongside elements. Form construction leverages save services to persist data, modifiers to customize behavior, and factories to dynamically create form components.
Save services
Class: \Hyva\Checkout\Model\Form\AbstractEntityFormSaveService
Save Services in the Hyvä Checkout Form API are responsible for writing form data to a specific destination such as the database. Every Hyvä Checkout form is obligated to include a Save Service that implements the logic for persisting form values.
Save Services handle the final step of form submission—taking validated form data and storing it in the appropriate location.
Modifiers
Interface: \Hyva\Checkout\Model\Form\EntityFormModifierInterface
Form modifiers in Hyvä Checkout allow developers to customize form behavior based on specific hooks triggered during initialization and submit handling processes. Modifiers provide a structured way to inject custom logic without editing core form classes.
Modifiers necessitate a single apply() method where modification callbacks can be registered using the registerModificationListener() method. Existing modifiers can be removed using the unregisterModificationListener() method. Hooks are dispatched using the dispatchModificationHook() method to trigger registered callbacks at specific lifecycle points.
Elements
Class: \Hyva\Checkout\Model\Form\AbstractEntityFormElement
Form elements in Hyvä Checkout are layout blocks with templates that automatically bind to element objects. Elements provide the visual components of a form such as buttons, links, and promotional banners. Elements cannot hold values themselves—use fields for data capture.
The element object can consist of basic classes provided with Hyvä Checkout or custom injected objects. Elements provide structure and interactivity but delegate data storage to fields.
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
The Clickable element is an abstraction layer for HTML elements that respond to click events. A clickable element doesn't necessarily have to be a <button> element—it can be any interactive element that users can click.
Clickable elements serve as the foundation for buttons and other interactive form components in Hyvä Checkout.
Button
Class: \Hyva\Checkout\Model\Form\EntityFormElement\Button
The Button element extends the Clickable abstraction and renders as an HTML <button> element by default. Buttons in Hyvä Checkout can function as regular buttons or serve as abstraction layers for specialized buttons like submit buttons.
This example demonstrates creating a button element using the Hyvä Checkout Form API:
<?php
// Create a button element with a custom label
$form->createElement(Hyva\Checkout\Model\Form\EntityFormElement\Button::class, [
'data' => [
'label' => 'Open modal'
]
])
Submit
Class: \Hyva\Checkout\Model\Form\EntityFormElement\Submit
The Submit button is a specialized derivation of a regular button element. Submit buttons in Hyvä Checkout require additional specifications such as a default "Submit" label, a different element type, and a unique layoutAlias to enable specific template assignment.
This example shows how to create a submit button with a custom label:
<?php
// Create a submit button element for form submission
$form->createElement(Hyva\Checkout\Model\Form\EntityFormElement\Submit::class, [
'data' => [
'label' => 'Submit form'
]
])
URL
Class: \Hyva\Checkout\Model\Form\EntityFormElement\Url
The URL element possesses its own layoutAlias (denoted by the url alias) enabling specific template application. URL elements in Hyvä Checkout can contain a value accessed through the src attribute, which can be set using setValue(string $url).
This example demonstrates creating a URL element that renders as a link:
<?php
// Create a URL element with destination and label
$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 in Hyvä Checkout are extensions of elements specifically designed to hold values. Fields are more elaborate than basic elements and contain a more specific set of capabilities including validation, data binding, and value storage.
Fields represent the data capture components of Hyvä Checkout forms—use fields when you need to collect user input.
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. Input fields in Hyvä Checkout can be used for multiple purposes—form modifiers can adjust the input type to email, tel, or other HTML5 input types as needed.
Input fields provide the most flexible field type for text-based data capture in Hyvä Checkout forms.
Factories
Form factories in Hyvä Checkout enable dynamic creation of form elements and fields through dedicated factory classes. 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 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.
Custom factories can be injected into your form object to fulfill diverse requirements 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.
This example demonstrates injecting a global custom factory into the Hyvä Checkout Form API:
<!-- 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>
Once the custom factory is registered, access it through the form object:
Elements factory
The elements factory in Hyvä Checkout generates form element objects based on provided mappings. To instruct the create() method on what to produce, specify a $name such as "button" or "promotional_banner".
Element mappings are defined within etc/frontend/di.xml to associate element names with their implementation classes:
<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>
After mapping the element, create it through the form object:
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).
Direct object assignment in Hyvä Checkout requires additional options where the default element renderer will search for either a layout alias or the ID data value. This can be accomplished through two approaches:
Approach 1: By layout alias
Define a getLayoutAlias() method that returns a searchable identifier:
<?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';
}
}
Approach 2: By element ID
Pass an ID in the element data that the renderer can use for template matching:
<?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 in Hyvä Checkout constructs field objects using a fallback mechanism to determine the appropriate implementation. 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.
The resolution process for determining the appropriate field object 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.
The field factory fallback mechanism searches in this order:
| 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>
Create the field through the form object with the field name and input type:
System elements
Hyvä Checkout includes multiple fields and elements out of the box, some specifically designed to handle EAV attributes. The system elements provide common form components for address forms and customer data capture.
| 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.