The general structure of the Loki Checkout is created via the XML layout. This makes it easy for newcomers to understand the architecture and it makes changes also very simple. Through the XML layout, blocks are created - the overall checkout, steps, forms, fields, sidebar, etcetera.
Most of the templates are kept small and simple: Logic is moved to PHP classes instead. The only exception here is that most blocks are created with the XML layout, but some child templates are rendered via PHTML (using generic methods like $block->getChildHtml()
or the Loki Checkout specific $blockRenderer
).
Most blocks are created without any custom Block
classes - in other words, in the XML layout, they lack the class
attribute. Therefor the default Template
class is used to allow rendering the HTML via a PHTML template.
For example:
<block name="loki.checkout.generic.title" template="Yireo_LokiCheckout::generic/title.phtml"/>
ViewModels are used to insert data into templates. Often, a single template is making use of multiple ViewModels, each ViewModel having a specific purpose.
A special group of ViewModels is formed by Loki Components. With Loki Components, a block is transformed into a component by adding various parts automatically to the block, including a Viewmodel. Because these ViewModels inherit from an interface ComponentViewModelInterface
and often extend from ComponentViewModel
, they are commonly referred to as Loki Component ViewModel or ComponentViewModel
.
The goal of Loki Components is to streamline the updating of blocks via user input, including client-side validation and server-side validation, including filtering and including refreshing various parts on the page.
In an overview, the following parts are involved with a Loki Component:
etc/loki_components.xml
where the component name matches the block nameWhen a component includes a ViewModel, this ViewModel is automatically inserted into the PHTML templatea as $viewModel
.
There is much more to say about Loki Components. This will be explained through the documentation of this site.
To allow ViewModels to be inserted into PHTML templates easily, Loki makes use of a custom ViewModelFactory
class that is automatically inserted into every PHTML templates (via the observable event view_block_abstract_to_html_before
). Thanks to this factory, ViewModels are easily instantiated in PHTML templates, without requiring XML layout block arguments:
$example = $viewModelFactory->create(ExampleViewModel::class);
This is very similar to the ViewModelRegistry $viewModels
solution of Hyvä. However, with Loki, this factory is also available under other frontends.
$css
, $debug
and other variablesIn a similar way that the $viewModelRegistry
variable is added to PHTML templates, there are also other variables added to the PHTML templates to lighten the load:
$css
executable creates an unique HTML class based on the block name, plus it allows overriding the default CSS classes via the XML layout;$debug
boolean is set when debugging is enabled;$blockRenderer
variable allows for rendering child blocks or any block (see below);A small example with the $css()
variable:
<div class="<?= $css('container') ?>"></div>
^^See Customizing CSS classes
Variables are inserted via the observer \Yireo\LokiCheckout\Observer\AssignAdditionalBlockVariables
.
Yet another variable that is inserted into any PHTML template is the $blockRenderer
variable - an instance of the class \Yireo\LokiCheckout\Util\Block\BlockRenderer
. This utility allows you to render a block identified either by its template or its layout name.
For instance, the loader icon can be easily inserted like this:
<?= $blockRenderer->html('loki.checkout.utils.loader-small') ?>
Similarly, a block can be rendered by passing its template:
<?= $blockRenderer->html('form/field/text/script.phtml', $block) ?>
The utitity has two main rendering methods:
html(string $blockIdentifier, ?AbstractBlock $parentBlock = null, array $data = []): AbstractBlock
get(string $blockIdentifier, ?AbstractBlock $parentBlock = null, array $data = []): AbstractBlock
Both methods boil down to the same thing - instantiating a block. The html()
method actually calls upon the get()
method to retrieve a block object and then calls upon the toHtml()
method of that object (so $block->toHtml()
).
Note that the $blockRenderer
utility does not take the layout hierarchy into account. It assumes that a block is defined somewhere with a specific block name (in the XML layout: <block name="foobar" />
) or that there is a PHTML template somewhere available. This means that you might still want to call upon child blocks regularly as well:
<?= $block->getChildHtml($childAlias) ?>
Every single PHTML template that starts with an HTML element (<div>
, <aside>
, <span>
) with a few exceptions (<script>
) is able to receive additional HTML attributes via the XML layout. This includes an unique ID which is based on the block name in the XML layout.
When a block belongs to a Loki Component - in other words, when the block name in the XML layout is also used to declare a component in the file etc/loki_components.xml
- the HTML is further extended with data for AlpineJS.
For instance, take the following PHTML template for the block loki-example.component
:
<div class="<?= $css('relative') ?>"></div>
This will be transformed into the following HTML output:
<div id="loki-example-component"
x-data="LokiComponent"
x-title="LokiExampleComponent"
x-init-data='{"foo":"bar"}'
class="loki-example-component relative"></div>
^^See also "A simple component"