Adding a ComponentRepository

The example given under the page A simple component is really a basic example to get you started. It lacks the ability to modify data on the Magento side. To allow you to do that, a repository needs to be added.

Adding a repository to the component definition

First, add the following to the XML file etc/loki_components.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Loki_Components:etc/loki_components.xsd">
    <component 
        name="loki-components.example"
        repository="YireoTraining\ExampleLokiComponent\Component\Example\ExampleRepository"
    />
</components>

Clean the configuration cache afterward.

Creating a ComponentRepository class

A new PHP class YireoTraining\ExampleLokiComponent\Component\Example\ExampleRepository now needs to be created in our module. This class is dubbed the ComponentRepository class in a component, because it extends upon a class Loki\Components\Component\ComponentRepository. This parent class is abstract and forces you to create two methods:

  • getValue() which retrieves data from the storage of your choice and adds them to the ViewModel;
  • saveValue(mixed $value) which saves data to the storage of your choice;

Let's use the customer storage to store a random value and display this as well:

namespace YireoTraining\ExampleLokiComponent\Component\Example;

use Loki\Components\Component\ComponentRepository;

class ExampleRepository extends ComponentRepository
{
    public function getValue(): mixed
    {
        return $this->getContext()->getCustomerSession()->getExampleValue();
    }

    public function saveValue(mixed $value): void
    {
        $this->getContext()->getCustomerSession()->setExampleValue(rand());
    }
}

Note that in this case, the context class LokiComponentsComponentComponentContext is used to retrieve the customer session. This is entirely optional. You could also just inject the dependency directly in the repository class.

Showing the random value in the template

Because the repository is now part of the component, the button click of the previous example will send an AJAX call to the Magento application, which will then trigger the ExampleRepository::saveValue() value (saving a random number to the customer session).

Next, the same AJAX call renders the block loki-components.example (with a $viewModel variable being an instance of the generic Loki\Components\Component\ComponentViewModel class) and refreshes the HTML of that block in the browser (so the HTML element with ID loki-components-example).

If we modify the PHTML template to the following, the random value is fetched from the $viewModel and shown:

<div>
    <span>Random value: <?= $viewModel->getValue() ?></span><br/>
    <button @change="post">Refresh</button>
</div>

Adding a value

We can also extend the component with a simple custom value. This is already built in within every Loki Component. Note, however, that there is no preference for the type of the value (string, boolean, array, integer). Casting will need to be done manually.

First of all, let's add an input field with the Alpine directive x-model pointing to the component property value:

<div>
    <span>My value: <?= $viewModel->getValue() ?></span><br/>
    <input type="text" x-model="value" /><br/>
    <button @change="post">Refresh</button>
</div>

Now, modifying value in the input field, will make sure that the same ExampleRepository class stores the $value. Even though it is not required, it is nice to modify the repository slightly to cast any value to a string:

class ExampleRepository extends ComponentRepository
{
    public function getValue(): mixed
    {
        return (string)$this->getContext()->getCustomerSession()->getExampleValue();
    }

    public function saveValue(mixed $value): void
    {
        $this->getContext()->getCustomerSession()->setExampleValue((string)$value);
    }
}

Adding a filter

We can automatically apply a filter to the input value, simply by modifying the component definition. For instance, we can transform any input value into an uppercase string, by applying the uppercase filter:

<?xml version="1.0" encoding="UTF-8" ?>
<components xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Loki_Components:etc/loki_components.xsd">
    <component 
        name="loki-components.example"
        repository="YireoTraining\ExampleLokiComponent\Component\Example\ExampleRepository">
        <filter name="uppercase" />
    </component>
</components>

Saving multiple values

It is also possible to have a single Loki Component save multiple values via the repository. In that case, the $value argument of the saveValue() method simply is an array. The simplest way is to modify the AlpineJS directives to treat the value property as an object (with a dot syntax):

<div>
    <span>My value: <?= $viewModel->getValue() ?></span><br/>
    <input type="text" x-model="value.value1" /><br/>
    <input type="text" x-model="value.value2" /><br/>
    <button @change="post">Refresh</button>
</div>

However, a better way is to come up with custom variables instead:

<div>
    <span>My value: <?= $viewModel->getValue() ?></span><br/>
    <input type="text" x-model="value1" /><br/>
    <input type="text" x-model="value2" /><br/>
    <button @change="post">Refresh</button>
</div>

These variables do not exist yet in the default AlpineJS component LokiComponent, so this leads into the creation of a new AlpineJS component ExampleLokiComponent which is then also declared in a custom ComponentViewModel.

To anticipate this in the repository, let's save each value separately in the session:

class ExampleRepository extends ComponentRepository
{
    public function getValue(): mixed
    {
        return [
            'value1' => (string)$this->getContext()->getCustomerSession()->getExampleValue1().
            'value2' => (string)$this->getContext()->getCustomerSession()->getExampleValue2().
        ]
    }

    public function saveValue(mixed $value): void
    {
        if (false === isset($value['value1']) || false === isset($value['value2'])) {
            return;
        }
    
        $this->getContext()->getCustomerSession()->setExampleValue1((string)$value['value1']);
        $this->getContext()->getCustomerSession()->setExampleValue2((string)$value['value2']);
    }
}

Filtering still applies to every value in that array (recursively), so combine filters with non-simple $value with care.

Last modified: September 4, 2025