SqliteRepository
SQLite document-store {@see RepositoryInterface}: the same six-method contract as {@see FileRepository}, over a real database file — and still zero migrations, because the table is not a schema. Each entity class gets one table of two payload columns — `id TEXT` plus `doc TEXT` holding the entity's `toArray()` as JSON — created lazily with `CREATE TABLE IF NOT EXISTS` on first use. The `toArray()`/`fromArray()` pair IS the schema: adding a field to an entity never touches the database. The table name derives from the entity class: snake_case of the short class name plus a crc32 suffix of the fully-qualified name — `App\Blog\Post` becomes `post_2f1f1893`. The suffix is what lets two classes with the same short name share one database file without silently sharing a table. Ids live in the `id TEXT` column through their string form, so — as with the PHP array keys backing {@see FileRepository} — `1` and `'1'` name the same row; the typed id survives inside the JSON document. Insertion order of {@see self::all()} rides an internal `seq INTEGER PRIMARY KEY AUTOINCREMENT` column; re-saving an existing id updates its row in place, so `seq` — and with it the entity's position — never moves. Concurrency is SQLite's own locking, the way `flock` is {@see FileRepository}'s: every save runs inside an immediate transaction held across id assignment and the write, so concurrent processes can neither mint the same fresh id nor lose each other's rows. Needs `ext-pdo_sqlite` (suggested, not required, by the package — the other backends need no extension). A missing extension, or a database path that cannot be created or opened, fails with an error that says what broke, why it matters, and how to fix it.
SqliteRepository::__construct()
public function __construct(string $dbPath, string $entityClass):Parameters
| Name | Type | Description |
|---|---|---|
| $dbPath | string | path to the SQLite database file; the file — and its directory — are created on first use if missing |
| $entityClass | class-string<T> | the entity class rows are rehydrated into via `fromArray()` |
SqliteRepository::find()
public function find(string|int $id): ?Milpa\Data\EntityInterfaceThe entity stored under `$id`, or `null` when no entity is stored under it.
Parameters
| Name | Type | Description |
|---|---|---|
| $id | string|int |
SqliteRepository::save()
public function save(Milpa\Data\EntityInterface $entity): string|intPersists `$entity` as one JSON document row, inside an immediate transaction held across id assignment and the write — so a concurrent saver against the same file can neither mint the same fresh id nor be overwritten. When `$entity->id()` is `null`, a fresh id is assigned via {@see self::nextId()}. Re-saving an existing id updates its row in place, keeping the entity's insertion position in {@see self::all()}. The stored document always carries the id actually used under the key `'id'`, regardless of what `$entity->toArray()` returned for it.
Parameters
| Name | Type | Description |
|---|---|---|
| $entity | T |
SqliteRepository::delete()
public function delete(string|int $id): voidRemoves the row stored under `$id`. A no-op when no entity is stored under it.
Parameters
| Name | Type | Description |
|---|---|---|
| $id | string|int |
SqliteRepository::all()
public function all(): arrayEvery stored entity, in insertion order — rows come back ordered by the internal autoincrement `seq` column, which records the order ids were first saved regardless of the ids themselves.
SqliteRepository::nextId()
public function nextId(): intThe id to assign to the next entity saved without one of its own — one past the highest integer id currently stored, or `1` when the store holds no integer id. Ids live in a TEXT column, so the maximum is computed in PHP over the fetched ids, exactly like `FileRepository` does over its array keys — a stored `'42'` counts as the integer it names, `'custom-id'` does not.
SqliteRepository::query()
public function query(array $criteria): arrayStored entities matching every `$criteria` pair by strict equality. Deliberately filtered in PHP after fetching every row — not pushed into SQL — so equality semantics stay exactly {@see FileRepository}'s (`===` over the decoded `toArray()` values, JSON types intact), at the cost of reading the whole table. For the collection sizes this backend targets that is the right trade; a SQL-side filter could come later without touching the contract.
Parameters
| Name | Type | Description |
|---|---|---|
| $criteria | array<string, mixed> |