Skip to content

Form rendering

Form rendering in Hyvä Checkout displays forms on the frontend through templates, renderers, and Layout XML configuration. Forms can be powered by different drivers such as Magewire or AlpineJS. Each form element and field has its own dedicated template, with fields automatically defaulting to the "text" renderer when no specific template is assigned.

The Hyvä Checkout rendering system separates presentation from logic, allowing developers to customize form appearance through Layout XML without modifying component code.

Renderers

Renderers in Hyvä Checkout are responsible for transforming form elements and fields into HTML output. The checkout contains a wrapper component called Main, and all components that can contain forms are housed within the Main component. Forms consist of elements and fields, and the checkout can only render the elements and fields that fall within the Main component.

Elements and fields each have their own renderer. For elements, the renderer is typically Hyva\Checkout\Model\Form\EntityFormElement\Renderer\Element, and for fields, the renderer is Hyva\Checkout\Model\Form\EntityField\EntityFieldRenderer. Custom renderers can be defined for each element or field when specialized rendering logic is required.

A renderer can be accessed within a PHTML file using the getRenderer() function. The renderer provides a render() method that expects an instance of the element itself. A shortcut for this is $element->render(), which automatically performs the rendering for you.

This example demonstrates rendering all form elements in a loop:

<?php foreach ($form->getElements() as $element): ?>
    <?= /* @noEscape */ $element->render() ?>
<?php endforeach ?>

The renderer object is important because, in addition to rendering the element or field itself, the renderer can also be used within the template to render so-called "accessories"—helper templates that provide labels, tooltips, and other supporting markup.

Layout XML

Important

Everything comes together under a Magento concept that you should be familiar with: Layout XML. It is therefore important to understand how both elements, fields, and accessories can be rendered.

Layout XML in Hyvä Checkout controls form rendering by mapping elements and fields to their templates. In addition to the layout handle hyva_checkout_components, an additional handle is also loaded in the form of hyva_checkout_form_elements. The latter is responsible for assigning templates to form elements and therefore the entity-form.field-renderers and entity-form.element-renderers blocks are made available.

These two renderer blocks will be searched by the renderer for a matching child block alias based on the element or field being rendered.

Elements

The element renderer searches for block aliases in entity-form.element-renderers to determine which template to use. The element renderer will, by default, search for the following block aliases:

Level Alias Since
2 {form_namespace}.{element_id} 1.1.0
1 {element_id} 1.1.0

This example demonstrates creating a "banner" element and binding it to a template:

$form->addElement($form->createElement('promotion-banner'));

Bind the element to a specific template using Layout XML:

<!-- File: view/frontend/layout/hyva_checkout_form_elements.xml -->

<referenceBlock name="entity-form.element-renderers">
    <block name="form-element.promotion-banner"
           as="promotion-banner"
           template="My_Example::form/element/promotion-banner.phtml"
    />
</referenceBlock>

Block (element) aliases

As you can see, the element renderer is looking for two specific aliases: {form_namespace}.{element_id} and {element.id}. Which of these aliases to choose depends on the situation and the uniqueness of the element ID.

In particular cases, you can imagine a global component renderer that may be used in multiple forms. In such scenarios, it may be the case that for one of those forms, the element should behave the same, but the HTML should differ. This means creating a template to serve as a generic base, and make a copy of it to apply modifications for the form-specific version.

Fields

Field rendering in Hyvä Checkout uses a more extensive fallback mechanism than elements. The field renderer will search for a wider range of block aliases in entity-form.field-renderers:

Level Alias Since
7 {form_namespace}.{field_id}.{input_type} 1.1.0
6 {form_namespace}.{field_id} 1.1.0
5 {field_id}.{input_type} 1.1.0
4 {field_id} 1.1.0
3 {form_namespace}.{input_type} 1.1.0
2 {input_type} 1.1.0
1 text 1.1.0

Due to this fallback mechanism, every field will eventually default to a text type renderer, which inherently includes a template:

<block name="form-field.text" as="text" template="Hyva_Checkout::form/field/text.phtml"/>

Hyvä Checkout also offers default templates for the most common input types such as select, checkbox, hidden, and password.

Accessories

Accessories in Hyvä Checkout are helper templates that can be utilized across multiple elements or fields without having a direct relationship with the component itself. Accessories provide reusable markup for labels, tooltips, comments, and container wrappers.

Accessories are specific to either elements or fields, and they cannot be interchanged. In other words, if you have a label accessory for a field, you cannot use it within an element, and vice versa.

