# Adding a `ComponentRepository`

**The example given under the page [A simple component](/checkout/dev/components/simple-component) is really just 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: A component repository is found by its component ID, allowing you to submit data easily from within Alpine.js towards your own PHP logic, without the need to know about the entire AJAX stack in between.**

## Adding a repository to the component definition
First, add the following to the XML file `etc/loki_components.xml`:

```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:

```php
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:

```html
<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`:

```html
<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:

```php
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
<?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):

```html
<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:
```html
<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:

```php
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.**
