tobento/service-repository

Repository interfaces for PHP applications.

Maintainers

👁 TOBENTOch

Package info

github.com/tobento-ch/service-repository

Homepage

pkg:composer/tobento/service-repository

Statistics

Installs: 187

Dependents: 13

Suggesters: 2

Stars: 0

Open Issues: 0

2.0.3 2026-03-27 18:22 UTC

Requires (Dev)

Suggests

None

Provides

None

Conflicts

None

Replaces

None

MIT 5dbcfa4e755146ed36baec479cd99cbcdbd1071f

packagephprepositorytobento

This package is auto-updated.

Last update: 2026-06-27 18:51:17 UTC


README

Repository interfaces for PHP applications.

Table of Contents

Getting started

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

composer require tobento/service-repository

Requirements

  • PHP 8.4 or greater

Highlights

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

Documentation

Interfaces

Repository Interface

namespace Tobento\Service\Repository;

interface RepositoryInterface extends ReadRepositoryInterface, WriteRepositoryInterface
{
 //
}

Read Repository Interface

namespace Tobento\Service\Repository;

interface ReadRepositoryInterface
{
 /**
 * Returns the found entity using the specified id (primary key)
 * or null if none found.
 *
 * @param int|string $id
 * @return null|object
 * @throws RepositoryReadException
 */
 public function findById(int|string $id): null|object;
 
 /**
 * Returns the found entity using the specified id (primary key)
 * or null if none found.
 *
 * @param int|string ...$ids
 * @return iterable<object>
 * @throws RepositoryReadException
 */
 public function findByIds(int|string ...$ids): iterable;

 /**
 * Returns the found entity using the specified where parameters
 * or null if none found.
 *
 * @param array $where
 * @return null|object
 * @throws RepositoryReadException
 */
 public function findOne(array $where = []): null|object;

 /**
 * Returns the found entities using the specified parameters.
 *
 * @param array $where Usually where parameters.
 * @param array $orderBy The order by parameters.
 * @param null|int|array $limit The limit e.g. 5 or [5(number), 10(offset)].
 * @return iterable<object>
 * @throws RepositoryReadException
 */
 public function findAll(array $where = [], array $orderBy = [], null|int|array $limit = null): iterable;
 
 /**
 * Returns the found column values using the specified parameters.
 *
 * @param string $column The column name for the values.
 * @param null|string $key The column name for the index key.
 * @param array $where Usually where parameters.
 * @param array $orderBy The order by parameters.
 * @param null|int|array $limit The limit e.g. 5 or [5(number), 10(offset)].
 * @return array
 * @throws RepositoryReadException
 */
 public function findColumn(
 string $column,
 null|string $key = null,
 array $where = [],
 array $orderBy = [],
 null|int|array $limit = null
 ): array;
 
 /**
 * Returns the number of items using the specified where parameters.
 *
 * @param array $where
 * @return int
 * @throws RepositoryReadException
 */
 public function count(array $where = []): int;
}

Write Repository Interface

namespace Tobento\Service\Repository;

interface WriteRepositoryInterface
{
 /**
 * Create an entity.
 *
 * @param array $attributes
 * @return object The created entity.
 * @throws RepositoryCreateException
 */
 public function create(array $attributes): object;
 
 /**
 * Update an entity by id.
 *
 * @param string|int $id
 * @param array $attributes The attributes to update the entity.
 * @return object The updated entity.
 * @throws RepositoryUpdateException
 */
 public function updateById(string|int $id, array $attributes): object;
 
 /**
 * Update entities.
 *
 * @param array $where The where parameters.
 * @param array $attributes The attributes to update the entities.
 * @return iterable<object> The updated entities.
 * @throws RepositoryUpdateException
 */
 public function update(array $where, array $attributes): iterable;
 
 /**
 * Delete an entity by id.
 *
 * @param string|int $id
 * @return object The deleted entity.
 * @throws RepositoryDeleteException
 */
 public function deleteById(string|int $id): object;
 
 /**
 * Delete entities.
 *
 * @param array $where The where parameters.
 * @return iterable<object> The deleted entities.
 * @throws RepositoryDeleteException
 */
 public function delete(array $where): iterable;
}

Locales Aware Interface

You may implement the locales aware interface into your repository using the HasLocales trait.

use Tobento\Service\Repository\HasLocales;
use Tobento\Service\Repository\LocalesAware;
use Tobento\Service\Repository\RepositoryInterface;

class SomeRepository implements RepositoryInterface, LocalesAware
{
 use HasLocales;
 
 // ...
}
namespace Tobento\Service\Repository;

interface LocalesAware
{
 /**
 * Sets the locale.
 *
 * @param string $locale
 * @return static $this
 */
 public function locale(string $locale): static;
 
 /**
 * Sets the locale returing a new instance.
 *
 * @param string $locale
 * @return static
 */
 public function withLocale(string $locale): static;
 
 /**
 * Returns the locale.
 *
 * @return string
 */
 public function getLocale(): string;
 
 /**
 * Sets the locales.
 *
 * @param string ...$locales
 * @return static $this
 */
 public function locales(string ...$locales): static;
 