Accessories are prefixed with an accessory. alias prefix. This means that if you have a "label" accessory, you add a child block to either the entity-form.element-renderers or entity-form.field-renderers, where you alias it with accessory.label.

This example demonstrates defining accessories for fields and elements:

<!-- File: view/frontend/layout/hyva_checkout_form_elements.xml -->

<referenceBlock name="entity-form.field-renderers">
    <!-- A form field label template. -->
    <block name="form-field.global.label"
           template="Hyva_Checkout::form/element/html/label.phtml"
           as="accessory.label"
    />
</referenceBlock>

<referenceBlock name="entity-form.element-renderers">
    <!-- A before accessory rendered with a container template that lets a block function as a container. -->
    <block name="form-element.global.before"
           template="Hyva_Checkout::form/element/html/container.phtml"
           as="accessory.before"
    >
        <!-- Will automatically render like it sits within a container. -->
        <block name="element-script-block"
               template="My_Example::page/js/element-script.phtml"/>
    </block>
</referenceBlock>

Accessory blocks will automatically receive the element or field block as the element data attribute. In some cases there is also a parent attribute available.

Accessories act as subordinates to the main element or field. They serve as support, where it's a best practice to always add a renderBefore() and renderAfter() within your component. This provides the opportunity to subsequently insert additional blocks in a controlled manner without having to override the template.

This example shows how to render accessories within a field template:

<?= $element->getRenderer()->renderBefore($element) ?>
<?= $element->getRenderer()->renderLabel($element) ?>
<input <?= /* @noEscape */ $element->renderAttributes($escaper) ?> />
<?= $element->getRenderer()->renderComment($element) ?>
<?= $element->getRenderer()->renderAfter($element) ?>

Accessory specific render method

Do not confuse the render methods as magic methods, where we have opted to only include predefined methods for the accessories provided out of the box. We have, however, opened up the renderAccessory() method to enable you to render your own custom accessory (since 1.1.11).

Hyvä Checkout provides the following global accessories out of the box:

Accessory Element Field Template Method
before Yes Yes Hyva_Checkout::form/element/html/container.phtml renderBefore
after Yes Yes Hyva_Checkout::form/element/html/container.phtml renderAfter
label No Yes Hyva_Checkout::form/element/html/label.phtml renderLabel
comment No Yes Hyva_Checkout::form/element/html/comment.phtml renderComment
tooltip No Yes Hyva_Checkout::form/element/html/tooltip.phtml renderTooltip

Forms

Forms in Hyvä Checkout are PHTML files that receive a form object through a ViewModel or Magewire component. The form template iterates over elements and renders each one using its assigned renderer.

This example demonstrates a form powered by a Magewire component of type Hyva\Checkout\Magewire\Component\AbstractForm:

<block name="example.form"
       as="form"
       template="Hyva_Checkout::magewire/component/form.phtml"
>
    <arguments>
        <argument name="magewire" xsi:type="object">
            \My\Example\Magewire\Checkout\ExampleForm
        </argument>
    </arguments>
</block>

The corresponding form template renders the form element and iterates over all form elements:

<?php

/** @var \Magento\Framework\Escaper $escaper */
/** @var \Hyva\Checkout\Magewire\Component\AbstractForm $magewire */

$form = $magewire->getPublicForm();
?>

<form <?= /* @noEscape */ $form->renderAttributes($escaper) ?>>
    <?php foreach ($form->getElements() as $element): ?>
        <?php if ($element->canRender()): ?>
            <?= /* @noEscape */ $element->render() ?>
        <?php endif ?>
    <?php endforeach ?>
</form>

For Magewire-driven forms, you may also utilize our pre-built form template, which can be found at Hyva_Checkout::magewire/component/form.phtml

As you can see, a form is very basic and primarily set up so that the form can be modified from modifiers without the need to override the template.

Advanced rendering

Advanced rendering techniques in Hyvä Checkout enable highly specific customizations without template overrides. Typically, many of your needs are already addressed by the out-of-the-box element templates we offer. However, behind the scenes, we've incorporated numerous unique rendering patterns to circumvent the need for template overrides in cases where a highly specific alteration is required.

Targeted accessories

Targeted accessories in Hyvä Checkout allow you to customize accessories for specific form elements without creating dedicated templates. Picture a scenario where you simply need to alter the label of a particular element or field, such as within the shipping address form, without the hassle of assigning a dedicated template for that element. In such cases, we've implemented a pattern enabling you to pinpoint a specific form element and designate a child block aliased as label.

