A repository serves a simple purpose — it enumerates state machines matching the criteria of our query. In other words, it answers our queries with a state machine reference or with a set of state machine references.

Each state machine has a dedicated repository, which provides us a reference to a state machine of a given ID. This is done by the ref() method of SmalldbRepositoryInterface. It is the only way to create a reference.

Our To Do List application also needs a repository for the Task state machines:

<?php // src/StateMachine/TaskRepository.php
declare(strict_types = 1);

namespace App\StateMachine;

use Smalldb\StateMachine\SmalldbRepositoryInterface;
use Smalldb\StateMachine\SqlExtension\AbstractSqlRepository;
use Smalldb\StateMachine\SqlExtension\ReferenceDataSource\ReferenceQueryResult;

class TaskRepository extends AbstractSqlRepository implements SmalldbRepositoryInterface
	protected const REF_CLASS = Task::class;

	public function ref($id): Task
		/** @var Task $ref */
		$ref = parent::ref($id);
		return $ref;

	public function findAll(): ReferenceQueryResult
		return $this->createQueryBuilder()


The repository is fairly simple. The ref() method provides a reference to a Task state machine of given ID — we reimplement it only to provide a better type hint. The findAll() method enumerates all the tasks ordered by their descriptions.

AbstractSqlRepository provides us with a ReferenceQueryBuilder (via createQueryBuilder() method). This query builder extends Doctrine DBAL QueryBuilder with executeRef() method that, like the original execute() method, executes the query, but instead of returning a raw dataset (ResultStatement), it hydrates the result into state machine references (ReferenceQueryResult).

The createQueryBuilder() also populates the query builder with SELECT clauses to load all state machine’s properties.

Data Sources

Smalldb References use data source objects (implementing ReferenceDataSourceInterface) provided by the repository to load the state of a state machine and all the required data (properties).

The particular implementation of the data source depends on the underlying storage. In our case, the data source receives a ResultStatement from the executed query and fetches the data into the references. It is also configured with a query to obtain state machine data when the reference needs to reload them.

The data sources are mostly invisible components, hidden in the background. The typical repository implementation should interact only with the query builder, and the result objects.

SQL Extension

When we defined the TaskProperties class in the previous chapter, we used numerous SQL annotations to define which column of which SQL table should be mapped to which property. The SQL annotations @SQL\Column and @SQL\Id extend the state machine definition with SqlPropertyExtension which we can see in the Symfony Profiler:

Screenshot: Task Machine Properties in Profiler

All the SQL-related features of Smalldb are located in the SQL Extension. Therefore, we can easily remove this extension and use another extension to store our data in a completely different way. Each state machine can also use a different extension so that we can let one state machine access SQL database, and another to access an LDAP directory, for example.