 /**
 * Sets the locales returning a new instance.
 *
 * @param string ...$locales
 * @return static
 */
 public function withLocales(string ...$locales): static;
 
 /**
 * Returns the locales.
 *
 * @return array
 */
 public function getLocales(): array;
 
 /**
 * Sets the locale fallbacks.
 *
 * @param array<string, string> $localeFallbacks
 * @return static $this
 */
 public function localeFallbacks(array $localeFallbacks): static;
 
 /**
 * Sets the locale fallbacks returning a new instance.
 *
 * @param array<string, string> $localeFallbacks
 * @return static
 */
 public function withLocaleFallbacks(array $localeFallbacks): static;
 
 /**
 * Returns the locale fallbacks.
 *
 * @return array<string, string>
 */
 public function getLocaleFallbacks(): array;
}

Entity Factory Interface

namespace Tobento\Service\Repository;

interface EntityFactoryInterface
{
 /**
 * Create an entity from array.
 *
 * @param array $attributes
 * @return object The created entity.
 */
 public function createEntityFromArray(array $attributes): object;
}

Null Repository

The NullRepository is a no-operation implementation of RepositoryInterface.
It accepts all read and write operations without performing any side effects and returns neutral, predictable values.

This makes it useful for:

  • disabling persistence in development or testing environments
  • providing a safe fallback when no repository is configured
  • stubbing repository dependencies in prototypes
  • avoiding conditional logic (if ($repo) { ... })
  • ensuring repository calls never throw or mutate state

Features

  • Implements the full RepositoryInterface
  • All read operations return neutral values:
    • null for single-entity lookups
    • empty arrays/iterables for multi-entity queries
    • 0 for counts
  • All write operations return simple objects based on the provided attributes
  • No exceptions are thrown
  • No state is stored and no side effects occur

Returned Values Overview

Method Return Value
findById() null
findByIds() []
findOne() null
findAll() []
findColumn() []
count() 0
create() (object)$attributes
updateById() (object)$attributes
update() []
deleteById() (object)['id' => $id]
delete() []

Example

use Tobento\Service\Repository\NullRepository;

$repo = new NullRepository();

// Always returns null
$user = $repo->findById(1);

// Always returns empty iterable
$users = $repo->findAll(['active' => true]);

// Creates a simple object from attributes
$entity = $repo->create(['name' => 'Alice']);
// $entity->name === 'Alice'

// Update returns an object with the provided attributes
$updated = $repo->updateById(5, ['name' => 'Bob']);

// Delete returns an object containing the id
$deleted = $repo->deleteById(10);
// $deleted->id === 10

Read Only Repository Adapter

Any repository implementing the RepositoryInterface::class can be made read-only by decorating them using the ReadOnlyRepositoryAdapter::class:

use Tobento\Service\Repository\ReadOnlyRepositoryAdapter;
use Tobento\Service\Repository\RepositoryInterface;

$readOnlyRepository = new ReadOnlyRepositoryAdapter(
 repository: $repository, // RepositoryInterface
);

Events Repository Adapter

Any repository implementing the ReadRepositoryInterface::class or WriteRepositoryInterface::class can be made to dispatch default events by decorating them using the EventsRepositoryAdapter::class:

use Psr\EventDispatcher\EventDispatcherInterface;
use Tobento\Service\Repository\EventsRepositoryAdapter;
use Tobento\Service\Repository\ReadRepositoryInterface;
use Tobento\Service\Repository\WriteRepositoryInterface;
use Tobento\Service\Repository\Event;

$eventsRepository = new EventsRepositoryAdapter(
 eventDispatcher: $eventDispatcher, // EventDispatcherInterface
 repository: $repository, // ReadRepositoryInterface or WriteRepositoryInterface
 
 // if false (default) event attributes get used on write methods
 immutableAttributes: false,
);

Default Events

Event Description
Event\Retrieved::class The event will dispatch after an entity is retrieved from the read methods only.
Event\Creating::class The event will dispatch before an entity is created.
Event\Created::class The event will dispatch after an entity is created.
Event\Updating::class The event will dispatch before an entity is updated.
Event\Updated::class The event will dispatch after an entity is updated.
Event\Deleting::class The event will dispatch before an entity is deleted.
Event\Deleted::class The event will dispatch after an entity is deleted.

Eventer

The eventer may be used to easily create a EventsRepositoryAdapter::class if you want only to have certain events dispatched.

Create Eventer

use Psr\EventDispatcher\EventDispatcherInterface;
use Tobento\Service\Repository\EventerInterface;
use Tobento\Service\Repository\Eventer;

$eventer = new Eventer(
 eventDispatcher: $eventDispatcher, // EventDispatcherInterface
);

var_dump($eventer instanceof EventerInterface);
// bool(true)

Using Eventer

use Tobento\Service\Repository\EventerInterface;

class SomeService
{
 public function createAction(EventerInterface $eventer)
 {
 $entity = $eventer
 ->repository($this->someRepository)
 ->create(['title' => 'Lorem']);
 
 // or
 $entity = $eventer
 ->repository(
 repository: $this->someRepository,
 immutableAttributes: true, // default is false
 )
 ->create(['title' => 'Lorem']); 
 }
}

Credits