This example demonstrates targeting a specific field to customize its label accessory:

<!-- File: view/frontend/layout/hyva_checkout_form_elements.xml -->

<referenceBlock name="entity-form.field-renderers">

    <!-- Target field by alias {form-namespace}.{field-id}. -->
    <block name="specific-shipping-firstname" as="shipping.firstname">

        <!-- Assign a custom template just for the label. -->
        <block name="specific-shipping-firstname.label"
               as="label"
               template="My_Example::form/shipping/field/firstname-label.phtml"
        />
    </block>
</referenceBlock>

Removing the label block will allow the field to automatically fallback onto the accessory.label template.

The label rendering would resemble something along these lines:

<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Hyva\Checkout\Model\Form\EntityField\AbstractEntityField $element */
/** @var \Magento\Framework\Escaper $escaper */

// The element object is automatically being passed along to the block object.
$element = $block->getData('element');
?>

<span class="font-bold text-blue-700 italic">
    <?= $escaper->escapeHtml($element->getLabel()) ?>
</span>

The example above illustrates a form field, where elements can also be targeted by referencing the block entity-form.element-renderers.

Wrapping a form element

Wrapping form elements in Hyvä Checkout allows you to add container markup around fields without duplicating template code. Imagine a situation where you have a "Save to address book" checkbox, which is rendered using our native Hyva_Checkout::form/field/checkbox.pthml template. However, you want to enclose it with a <div class="bg-gray-100">...</div> without having to rewrite all the code for the checkbox. Doing so not only leads to a significant amount of duplicated code but also introduces inconsistencies across your checkboxes.

Therefore, we devised a solution that allows you to render the standard checkbox field within your custom template for the checkbox.

This example shows how to wrap a checkbox field with a custom container:

<!-- File: view/frontend/layout/hyva_checkout_form_elements.xml -->

<referenceBlock name="entity-form.field-renderers">

    <!-- Target checkbox field by alias {form-namespace}.{field-id}.{input-type} -->
    <block name="save_address_book_shipping"
           as="shipping.save.checkbox"
           template="My_Example::form/shipping/field/save-address-book.phtml"
    />
</referenceBlock>

The checkbox rendering would resemble something along these lines:

<?php
/** @var \Magento\Framework\View\Element\Template $block */
/** @var \Hyva\Checkout\Model\Form\EntityField\AbstractEntityField $element */
/** @var \Magento\Framework\Escaper $escaper */

// The element object is automatically being passed along to the block object.
$element = $block->getData('element');
?>

<div class="bg-gray-100 px-6 py-4">
    <?= $element->getRenderer()->renderWithTemplate('Hyva_Checkout::form/field/checkbox.phtml', $element) ?>
</div>
Why opt for the specific use of the .checkbox type specification?

Employing the shipping.save.checkbox alias helps prevent a scenario where a different input type is unintentionally rendered using this custom template. In simpler terms, if a form modifier were implemented to change the input type to a select or button, it would default to its standard rendering. Failing to narrow it down specifically to .checkbox could result in a select being rendered as a checkbox, as you can envision by examining the HTML structure.

Conditional displayment

Conditional rendering in Hyvä Checkout allows form elements to appear based on system configuration settings. Consider a scenario where a form element or field is meant to be displayed only if a specific system configuration setting permits it. This can be achieved by incorporating a standard ifconfig statement within your form element block.

This example illustrates a method to display or conceal the street field based on a setting:

<!-- File: view/frontend/layout/hyva_checkout_form_elements.xml -->

<block name="form-field.street"
       as="street"
       template="Hyva_Checkout::form/field/street.phtml"
       ifconfig="hyva_themes_checkout/developer/address_form/use_street_renderer"
/>

The street field will seamlessly revert to the standard text field type if the config doesn't exist or returns false.

Street field variants

Street field variants in Hyvä Checkout provide flexible layouts for multi-line street addresses. By default, Magento adopts the approach of incorporating multiple (1-4) street fields to accommodate various address formats, recognizing that some addresses require more than just a street and a house number. Determining the number of fields displayed is a native Magento setting. However, fine-tuning their presentation to match your preferences is a feature provided by Hyvä Checkout out of the box since version 1.1.1.

Since its release, in addition to the default rendering that displays the fields underneath each other, we've introduced two new options: "Two Column Grid" and "One Column Row." You can find these options within the Hyvä Checkout system settings > Developer > Address Forms.