PHPackages                             tobento/service-repository-storage - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. tobento/service-repository-storage

ActiveLibrary[File &amp; Storage](/categories/file-storage)

tobento/service-repository-storage
==================================

Storage repository implementation.

2.0.2(6mo ago)0211↓50%11MITPHPPHP &gt;=8.4

Since Apr 22Pushed 6mo ago1 watchersCompare

[ Source](https://github.com/tobento-ch/service-repository-storage)[ Packagist](https://packagist.org/packages/tobento/service-repository-storage)[ Docs](https://www.tobento.ch)[ RSS](/packages/tobento-service-repository-storage/feed)WikiDiscussions 2.x Synced 1mo ago

READMEChangelog (10)Dependencies (12)Versions (17)Used By (11)

Repository Storage Service
==========================

[](#repository-storage-service)

[Storage](https://github.com/tobento-ch/service-storage) [repository](https://github.com/tobento-ch/service-repository) implementation.

Table of Contents
-----------------

[](#table-of-contents)

- [Getting started](#getting-started)
    - [Requirements](#requirements)
    - [Highlights](#highlights)
- [Documentation](#documentation)
    - [Storage Repository](#storage-repository)
    - [Storage Read Repository](#storage-read-repository)
    - [Storage Write Repository](#storage-write-repository)
    - [Where Parameters](#where-parameters)
    - [Storage Entity Factory](#storage-entity-factory)
    - [Repository With Columns](#repository-with-columns)
    - [Columns Common Methods](#columns-common-methods)
        - [Type](#type)
        - [Read](#read)
        - [Write](#write)
        - [Force Writing](#force-writing)
        - [Storable](#storable)
    - [Columns](#columns)
        - [Boolean](#boolean)
        - [Datetime](#datetime)
        - [Float](#float)
        - [Id](#id)
        - [Integer](#integer)
        - [Json](#json)
        - [Text](#text)
        - [Translatable](#translatable)
    - [Translations](#translations)
        - [Where Parameters Translations](#where-parameters-translations)
        - [Write Translations](#write-translations)
    - [Migration](#migration)
- [Credits](#credits)

---

Getting started
===============

[](#getting-started)

Add the latest version of the repository storage service project running this command.

```
composer require tobento/service-repository-storage

```

Requirements
------------

[](#requirements)

- PHP 8.4 or greater

Highlights
----------

[](#highlights)

- Framework-agnostic, will work with any project
- Decoupled design

Documentation
=============

[](#documentation)

Check out the [Storage Service - Storages](https://github.com/tobento-ch/service-storage#storages) for its available storages.

Check out the [Repository Service](https://github.com/tobento-ch/service-repository) for its documentation.

Storage Repository
------------------

[](#storage-repository)

To create a storage repository simply extend the `StorageRepository::class`:

You may also check out the [Repository With Columns](#repository-with-columns) section to create the repository using columns.

```
use Tobento\Service\Repository\RepositoryInterface;
use Tobento\Service\Repository\ReadRepositoryInterface;
use Tobento\Service\Repository\WriteRepositoryInterface;
use Tobento\Service\Repository\Storage\StorageRepository;
use Tobento\Service\Repository\Storage\StorageEntityFactoryInterface;
use Tobento\Service\Storage\InMemoryStorage;
use Tobento\Service\Storage\Tables\Tables;

class ProductRepository extends StorageRepository
{
    //
}

$repository = new ProductRepository(
    storage: new  InMemoryStorage(
        items: [],
        tables: new Tables()->add('products', ['id', 'sku', 'price'], 'id')
    ),
    table: 'products', // specify which storage table should be used.
    entityFactory: null, // null|StorageEntityFactoryInterface
);

var_dump($repository instanceof RepositoryInterface);
// bool(true)

var_dump($repository instanceof ReadRepositoryInterface);
// bool(true)

var_dump($repository instanceof WriteRepositoryInterface);
// bool(true)
```

By default, the read and write methods will return the following types depending on the method called:

- `Tobento\Service\Storage\ItemInterface::class` [Storage - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface)
- `Tobento\Service\Storage\ItemsInterface::class` [Storage - ItemsInterface](https://github.com/tobento-ch/service-storage#items-interface)
- `null`

You may specify a custom [Storage Entity Factory](#storage-entity-factory) to return custom entities by the `entityFactory` parameter.

Storage Read Repository
-----------------------

[](#storage-read-repository)

To create a storage read repository simply extend the `StorageReadRepository::class`:

You may also check out the [Repository With Columns](#repository-with-columns) section to create the repository using columns.

```
use Tobento\Service\Repository\ReadRepositoryInterface;
use Tobento\Service\Repository\Storage\StorageReadRepository;
use Tobento\Service\Repository\Storage\StorageEntityFactoryInterface;
use Tobento\Service\Storage\InMemoryStorage;
use Tobento\Service\Storage\Tables\Tables;

class ProductReadRepository extends StorageReadRepository
{
    // adding custom find methods
}

$repository = new ProductReadRepository(
    storage: new  InMemoryStorage(
        items: [
            'products' => [
                1 => ['id' => 1, 'sku' => 'paper', 'price' => 1.2],
                2 => ['id' => 2, 'sku' => 'pen', 'price' => 1.8],
                3 => ['id' => 3, 'sku' => 'pencil', 'price' => 1.5],
            ],
        ],
        tables: new Tables()->add('products', ['id', 'sku', 'price'], 'id')
    ),
    table: 'products',
    entityFactory: null, // null|StorageEntityFactoryInterface
);

var_dump($repository instanceof ReadRepositoryInterface);
// bool(true)
```

By default, the find methods will return the following types depending on the method called:

- `Tobento\Service\Storage\ItemInterface::class` [Storage - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface)
- `Tobento\Service\Storage\ItemsInterface::class` [Storage - ItemsInterface](https://github.com/tobento-ch/service-storage#items-interface)
- `null`

You may specify a custom [Storage Entity Factory](#storage-entity-factory) to return custom entities by the `entityFactory` parameter.

**findById**

```
use Tobento\Service\Storage\ItemInterface;

$entity = $repository->findById(id: 2);

var_dump($entity instanceof ItemInterface);
// bool(true)

var_dump($entity->get('sku'));
// string(3) "pen"

$entity = $repository->findById(id: 5);

var_dump($entity);
// NULL
```

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

**findByIds**

```
use Tobento\Service\Storage\ItemsInterface;
use Tobento\Service\Storage\ItemInterface;

$entities = $repository->findByIds(1, 2, 8);

var_dump($entities instanceof ItemsInterface);
// bool(true)

var_dump($entities->count());
// int(2)

foreach($entities as $entity) {
    var_dump($entity instanceof ItemInterface);
    // bool(true)
}
```

You may check out the [Storage Service - ItemsInterface](https://github.com/tobento-ch/service-storage#items-interface) for its documentation.

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

**findOne**

```
use Tobento\Service\Storage\ItemInterface;

$entity = $repository->findOne(where: [
    'sku' => 'pen',
]);

var_dump($entity instanceof ItemInterface);
// bool(true)

var_dump($entity->get('sku'));
// string(3) "pen"

$entity = $repository->findOne(where: [
    'sku' => 'foo',
]);

var_dump($entity);
// NULL
```

You may check out the [Where Parameters](#where-parameters) for its supported parameters.

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

**findAll**

```
use Tobento\Service\Storage\ItemsInterface;
use Tobento\Service\Storage\ItemInterface;

$entities = $repository->findAll(where: [
    'price' => ['>' => 1.3],
]);

var_dump($entities instanceof ItemsInterface);
// bool(true)

var_dump($entities->count());
// int(2)

foreach($entities as $entity) {
    var_dump($entity instanceof ItemInterface);
    // bool(true)
}

$entities = $repository->findAll(
    where: [
        'price' => [
            '>' => 1.3,
            '' => 1],
    ],
    attributes: [
        'price' => 2.5,
    ],
);

var_dump($updatedEntities instanceof ItemsInterface);
// bool(true)

var_dump($updatedEntities->count());
// int(2)

foreach($updatedEntities as $entity) {
    var_dump($entity instanceof ItemInterface);
    // bool(true)
}
```

You may check out the [Where Parameters](#where-parameters) for its supported parameters.

You may check out the [Storage Service - ItemsInterface](https://github.com/tobento-ch/service-storage#items-interface) for its documentation.

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

**deleteById**

```
use Tobento\Service\Storage\ItemInterface;
use Tobento\Service\Repository\RepositoryDeleteException;

$deletedEntity = $repository->deleteById(id: 2);

var_dump($deletedEntity instanceof ItemInterface);
// bool(true)

var_dump($deletedEntity->all());
// array(3) { ["id"]=> int(2) ["sku"]=> string(3) "pen" ["price"]=> float(1.8) }
```

This method will throw a `RepositoryDeleteException::class` exception if the storage table has no primary key specified or the entity to delete does not exist.

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

**delete**

```
use Tobento\Service\Storage\ItemsInterface;
use Tobento\Service\Storage\ItemInterface;

$deletedEntities = $repository->delete(where: [
    'id' => ['>' => 1],
]);

var_dump($deletedEntities instanceof ItemsInterface);
// bool(true)

var_dump($deletedEntities->count());
// int(2)

foreach($deletedEntities as $entity) {
    var_dump($entity instanceof ItemInterface);
    // bool(true)
}
```

You may check out the [Where Parameters](#where-parameters) for its supported parameters.

You may check out the [Storage Service - ItemsInterface](https://github.com/tobento-ch/service-storage#items-interface) for its documentation.

You may check out the [Storage Service - ItemInterface](https://github.com/tobento-ch/service-storage#item-interface) for its documentation.

Where Parameters
----------------

[](#where-parameters)

The following where clauses are supported (for all read/write methods with where parameter):

```
$entities = $repository->findAll(where: [
    'sku' => 'pen',
    // is equal to:
    'sku' => ['=' => 'pen'],

    'sku' => ['!=' => 'pen'],

    'sku' => ['null'],
    'sku' => ['not null'],

    'price' => ['>' => 1.5],
    'price' => ['=' => 1.5],
    'price' => ['color' => 'blue',

    'options->colors' => ['contains' => 'blue'],
    'options->colors' => ['contains' => ['blue']],

    'options->colors' => ['contains one of' => ['blue', 'red']],

    'options->color' => ['contains key'],
]);
```

**Or Clauses**

To create or clauses add the `or` keyword before any operator:

```
$entities = $repository->findAll(where: [
    'title' => ['like' => '%foo%', 'or like' => '%bar%'],

    // or
    'title' => ['like' => '%foo%'],
    'price' => ['or >' => 1.5],

    // or using an array
    'id' => ['in' => [2,5,6]],
    [
        'title' => ['or like' => '%foo%'],
        'price' => ['or >' => 1.5],
    ]
]);
```

Storage Entity Factory
----------------------

[](#storage-entity-factory)

You may create a custom entity factory to return custom entities by the [Storage Repository](#storage-repository), [Storage Read Repository](#storage-read-repository) or [Storage Write Repository](#storage-write-repository).

To create a custom entity factory simply extend the `EntityFactory::class` and adjust the `createEntityFromArray` method:

```
use Tobento\Service\Repository\Storage\EntityFactory;
use Tobento\Service\Repository\Storage\StorageEntityFactoryInterface;
use Tobento\Service\Repository\EntityFactoryInterface;
use Tobento\Service\Storage\ItemInterface;

class ProductFactory extends EntityFactory
{
    public function createEntityFromArray(array $attributes): Product
    {
        // Process the columns reading:
        $attributes = $this->columns->processReading($attributes);

        // Create entity:
        return new Product(
            id: $attributes['id'] ?? 0,
            sku: $attributes['sku'] ?? '',
        );
    }
}

class Product
{
    public function __construct(
        public readonly int $id,
        public readonly string $sku,
    ) {}
}

$productFactory = new ProductFactory();
$productFactory->setColumns([]); // will be set by the storage

var_dump($productFactory instanceof StorageEntityFactoryInterface);
// bool(true)

var_dump($productFactory instanceof EntityFactoryInterface);
// bool(true)

$product = $productFactory->createEntityFromArray([
    'id' => 1,
    'sku' => 'pen',
]);

var_dump($product);
// object(Product)#4 (2) { ["id"]=> int(1) ["sku"]=> string(3) "pen" }
```

Repository With Columns
-----------------------

[](#repository-with-columns)

Creating a storage repository with columns has the following advantages:

- casts values to primitive types on reading and writing
- specify a reader and writer to handle casting
- create database migration based on columns

```
use Tobento\Service\Repository\Storage\StorageRepository;
use Tobento\Service\Repository\Storage\Column;
use Tobento\Service\Storage\InMemoryStorage;

class ProductRepository extends StorageRepository
{
    //
}

$repository = new ProductRepository(
    storage: new  InMemoryStorage(items: []),
    table: 'products',

    // specify the columns:
    columns: [
        new Column\Id(),
        new Column\Text('sku'),
        new Column\Text('title')
            ->read(fn (string $value, array $attributes): string => ucfirst($value))
            ->write(fn (string $value, array $attributes, string $action): string => ucfirst($value)),
        new Column\Boolean('active'),
    ],
);
```

You may prefer to specify the columns on its class instead by using the `configureColumns` method:

```
use Tobento\Service\Repository\Storage\StorageRepository;
use Tobento\Service\Repository\Storage\Column\ColumnsInterface;
use Tobento\Service\Repository\Storage\Column\ColumnInterface;
use Tobento\Service\Repository\Storage\Column;
use Tobento\Service\Storage\InMemoryStorage;

class ProductRepository extends StorageRepository
{
    /**
     * Returns the configured columns.
     *
     * @return iterable|ColumnsInterface
     */
    protected function configureColumns(): iterable|ColumnsInterface
    {
        return [
            new Column\Id(),
            new Column\Text('sku'),
            new Column\Text('title')
                ->read(fn (string $value, array $attributes): string => ucfirst($value))
                ->write(fn (string $value, array $attributes, string $action): string => ucfirst($value)),
            new Column\Boolean('active'),
        ];
    }
}

$repository = new ProductRepository(
    storage: new  InMemoryStorage(items: []),
    table: 'products',
);
```

Columns Common Methods
----------------------

[](#columns-common-methods)

### Type

[](#type)

The parameters set on the `type` method are used for [Migration](#migration) purpose only.

```
use Tobento\Service\Repository\Storage\Column\Text;
use Tobento\Service\Repository\Storage\Column\Int;

$column = new Text(name: 'name')
    ->type(length: 150, nullable: false, default: 'foo', parameters: ['charset' => 'utf8mb4']);

$column = new Int(name: 'name')
    ->type(
        length: 20,
        unsigned: true,
        index: ['name' => 'index_name', 'column' => 'name', 'unique' => true, 'primary' => true],
    );

$column = new Float(name: 'name', type: 'decimal')
    ->type(precision: 10, scale: 0);
```

Check out the [Service Database - Column Factory](https://github.com/tobento-ch/service-database#column-factory) `createColumnFromArray` method for more detail.

Check out the [Service Database - Index Factory](https://github.com/tobento-ch/service-database#index-factory) `createIndexFromArray` method for more detail.

### Read

[](#read)

You may use the read method to specify a reader (callable). The reader will automatically be called by the repository when attempting to retrieve the value.

```
use Tobento\Service\Repository\Storage\Column\Text;

$column = new Text(name: 'name')
    ->read(fn (string $value, array $attributes): string => ucfirst($value));
```

The value is casted to its column type before being passed to the reader!

### Write

[](#write)

You may use the write method to specify a writer (callable). The writer will automatically be called by the repository when attempting to write the value. The writer will only be called if the attribute exists on any write methods. If you you want the writer to be always called, check out the [Force Writing](#force-writing) section.

```
use Tobento\Service\Repository\Storage\Column\Text;

$column = new Text(name: 'name')
    ->write(fn (string $value, array $attributes, string $action): string => ucfirst($value));
    // $action = the action name processed such as 'create' or 'update'
```

The value is casted to its column type before being passed to the writer!

### Force Writing

[](#force-writing)

You may use the `forceWriting` method to specify if you want to force writing, meaning the writer gets always called even if the attribute was not passed on any write methods.

```
use Tobento\Service\Repository\Storage\Column\Text;

$column = new Text(name: 'name')
    ->forceWriting(action: 'create|update')
    ->write(fn (string $value, array $attributes, string $action): string => ucfirst($value));

$column = new Text(name: 'name')
    ->forceWriting(action: 'create')
    ->write(fn (string $value, array $attributes, string $action): string => ucfirst($value));
```

### Storable

[](#storable)

```
use Tobento\Service\Repository\Storage\Column\Text;

$column = new Text(name: 'name')
    ->storable(false);
```

Columns
-------

[](#columns)

### Boolean

[](#boolean)

```
use Tobento\Service\Repository\Storage\Column\Boolean;

$column = new Boolean(name: 'active');

$column = new Boolean(name: 'active')
    ->type(default: true);
```

### Datetime

[](#datetime)

```
use Tobento\Service\Repository\Storage\Column\Datetime;

$column = new Datetime(name: 'created_at');

// with datetime type (default):
$column = new Datetime(name: 'created_at', type: 'datetime')
    ->type(nullable: true);

// with date type:
$column = new Datetime(name: 'created_at', type: 'date')
    ->type(nullable: true);

// with time type:
$column = new Datetime(name: 'created_time', type: 'time')
    ->type(nullable: true);

// with timestamp type:
$column = new Datetime(name: 'created_ts', type: 'timestamp')
    ->type(nullable: true);
```

**read**

You may use the read method to cast your value. Without specifying a read method, the value will be casted to a string only.

```
use Tobento\Service\Repository\Storage\Column\Datetime;
use Tobento\Service\Dater\DateFormatter;
use DateTimeImmutable;

$column = new Datetime(name: 'created_at');

$read = fn (mixed $value, array $attributes, DateFormatter $df)
    : DateTimeImmutable => $df->toDateTime(value: $value);

$column = new Datetime(name: 'created_at')->read($read);
```

Check out the [Dater Service - DateFormatter](https://github.com/tobento-ch/service-dater#date-formatter) for more detail.

**write**

You may use the write method to cast your value. Without specifying a write method, the value will be casted to the following formats:

- datetime type: `Y-m-d H:i:s`
- date type: `Y-m-d`
- time type: `H:i:s`
- timestamp type: timestamp string

```
use Tobento\Service\Repository\Storage\Column\Datetime;
use Tobento\Service\Dater\DateFormatter;

$column = new Datetime(name: 'created_at');

$write = fn (mixed $value, array $attributes, string $action, DateFormatter $df)
    : string => $df->format(value: $value, format: 'H:i:s');

$column = new Datetime(name: 'created_at')->read($read);
```

Check out the [Dater Service - DateFormatter](https://github.com/tobento-ch/service-dater#date-formatter) for more detail.

**autoCreate**

Use the `autoCreate` method if you want the date to be automatically created if no value was passed by any write methods.

```
use Tobento\Service\Repository\Storage\Column\Datetime;

$column = new Datetime(name: 'created_at')->autoCreate();
```

**autoUpdate**

Use the `autoUpdate` method if you want the date to be automatically updated if no value was passed by any write methods.

```
use Tobento\Service\Repository\Storage\Column\Datetime;

$column = new Datetime(name: 'updated_at')->autoUpdate();
```

### Float

[](#float)

```
use Tobento\Service\Repository\Storage\Column\FloatCol;

$column = new FloatCol(name: 'name');

// with float type (default):
$column = new FloatCol(name: 'name', type: 'float')
    ->type(nullable: false, default: 0.5);

// with double type:
$column = new FloatCol(name: 'name', type: 'double')
    ->type(nullable: false, default: 0.5);

// with decimal type:
$column = new FloatCol(name: 'name', type: 'decimal')
    ->type(nullable: false, default: 0.5, precision: 10, scale: 0);
```

### Id

[](#id)

```
use Tobento\Service\Repository\Storage\Column\Id;

$column = new Id();

// with name (default is id):
$column = new Id(name: 'some_id');

// with bigPrimary type (default):
$column = new Id(type: 'bigPrimary')
    ->type(
        length: 18,
        unsigned: true,
        index: ['name' => 'index_name', 'primary' => true],
    );

// with primary:
$column = new Id(type: 'primary')
    ->type(
        length: 5,
        unsigned: true,
        index: ['name' => 'index_name', 'primary' => true],
    );
```

### Integer

[](#integer)

```
use Tobento\Service\Repository\Storage\Column\Integer;

$column = new Integer(name: 'name');

// with int type (default):
$column = new Integer(name: 'name', type: 'int')
    ->type(length: 11, unsigned: true, nullable: false, default: 0);

// with tinyInt type:
$column = new Integer(name: 'name', type: 'tinyInt')
    ->type(length: 5, unsigned: true, nullable: false, default: 0);

// with bigInt type:
$column = new Integer(name: 'name', type: 'bigInt')
    ->type(length: 200, unsigned: true, nullable: false, default: 0);
```

### Json

[](#json)

```
use Tobento\Service\Repository\Storage\Column\Json;

$column = new Json(name: 'name');

$column = new Json(name: 'name')
    ->type(nullable: false, default: ['foo', 'bar']);
```

### Text

[](#text)

```
use Tobento\Service\Repository\Storage\Column\Text;

$column = new Text(name: 'sku');

// with string type (default):
$column = new Text(name: 'sku', type: 'string')
    ->type(length: 100, nullable: false, default: '');

// with char type:
$column = new Text(name: 'locale', type: 'char')
    ->type(length: 5, nullable: false, default: 'en');

// with text type:
$column = new Text(name: 'desc', type: 'text')
    ->type(nullable: false, default: 'lorem ipsum');
```

### Translatable

[](#translatable)

```
use Tobento\Service\Repository\Storage\Column\Translatable;

$column = new Translatable(name: 'name');

// with string subtype (default):
$column = new Translatable(name: 'name', subtype: 'string')
    ->type(nullable: false)
    ->read(fn (string $value, array $attributes, string $locale): string => strtoupper($value))
    ->write(fn (string $value, array $attributes, string $action, string $locale): string => strtoupper($value));

// with array subtype:
$column = new Translatable(name: 'name', subtype: 'array')
    ->type(nullable: false)
    ->read(fn (array $value, array $attributes, string $locale): array => $value)
    ->write(fn (array $value, array $attributes, string $action, string $locale): array => $value);
```

**Read Attribute**

After reading, a `StringTranslations::class` or `ArrayTranslations::class` is being created depending on its column subtype `string` or `array`.

```
use Tobento\Service\Repository\Storage\Attribute\StringTranslations;

$repository->locale('en');
$repository->locales('en', 'de', 'fr');
$repository->localeFallbacks(['de' => 'en']);

$entity = $repository->findById(id: 2);

var_dump($entity->get('title') instanceof StringTranslations);
// bool(true)

// The title on the current locale set on the repository:
$title = (string)$entity->get('title');

// or:
$title = $entity->get('title')->get();

// specific locale:
$title = $entity->get('title')->get(locale: 'de');

// specific locale with default value
// if fallback locale value does not exist:
$title = $entity->get('title')->get(locale: 'fr', default: 'title');

// check if translation exists:
var_dump($entity->get('title')->has(locale: 'de'));
// bool(true)

// returns all translations:
var_dump($entity->get('title')->all();
// array(2) {["en"]=> string(5) "Title" ["de"]=> string(5) "Titel"}
```

```
use Tobento\Service\Repository\Storage\Attribute\ArrayTranslations;

$repository->locale('en');
$repository->locales('en', 'de', 'fr');
$repository->localeFallbacks(['de' => 'en']);

$entity = $repository->findById(id: 2);

var_dump($entity->get('meta') instanceof ArrayTranslations);
// bool(true)

// The meta on the current locale set on the repository:
$meta = $entity->get('meta')->get();
// array(1) {["color"]=> string(3) "red"}

// specific locale:
$meta = $entity->get('meta')->get(locale: 'de');
// array(1) {["color"]=> string(3) "rot"}

// specific locale with default value
// if fallback locale value does not exist:
$meta = $entity->get('meta')->get(locale: 'fr', default: ['color' => 'rot']);
// array(1) {["color"]=> string(3) "red"}

// check if translation exists:
var_dump($entity->get('title')->has(locale: 'de'));
// bool(true)

// returns all translations:
var_dump($entity->get('meta')->all();
// array(2) {["en"]=> array(1) {["color"]=> string(3) "red"} ["de"]=> array(1) {["color"]=> string(3) "rot"}}

// The meta color on the current locale set on the repository:
$color = $entity->get('meta')->get(key: 'color');
// string(3) "red"

// specific locale:
$color = $entity->get('meta')->get(locale: 'de', key: 'color');
// string(3) "rot"

// specific locale with default value
// if fallback locale value does not exist:
$color = $entity->get('meta')->get(locale: 'fr', key: 'color', default: ['color' => 'rot']);
// string(3) "red"

// check if translation exists:
var_dump($entity->get('title')->has(locale: 'de', key: 'color'));
// bool(true)
```

Translations
------------

[](#translations)

Confiure the locales for the repository:

```
// current locale:
$repository->locale('en');

// only the defined locales are used:
$repository->locales('en', 'de', 'fr');

// fallbacks:
$repository->localeFallbacks(['de' => 'en']);
```

### Where Parameters Translations

[](#where-parameters-translations)

Where clauses for translation columns (for all read/write methods with where parameter):

```
$entities = $repository->findAll(where: [

    // query current locale set on the repository:
    'title' => ['like' => 'pe%'],

    // query specific locale using json syntax:
    'title->de' => ['like' => 'pe%'],

    // Array translations:
    // query current locale set on the repository:
    'options->color' => 'red', // same as: options->en->color

    // query specific locale using json syntax:
    'options->de->color' => 'red',
]);
```

### Write Translations

[](#write-translations)

**create**

```
$createEntity = $repository->create([
    'title' => [
        'en' => 'Title',
        'de' => 'Titel',
    ],
]);
```

**update**

```
// updates all:
$updatedEntity = $repository->updateById(2, [
    'title' => [
        'en' => 'Title',
        'de' => 'Titel',
    ],
]);

// updates specific locale using json syntax:
$updatedEntity = $repository->updateById(2, [
    'title->de' => 'Title',
]);

// Array translations:
// updates specific locale using json syntax:
$updatedEntity = $repository->updateById(2, [
    'options->de->color' => 'red',
]);
```

Migration
---------

[](#migration)

If you have set up your [Repository With Columns](#repository-with-columns), you might use the migration `RepositoryAction::class` and `RepositoryDeleteAction::class` to create your database migration from the columns defined.

First, you will need to install:

- `composer require tobento/service-database-storage`
- `composer require tobento/service-migration`

**Example of migration class**

```
use Tobento\Service\Repository\Storage\Migration\RepositoryAction;
use Tobento\Service\Repository\Storage\Migration\RepositoryDeleteAction;
use Tobento\Service\Migration\MigrationInterface;
use Tobento\Service\Migration\ActionsInterface;
use Tobento\Service\Migration\Action;
use Tobento\Service\Migration\Actions;

class UserMigration implements MigrationInterface
{
    public function __construct(
        protected UserRepository $userRepository,
    ) {}

    /**
     * Return a description of the migration.
     *
     * @return string
     */
    public function description(): string
    {
        return 'Users migration';
    }

    /**
     * Return the actions to be processed on install.
     *
     * @return ActionsInterface
     */
    public function install(): ActionsInterface
    {
        // you might check if repository is supported for the action:
        if (RepositoryAction::isSupportedRepository($this->userRepository)) {
            // create action
        }

        return new Actions(
            new RepositoryAction(
                repository: $this->userRepository,
                description: 'User migration',

                // you might set items to be migrated
                items: [
                    ['email' => 'demo@example.com'],
                ],

                // you may set if items should be created using a boolean:
                createItems: true, // default

                // or using a closure:
                createItems: fn ($repo): bool => is_null($repo->findOne()),
            ),

            // you might use the newOrNull method
            // if the defined repository is of any type.
            // If it is an unsupported repository
            // a Action\NullAction::class is created.
            RepositoryAction::newOrNull(
                repository: $this->userRepository,
                description: 'User migration',

                // you might set items to be migrated
                items: [
                    ['email' => 'demo@example.com'],
                ],

                createItems: true, // default
            ),

            // you might use the newOrFail method
            // if the defined repository is of any type.
            // If it is an unsupported repository
            // a Action\Fail::class is created.
            RepositoryAction::newOrFail(
                repository: $this->userRepository,
                description: 'User migration',

                // you might set items to be migrated
                items: [
                    ['email' => 'demo@example.com'],
                ],

                createItems: true, // default
            ),
        );
    }

    /**
     * Return the actions to be processed on uninstall.
     *
     * @return ActionsInterface
     */
    public function uninstall(): ActionsInterface
    {
        // you might check if repository is supported for the action:
        if (RepositoryDeleteAction::isSupportedRepository($this->userRepository)) {
            // create action
        }

        return new Actions(
            new RepositoryDeleteAction(
                repository: $this->userRepository,
                description: 'User migration',
            ),

            // you might use the newOrNull method
            // if the defined repository is of any type.
            // If it is an unsupported repository
            // a Action\NullAction::class is created.
            RepositoryDeleteAction::newOrNull(
                repository: $this->userRepository,
                description: 'User migration',
            ),

            // you might use the newOrFail method
            // if the defined repository is of any type.
            // If it is an unsupported repository
            // a Action\Fail::class is created.
            RepositoryDeleteAction::newOrFail(
                repository: $this->userRepository,
                description: 'User migration',
            ),
        );
    }
}
```

You may check out the [Migration Service](https://github.com/tobento-ch/service-migration) for more detail.

Credits
=======

[](#credits)

- [Tobias Strub](https://www.tobento.ch)
- [All Contributors](../../contributors)

###  Health Score

45

—

FairBetter than 92% of packages

Maintenance69

Regular maintenance activity

Popularity14

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~58 days

Recently: every ~20 days

Total

17

Last Release

180d ago

Major Versions

1.x-dev → 2.02025-09-30

PHP version history (2 changes)1.0.0PHP &gt;=8.0

2.0PHP &gt;=8.4

### Community

Maintainers

![](https://www.gravatar.com/avatar/055d6a1b5c2384bb179c75ab0b55914231d898fdc4dffeb30770f81200e52206?d=identicon)[TOBENTOch](/maintainers/TOBENTOch)

---

Top Contributors

[![tobento-ch](https://avatars.githubusercontent.com/u/16684832?v=4)](https://github.com/tobento-ch "tobento-ch (70 commits)")

---

Tags

phppackagestoragerepositorytobento

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tobento-service-repository-storage/health.svg)

```
[![Health](https://phpackages.com/badges/tobento-service-repository-storage/health.svg)](https://phpackages.com/packages/tobento-service-repository-storage)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
