Magewire driven forms
Magewire-driven forms in Hyvä Checkout provide server-side form handling with real-time validation and dynamic behavior. You have complete freedom in deciding how to manage your forms. While utilizing Magewire is one option, you could alternatively opt for a more frontend-centric approach such as AlpineJS. Each method has its own advantages and disadvantages, and the ideal choice depends on the specific use case.
Magewire-driven forms combine the power of server-side logic with reactive frontend updates, enabling forms that sync data via Ajax requests as users interact with fields.
Why use it?
Magewire-driven forms in Hyvä Checkout offer a server-centric development approach with built-in reactivity. Magewire presents a technique that provides both backend and frontend developers a means to create dynamic forms run via the server by Ajax requests. This enables direct data syncing to the server, either on field completion or full form submission.
Magewire offers a wide range of features to accommodate various requirements. If you lean towards a server-driven approach, Magewire can be utilized, powering default forms such as shipping and billing forms out of the box.
Example scenario
This example demonstrates the Guest Details component implementation using the Hyvä Checkout Form API with Magewire.
In version 1.1.13 a new Magewire component was introduced, providing extensibility when constructing your form object. This component serves as a robust abstraction, converting your form into a dynamic entity that can be augmented with modifiers. It comes equipped with numerous modification hooks, allowing for the integration of custom hooks as needed to meet specific requirements.
For more global details on constructing a form, please refer to the documentation available here
We'll analyze a minimized version of the Guest Details component that is available from version 1.1.12.
Step 1: Constructing the form component
The Magewire component class handles real-time updates and customer existence checking. This example demonstrates a Guest Details form component that checks if a customer exists when an email is entered:
<?php
class GuestDetails extends \Hyva\Checkout\Magewire\Component\AbstractForm
{
// Public property to track whether the entered email belongs to an existing customer
public bool $customerExists = false;
/**
* Boot method runs during component initialization.
* Checks if the email field has a value and determines customer existence.
*/
public function boot(): void
{
parent::boot();
$email = $this->getForm()->getField(GuestDetailsForm::FIELD_EMAIL);
if ($email && $email->hasValue()) {
$this->handleCustomerExistence($email->getValue());
}
}
/**
* Magewire magic property hook method for $this->data['email_address'].
* This method is called whenever the email_address field value changes.
*
* @param string $value - The new email address value
* @return string - The validated email value
*/
public function updatedDataEmailAddress($value)
{
// Check if the email belongs to an existing customer
$this->handleCustomerExistence($value);
// Submit and save the email value using the forms Save Service.
$this->submit([GuestDetailsForm::FIELD_EMAIL => $value]);
return $value;
}
/**
* Try and toggle the customer existence variable value to either true or false.
* This method validates the email and checks customer existence via the account management API.
*
* @param string $email - The email address to check
*/
private function handleCustomerExistence(string $email): void
{
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
try {
$this->customerExists = !$this->accountManagement->isEmailAvailable($email);
} catch (LocalizedException $exception) {
$this->logger->critical(sprintf('Something went wrong while checking the availability of the email address %s', $email));
}
}
}
}
Step 2: Apply modifications (optional)
Form modifiers for Magewire-driven forms can hook into various lifecycle events to customize behavior. Additionally, we have the option to extend the base form on various triggers including form:init, form:build:magewire, and form:execute:submit:magewire.
For readability purposes, the hook callback methods are omitted in this example. These modifier hooks ensure the following requirements:
On form:init hook:
- The email field has the
autocompleteattribute set tooff. - An advanced form validation rule
emailis set totrue. - The password field, along with a comment, is created and added.
- The submit button element, along with a custom
Submitlabel, is created and added.
On form:build:magewire hook:
- Utilizing the extended AlpineJS component
initMagewireFormForGuestDetails($el, $wire)via ax-dataattribute on the form. - Setting a
wire:loading.attrattribute asdisabledon each field. - Hiding both the submit element and password field if the customer email address is non-existent.
- Making the fields compatible with the Magewire component by setting a
wire:model.deferattribute asdata.{field_id}. - Binding a new
submitvalue onto the Magewire$loaderproperty, displayingTrying to authenticatewhen the button is clicked.
This example demonstrates a form modifier with multiple hooks for the Guest Details form:
<?php
class WithAuthenticationModifier extends \Hyva\Checkout\Model\Form\EntityFormModifierInterface
{
public function construct(
private readonly \Hyva\Checkout\Model\ConfigData\HyvaThemes\SystemConfigGuestDetails $systemConfigGuestDetails
) {
//
}
/**
* Apply modifications to the form by registering hooks.
* This method checks system configuration and registers modification listeners.
*
* @param \Hyva\Checkout\Model\Form\EntityFormInterface $form
* @return \Hyva\Checkout\Model\Form\EntityFormInterface
*/
public function apply(\Hyva\Checkout\Model\Form\EntityFormInterface $form): \Hyva\Checkout\Model\Form\EntityFormInterface
{
// Only apply authentication features if enabled in configuration
if (!$this->systemConfigGuestDetails->enableLogin()) {
return $form;
}
// Register hook to add authentication fields during form initialization
$form->registerModificationListener(
'includeAuthentication',
'form:init',
fn ($form) => $this->includeAuthentication($form)
);
// Register hook to configure Magewire bindings and attributes
$form->registerModificationListener(
'handleMagewireAuthenticationForm',
'form:build:magewire',
fn (\Hyva\Checkout\Model\Form\AbstractEntityForm $form, \Hyva\Checkout\Magewire\Checkout\GuestDetails $component)
=> $this->handleMagewireAuthenticationForm($form)
);
// Register hook to control visibility of authentication fields
$form->registerModificationListener(
'handleMagewireAuthenticationVisibility',
'form:build:magewire',
fn (\Hyva\Checkout\Model\Form\AbstractEntityForm $form, \Hyva\Checkout\Magewire\Checkout\GuestDetails $component)
=> $this->handleMagewireAuthenticationVisibility($form, $component)
);
// Register hook to handle form submission for authentication
$form->registerModificationListener(
'handleAuthenticationSubmitAction',
'form:execute:submit:magewire',
fn (\Hyva\Checkout\Model\Form\AbstractEntityForm $form, \Hyva\Checkout\Magewire\Checkout\GuestDetails $component, $result, $data, $exception)
=> $this->handleAuthenticationSubmitAction($form, $component, $result, $data, $exception)
);
return $form;
}
}
For a comprehensive understanding of the callback methods, please refer to \Hyva\Checkout\Model\Form\EntityFormModifier\GuestDetailsForm\WithAuthenticationModifier
Step 3: Assign the modifier to the form
Form modifiers are assigned to specific forms through dependency injection configuration. This example shows how to assign the authentication modifier to the Guest Details form:
<!-- File: etc/frontend/di.xml -->
<type name="Hyva\Checkout\Model\Form\EntityForm\GuestDetailsForm">
<arguments>
<argument name="entityFormModifiers" xsi:type="array">
<item name="with_authentication_feature" xsi:type="object">Hyva\Checkout\Model\Form\EntityFormModifier\GuestDetailsForm\WithAuthenticationModifier</item>
</argument>
</arguments>
</type>
Step 4: Make the checkout aware of the component
The Magewire component must be registered in the checkout layout to appear on the page. This example demonstrates registering the Guest Details component in the checkout layout:
<!-- File: view/frontend/layout/hyva_checkout_components.xml -->
<referenceBlock name="hyva.checkout.components">
<container name="checkout.guest-details.section">
<block name="checkout.guest-details"
template="Hyva_Checkout::magewire/component/form.phtml"
>
<arguments>
<!-- Transform the form into a Magewire driven form. -->
<argument name="magewire" xsi:type="object">
\Hyva\Checkout\Magewire\Checkout\GuestDetails
</argument>
</arguments>
</block>
</container>
</referenceBlock>
Step 5: Inject the component into the checkout shipping step
Layout moves position the form component within the checkout flow. This example shows how to inject the Guest Details component into the shipping step:
<!-- File: view/frontend/layout/hyva_checkout_default_shipping.xml -->
<body>
<move element="checkout.guest-details.section"
destination="column.main"
before="-"/>
</body>
Auto Saving
Available since 1.1.27
Auto-saving in Hyvä Checkout Magewire-driven forms provides controlled data persistence without continuous synchronization. Until version 1.1.27, auto-saving forms in Hyvä Checkout was an automated process. This ensured that form data was always synchronized with the server, optionally required by other components like the price summary.
While this approach guaranteed all data was readily available at all times, we realized that only a small subset of data is typically necessary for the checkout to function properly. Therefore, we have adjusted this concept, giving developers greater control over which data is stored.
The shift away from forms that automatically save themselves offers several key benefits:
- Greater control over when and where data is stored.
- Improved user experience, with no more unexpected behaviors while filling in forms.
- Frontend JavaScript validation can now occur before any Ajax requests are made.
The solution
Auto-saving in Hyvä Checkout leverages navigation buttons and the Evaluation API for controlled form persistence. Hyvä Checkout relies heavily on navigation buttons to guide users through the checkout process, either advancing them to the next step or completing their order, depending on the configuration. While Hyvä Checkout offers exceptional flexibility, these navigation buttons remain a core element, serving as the anchor point for binding functionality.
From a framework perspective, the Evaluation API integrates with these buttons to trigger asynchronous functionality as needed. This enables Evaluation Navigation Tasks—dynamically injected via PHP code—to be executed when a button is clicked. These tasks take priority, running before any other actions are performed and optionally fail when for instance form validation failed.
Forms built using either Hyva\Checkout\Magewire\Components\AbstractForm or the deprecated Hyva\Checkout\Magewire\Checkout\AddressView\AbstractMagewireAddressForm will automatically include a validation task. This ensures the checkout checks for any pending auto-save actions. If such actions are required, they are executed before the primary action, such as moving to the next step.
This example demonstrates how auto-save validation is injected into the evaluation batch:
<?php
// Check if a submit evaluation result already exists
$evaluationBatch->misses(fn (EvaluationResult $result) => $result->hasAlias('submit'), function (EvaluationResultBatch $batch) {
// Inject a validation task for auto-saving Magewire forms
$batch->push(
$batch->getFactory()
->createValidation('magewire-form')
->withDetails([
'saveAction' => 'autosave' // Specify this is an auto-save action
])
->withAlias('submit') // Alias as 'submit' to prevent duplicate injection
->withStackPosition(100) // Set stack position for execution order
);
});
The code is designed to be self-explanatory, automatically injecting a custom validation when a batch item flagged with submit is missing. However, developers can still inject their own validations, which will take precedence and skip the default injection if they already exist.
For more details about the Evaluation API, please refer to the documentation available here
The wire:auto-save Directive
The wire:auto-save directive in Hyvä Checkout marks form fields for automatic saving when navigation buttons are clicked. As we delegate more responsibility to developers, a way to flag fields for auto-saving became necessary. This is where the new custom wire:auto-save directive comes into play. It can be applied to an input field within a Magewire component, such as a form, alongside the wire:model.defer directive.
These two directives complement each other: wire:model.defer binds the field's data, while wire:auto-save indicates that the field's data doesn't need to be continuously saved to the backend. Instead, it will be automatically saved when the user interacts with one of the primary navigation buttons.
This example demonstrates different auto-save directive configurations:
<form id="shipping">
<!-- Marks this input for inclusion in the form's auto-save navigation task. -->
<input type="text" wire:model.defer="firstname" wire:auto-save/>
<!-- Explicitly links this input to the form with the specified ID for auto-saving. -->
<input type="text" wire:model.defer="firstname" wire:auto-save="shipping"/>
<!-- Configures this input to auto-save itself after the default delay of 1.5 seconds. -->
<input type="text" wire:model.defer="firstname" wire:auto-save.self/>
<!-- Configures this input to auto-save itself after a custom delay of 3 seconds. -->
<input type="text" wire:model.defer="firstname" wire:auto-save.self.3000ms/>
</form>
The Auto Save directive requires a corresponding wire:model.defer directive. If it is missing, a console error will be thrown.
Admin Configurable Auto-Save
Starting from version 1.1.29, administrators can configure auto-save behavior for individual form fields without code changes. Starting from version 1.1.29, we have enhanced the Shipping and Billing Address Forms in the admin area by introducing an "Auto Save" checkbox for each field. When enabled, the field automatically saves upon user input. This feature allows administrators to selectively enable auto-saving for specific fields, providing greater flexibility and control over the checkout process without requiring code modifications.
How to Enable Auto-Save
- In the admin panel, go to Stores > Configuration > Hyvä Themes > Checkout > Components > Shipping Address Form/Billing Address Form
- Locate the Auto Save checkbox for the desired field and toggle it on.
- Save the configuration.
- The field will now automatically save on the frontend.
If the Auto Save checkbox is unchecked and the field still autosaves, a form modifier may be overriding this behavior.