Step completion
Checkout component evaluation
Checkouts consist of one or more steps, for example "Shipping", "Billing" and "Review".
It is important a user is only able to proceed to the next step once the previous ones are complete.
For example, on the shipping step in the default checkout, a shipping address needs to be entered and a shipping method needs to be selected before a customer may proceed to the payment step.
The shipping address form and the shipping method selection are two distinct components.
Only when both are completed the customer can go on to the next step.
To enable this behavior, components can implement the \Hyva\Checkout\Model\Magewire\Component\EvaluationInterface
interface.
<?php
namespace Hyva\Checkout\Model\Magewire\Component;
interface EvaluationInterface
{
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface;
}
The returned value can be an error message, an error event, a blocking or a success result as described below.
The evaluation process
Under the hood a EvaluationInterface
component is evaluated each time it is instantiated, that is, during each request.
Even when a component is rendered during the initial preceding request, it is evaluated.
The returned EvaluationResult
data is part of the component state.
The result data is stored in the JavaScript component representation.
However, the evaluation results are only stored on the component - they are not immediately processed on the front end.
Initially, no evaluation events are dispatched and no evaluation messages are displayed.
Only when the "Proceed" button is clicked, the results of the last evaluation for each component are processed.
This is when the JavaScript evaluation result events are dispatched and messages are displayed.
Component evaluation results
Evaluation result instances are created by the \Hyva\Checkout\Model\Magewire\Component\EvaluationResultFactory
, which is conveniently passed as an argument to the evaluateCompletion()
method.
The EvaluationResultFactory
provides a number of factory methods to indicate if a component is "complete":
createSuccess()
createErrorMessage()
createErrorEvent()
createBlocking()
Each of those returns an EvaluationResultInterface
implementation matching the method name.
For example, the following component will never evaluate successfully.
Every time a visitor wants to proceed to the next step, a message is displayed and they are never allow to continue.
<?php
namespace My\Module\Magewire;
use Hyva\Checkout\Model\Magewire\Component\EvaluationInterface;
use Hyva\Checkout\Model\Magewire\Component\EvaluationResultFactory;
use Hyva\Checkout\Model\Magewire\Component\EvaluationResultInterface;
use Magewirephp\Magewire\Component;
class Example extends Component implements EvaluationInterface
{
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface
{
return $factory->createErrorMessage((string) __('YOU SHALL NOT PASS!'));
}
}
The following event types are available for components:
Evaluation\Success
To indicate the customer is allowed to proceed to the next step, a component can return a \Hyva\Checkout\Model\Magewire\Component\Evaluation\Success
instance.
It can be instantiated by calling Component\EvaluationResultFactory::createSuccess()
.
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface
{
return $factory->createSuccess();
}
Evaluation\ErrorMessage
Returning an ErrorMessage
evaluation result will prevent users from proceeding to the next step.
Additionally, the specified error message will be displayed as a flash message above the checkout.
(To display a message on the component, see the description for the ErrorEvent
result below)
It is instantiated as follows:
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface
{
return $factory->createErrorMessage((string) __('Enter the correct value'));
}
Two optional additional arguments are available to specify a different message type (for example 'warning'
) and the duration in milliseconds.
Evaluation\ErrorEvent
Returning an ErrorEvent
evaluation result will prevent users from proceeding to the next step.
The JavaScript event evaluation:error
will be dispatched together with the specified data as the payload.
This is useful to display error messages on a component itself, rather than displaying the message at the top of the page.
This result type is created by calling Component\EvaluationResultFactory::createErrorEvent()
.
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface
{
return $factory->createErrorEvent([
'error' => __('Foo is too short. Please check the frub is blarg.')
]);
}
Since all ErrorEvents dispatch the same event evaluation:error
, observing components need to check if the event was dispatched for themselves by checking if the component ID matches.
For example, the template code could look something like this:
<div>
<div x-data="{
message: '',
isForMe(event) {
return event.detail.component && event.detail.component.id === '<?= $escaper->escapeJs($block->getNameInLayout()) ?>';
}
}"
x-on:evaluation:error.window="isForMe($event) && (message = $event.detail.error)"
>
<template x-if="message.length > 0">
<div class="bg-red-500 rounded-lg p-4 text-white" x-text="message"></div>
</template>
</div>
</div>
The event needs to be captured on the window scope (not the component itself)
Instead of observing the generic evaluation:error
event and checking if the component ID matches, it is also possible to dispatch custom events instead:
public function evaluateCompletion(EvaluationResultFactory $factory): EvaluationResultInterface
{
return $factory->createErrorEvent(
['error' => 'Ping'],
'my-custom-evaluation-failure-event'
);
}
For custom events the frontend code can be simplified, since the component ID check can be omitted:
<div>
<div x-data="{ message: '' }"
@my-custom-evaluation-failure-event.window="message = $event.detail.error"
>
<template x-if="message.length > 0">
<div class="bg-red-500 rounded-lg p-4 text-white" x-text="message"></div>
</template>
</div>
</div>
Evaluation\Blocking
As long as an EvaluationInterface
component returns a Blocking
evaluation result, the button to proceed to the next step is disabled.
This is used for example on the Payment step, to force a user provide the required data for the selected payment method, before they can click the "Place Order" button.
The Blocking
result type is created by calling Component\EvaluationResultFactory::createBlocking()
.