Skip to content

Magewire driven forms

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.

Why use it?

Magewire presents a technique that provides both backend and frontend developers a means to create dynamic forms run via the server by XHR 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

Since version 1.1.13, a new Magewire component has been unveiled, offering 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.

1. Constructing the form component

<?php

class GuestDetails extends \Hyva\Checkout\Magewire\Component\AbstractForm
{
    public bool $customerExists = false;

    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'].
    public function updatedDataEmailAddress($value)
    {
        $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.
    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));
            }
        }
    }
}

2. Apply modifications (optional)

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:

  1. The email field has the autocomplete attribute set to off.
  2. An advanced form validation rule email is set to true.
  3. The password field, along with a comment, is created and added.
  4. The submit button element, along with a custom Submit label, is created and added.

On form:build:magewire:

  1. Utilizing the extended AlpineJS component initMagewireFormForGuestDetails($el, $wire) via a x-data attribute on the form.
  2. Setting a wire:loading.attr attribute as disabled on each field.
  3. Hiding both the submit element and password field if the customer email address is non-existent.
  4. Making the fields compatible with the Magewire component by setting a wire:model.defer attribute as data.{field_id}.
  5. Binding a new submit value onto the Magewire $loader property, displaying Trying to authenticate when the button is clicked.
<?php

class WithAuthenticationModifier extends \Hyva\Checkout\Model\Form\EntityFormModifierInterface
{
    public function construct(
        private readonly \Hyva\Checkout\Model\ConfigData\HyvaThemes\SystemConfigGuestDetails $systemConfigGuestDetails
    ) {
        //
    }

    public function apply(\Hyva\Checkout\Model\Form\EntityFormInterface $form): \Hyva\Checkout\Model\Form\EntityFormInterface
    {
        if (!$this->systemConfigGuestDetails->enableLogin()) {
            return $form;
        }

        $form->registerModificationListener(
            'includeAuthentication',
            'form:init',
            fn ($form) => $this->includeAuthentication($form)
        );

        $form->registerModificationListener(
            'handleMagewireAuthenticationForm',
            'form:build:magewire',
            fn (\Hyva\Checkout\Model\Form\AbstractEntityForm $form, \Hyva\Checkout\Magewire\Checkout\GuestDetails $component)
                => $this->handleMagewireAuthenticationForm($form)
        );

        $form->registerModificationListener(
            'handleMagewireAuthenticationVisibility',
            'form:build:magewire',
            fn (\Hyva\Checkout\Model\Form\AbstractEntityForm $form, \Hyva\Checkout\Magewire\Checkout\GuestDetails $component)
                => $this->handleMagewireAuthenticationVisibility($form, $component)
        );

        $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

3. Assign the modifier to the 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>

4. Make the checkout aware of the component

<!-- 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>

5. Inject the component into the checkout shipping step

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

<body>
    <move element="checkout.guest-details.section"
          destination="column.main"
          before="-"/>
</body>