MMOframework
Why AI Playground Libraries Documentation Search Agency
FR EN
Index

PDORepository

MMO\pdo · 1 symbol · generated 27 Oct 2026

class

PDORepository

PDORepository.php:38

A aRepository backed by a SQL database: one Table per collection.

class MMO\pdo\PDORepository extends MMO\core\classes\aRepository
Namespace
MMO\pdo
Extends
aRepository
```php $handler = new SqliteHandler('data/app.sqlite'); $handler->connect(); $users = new Table($handler, 'user'); $users->addFields(['id' => 'INTEGER PRIMARY KEY AUTOINCREMENT', 'name' => 'VARCHAR(100) NOT NULL']); $repo = new PDORepository($handler); $repo->addTable($users); $marc = $repo->getCollection('user')->createEntity(['name' => 'marc']); \MMO\commit($repo); // INSERT, and $marc->getId() is now the generated id foreach ($repo->getCollection('user')->query(['name' => 'marc']) as $user) { … } ``` A collection's name is the table's name. [`addTable`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-addtable) registers both at once; [`setCollections`](https://mmoframework.fr/en/documentation/mmo.core/php/classes/aRepository.md#arepository-setcollections) still works for a collection whose table was already declared on the handler.

Index

entities
handler
lastIds
pendingIdA placeholder id, handed to a new entity whose real id the DATABASE will generate.
tables
addTable($table, $name)Register a table and the collection that holds its rows.
commitEntity($entity, $commit, $counts)Persist one entity. Called from inside onCommit's transaction.
createEntity($class, $data, $collection)
debugName()
dropCollection($collection)Drop a collection's table, and forget the collection.
executeQuery($query, $opts)Run a query against the database.
exportFilterValue($operator, $value, $table, $column)A condition's value, converted as the column stores it (prepareFilter).
exportRow($table, $values)
exportValue($table, $column, $value)Convert one entity value into something a column can hold (PDOExporter), using the column's own type to decide a date's format.
generateId($collection)An id is not given twice by one repository: what is keyed by an id (a scope edms.files.12, a link, a cache) would apply to the next entity.
getHandler()
getTable($collection)
idKeys($class, $table)
install()Create any of this repository's tables that the database does not have yet.
IsPendingId($id) static
loadEntities($collection, $rows, $reload)Turn rows into entities, reusing the instance a row's id already has in the collection so the same record is never represented twice.
logsQueries()
nextPendingId()
onCommit($commit)Persist every entity of this commit that belongs to one of this repository's collections.
prepareCondition($path, $assert, $table, $collection)One condition of a filter (prepareFilter).
prepareFilter($filter, $table, $collection)Rewrite a query's filter into one this table can answer in SQL.
prepareId($operator, $value, $table, $collection)A condition on id for a table whose primary key has another name, or several columns (prepareCondition).
queryColumns($query, $table)The columns a query should actually SELECT.
referencedCollection($collection, $property)
references($collection)
rowId($class, $row)
setEntities($entities)Your own entity class for a table's rows, rather than one generated from its columns.
setHandler($handler)
setTables($tables)Declare this repository's tables from configuration, so an application describes its schema in one place instead of building every Table by hand.
uid()
unregister()Detach this repository, and its collections, from the global Commit / Fetch events.
__construct($handler, $collections)

Properties5

entities

#[MMO\core\attributes\Serialize(serialize: false, unserialize: 'setEntities', order: 95)] protected array $entities = []
Type
array
See
setEntities -- declared here so the configuration key entities reaches it.

handler

protected MMO\pdo\interfaces\iHandler $handler
Type
iHandler

lastIds

#[MMO\core\attributes\Serialize(false)] protected array $lastIds = []
Type
array

pendingId

A placeholder id, handed to a new entity whose real id the DATABASE will generate.

protected int $pendingId = 0
Type
int
The framework keys a collection by entity id and assumes every entity has one from birth (Mongo mints an ObjectId client-side; the in-memory Repository counts up). AUTO_INCREMENT breaks that: the id only exists after the INSERT. Leaving it empty is not an option -- [`tWithEntities::addEntity`](https://mmoframework.fr/en/documentation/mmo.core/php/classes/tWithEntities.md#twithentities-addentity) then files the entity under a positional integer key, which the NEXT entity's real id collides with, and one entity silently starts answering to another's id. Verified directly. A pending id is therefore negative: type-compatible with an integer key column, and unreachable by an AUTO_INCREMENT sequence, which always counts upward from 1.

tables

#[MMO\core\attributes\Serialize(serialize: false, unserialize: 'setTables', order: 90)] protected array $tables = []
Type
array
See
setTables -- declared here so the configuration key tables reaches it.

Methods32

addTable($table, $name)

PDORepository.php:179

Register a table and the collection that holds its rows.

function addTable(MMO\pdo\helpers\Table $table, ?string $name = null): MMO\core\interfaces\iCollection
$tableTable
$name = nullnull | stringthe collection's name; the table's own name by default
returnsiCollection

commitEntity($entity, $commit, $counts)

PDORepository.php:739

Persist one entity. Called from inside onCommit's transaction.

protected function commitEntity( MMO\core\interfaces\iEntity $entity, MMO\core\events\Commit $commit, array &$counts ): voidAn entity removed leaves its collection, and [`generateId`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-generateid) does not give its id again. One created and removed before being saved has no row: nothing is deleted, and nothing inserted. NOTE: until 2026-09 an entity removed stayed in its collection ([`Collection::getById`](https://mmoframework.fr/en/documentation/mmo.core/php/entities/Collection.md#collection-getbyid) found it), and one created and removed before being saved was inserted by the next commit.
$entityiEntity
$commitCommit
$countsarray
returnsvoid

createEntity($class, $data, $collection)

PDORepository.php:244
function createEntity( string $class, array $data = [], ?MMO\core\interfaces\iCollection $collection = null ): MMO\core\interfaces\iEntityA column the database fills in itself gets a placeholder ([`nextPendingId`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-nextpendingid)) that [`commitEntity`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-commitentity) swaps for the real one; any other single primary key is generated here ([`generateId`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-generateid)).
See
aRepository::createEntity, making sure the new entity has an id of some kind.
$classstring
$data = []array
$collection = nullnull | iCollection
returnsiEntity

dropCollection($collection)

PDORepository.php:200

Drop a collection's table, and forget the collection.

function dropCollection(MMO\core\interfaces\iCollection|string $collection): bool
$collectioniCollection | string
returnsbool

executeQuery($query, $opts)

PDORepository.php:554

Run a query against the database.

function executeQuery(MMO\core\entities\Query $query, array $opts = []): arrayWARNING: called by the [`Query`](https://mmoframework.fr/en/documentation/mmo.core/php/entities/Query.md#query) itself. Use `$collection->query(…)` / `$query->getEntities()`.
$queryQuery
$opts = []array- raw: {bool} return the rows as plain arrays instead of entities - reload: {bool} refresh an already-loaded entity from the row, instead of keeping it as is
returnsarray

exportFilterValue($operator, $value, $table, $column)

PDORepository.php:516

A condition's value, converted as the column stores it (prepareFilter).

protected function exportFilterValue( MMO\core\helpers\FilterOperator $operator, mixed $value, ?MMO\pdo\helpers\Table $table, ?string $column ): mixed
$operatorFilterOperator
$valuemixed
$tablenull | Tablenull for a value read from JSON, which is compared as it is
$columnnull | string
returnsmixed

exportRow($table, $values)

PDORepository.php:678
protected function exportRow(MMO\pdo\helpers\Table $table, array $values): array
$tableTable
$valuesarrayproperty => value
returnsarrayonly the values that map to a real column, each converted for storage

exportValue($table, $column, $value)

PDORepository.php:668

Convert one entity value into something a column can hold (PDOExporter), using the column's own type to decide a date's format.

protected function exportValue(MMO\pdo\helpers\Table $table, string $column, mixed $value): mixed
$tableTable
$columnstring
$valuemixed
returnsmixed

generateId($collection)

PDORepository.php:279

An id is not given twice by one repository: what is keyed by an id (a scope edms.files.12, a link, a cache) would apply to the next entity.

function generateId(MMO\core\interfaces\iCollection $collection): intWARNING: a later request only sees the rows: once the highest one was deleted, its id is given again there. A key the database generates (`INTEGER PRIMARY KEY AUTOINCREMENT`, `AUTO_INCREMENT`) never gives an id twice: give it to a table whose ids must not come back (docs/Repository.md, Ids).
$collectioniCollection
returnsintone more than the highest id in use -- counting both the rows already stored AND the entities already created in this collection, since several can be created before any of them is committed -- and than the ids of the entities removed ($lastIds).
$repository->generateId($codes); // 3 once the code 2, the highest, was removed: not 2 again

getTable($collection)

PDORepository.php:192
function getTable(MMO\core\interfaces\iCollection|string $collection): MMO\pdo\helpers\Table
$collectioniCollection | string
returnsTablethe table behind a collection
throwsif there is none

idKeys($class, $table)

PDORepository.php:484
protected function idKeys(string $class, MMO\pdo\helpers\Table $table): array
$classstringan entity class
$tableTableits table
returnsarrayarray<string>, 1: non-empty-string} the columns of its id (PDOEntity::ID_PROP), and what joins them

install()

PDORepository.php:136

Create any of this repository's tables that the database does not have yet.

function install(): arrayDeliberately explicit: creating tables as a side effect of connecting is the kind of thing that silently rebuilds a production schema, so nothing calls this on its own.
returnsarraythe names of the tables that were created

IsPendingId($id)

staticPDORepository.php:231
static function IsPendingId(mixed $id): bool
$idmixed@return bool whether $id is a placeholder waiting to be replaced by the database's own.
returnsbool

loadEntities($collection, $rows, $reload)

PDORepository.php:595

Turn rows into entities, reusing the instance a row's id already has in the collection so the same record is never represented twice.

function loadEntities( MMO\core\interfaces\iCollection $collection, iterable $rows, bool $reload = false ): array
$collectioniCollection
$rowsiterable
$reload = falseboolif true, refresh an already-loaded entity from its row
returnsarray

logsQueries()

PDORepository.php:64
function logsQueries(): bool
returnsboolwhether query logging is on -- i.e. whether a collection holding LogQuery entities is registered. Without one there is nowhere to put a log, and asking for one only raises eNotFound.

onCommit($commit)

PDORepository.php:695

Persist every entity of this commit that belongs to one of this repository's collections.

function onCommit(MMO\core\events\Commit $commit): voidEverything happens inside one transaction, so a failure part-way through leaves the database as it was rather than half-written.
$commitCommit
returnsvoid

prepareCondition($path, $assert, $table, $collection)

PDORepository.php:401

One condition of a filter (prepareFilter).

protected function prepareCondition( string $path, mixed $assert, MMO\pdo\helpers\Table $table, MMO\core\interfaces\iCollection $collection ): MMO\core\interfaces\iFilter|bool|null
$pathstring
$assertmixedthe condition: a value, or a FilterValue
$tableTable
$collectioniCollection
returnsiFilter | bool | nullthe condition for the table, true/false when the row does not decide it, null for a name the table cannot answer

prepareFilter($filter, $table, $collection)

PDORepository.php:351

Rewrite a query's filter into one this table can answer in SQL.

protected function prepareFilter( MMO\core\interfaces\iFilter $filter, MMO\pdo\helpers\Table $table, MMO\core\interfaces\iCollection $collection ): MMO\core\interfaces\iFilter|bool- A value is converted as it is stored ([`exportValue`](https://mmoframework.fr/en/documentation/mmo.pdo/php/PDORepository.md#pdorepository-exportvalue)): a date in the column's format, an entity as its id, an enum as its name. A date alone (`'2026-10-09'`) against a DATETIME column is that day's midnight, or its end for `$lte`/`$gt`, as [`FilterOperator::test`](https://mmoframework.fr/en/documentation/mmo.core/php/helpers/FilterOperator.md#filteroperator-test) reads it. - `id` is the primary key's column when the table has no `id` ([`PDOEntity::ID_PROP`](https://mmoframework.fr/en/documentation/mmo.pdo/php/helpers/PDOEntity.md#pdoentity-id-prop)), all of them for a composite key (`'1' . PDOEntity::ID_SEPARATOR . '2'`): `Collection::getById()` looks an entity up by `id`. - A property of the entity that has no column holds its default on every row (an entity's `status`): its condition is true or false for the whole table, and is decided here. - `author/name`, where `author` holds the id of an entity of another table of this repository, is `author IN (SELECT id FROM … WHERE name = …)`; any other nested path reads a column holding JSON. A name that is none of these cannot be honoured, and quietly dropping it would return MORE rows than were asked for, which is exactly the wrong way to fail: it is an error. A qualified name (`t2.x`) is left alone: it belongs to a joined table, which this cannot see.
$filteriFilter
$tableTable
$collectioniCollection
returnsiFilter | boolthe filter to give the table, or true/false when it matches every row or none
throwson a name that is neither a column nor a property
$this->prepareFilter(Filter::From(['status' => ['$ne' => 'ARCHIVE']]), $table, $collection); // true: no status column

prepareId($operator, $value, $table, $collection)

PDORepository.php:457

A condition on id for a table whose primary key has another name, or several columns (prepareCondition).

protected function prepareId( MMO\core\helpers\FilterOperator $operator, mixed $value, MMO\pdo\helpers\Table $table, MMO\core\interfaces\iCollection $collection ): MMO\core\interfaces\iFilter|bool|null
$operatorFilterOperator
$valuemixed
$tableTable
$collectioniCollection
returnsiFilter | bool | null
throwson an operator that cannot compare a composite id

queryColumns($query, $table)

PDORepository.php:311

The columns a query should actually SELECT.

protected function queryColumns(MMO\core\entities\Query $query, MMO\pdo\helpers\Table $table): arrayA Query's field list comes from the entity SCHEMA, which carries properties no table has -- `status`, `uid` and `metadata` are on every entity, and [`Query::listOnly`](https://mmoframework.fr/en/documentation/mmo.core/php/entities/Query.md#query-listonly) also asks for `label`. Asking the database for those is an error, so the list is intersected with the table's real columns; the primary key is always added back, because without it a row cannot be matched to its entity. So is the column of a hidden property ([`ArrayProperty::isHidden`](https://mmoframework.fr/en/documentation/mmo.core/php/attributes/ArrayProperty.md#arrayproperty-ishidden)): never shown, it is never among the fields, but its entity needs it all the same -- a password's hash, to check a password against.
$queryQuery
$tableTable
returnsarray

referencedCollection($collection, $property)

PDORepository.php:494
protected function referencedCollection(MMO\core\interfaces\iCollection $collection, string $property): ?MMO\core\interfaces\iCollection
$collectioniCollection
$propertystring
returnsnull | iCollectionthe collection of this repository whose entities $property references, if it does

references($collection)

PDORepository.php:635
protected function references(MMO\core\interfaces\iCollection $collection): array
$collectioniCollection
returnsarrayiCollection> its columns that reference another collection's entities, and that collection

setEntities($entities)

PDORepository.php:160

Your own entity class for a table's rows, rather than one generated from its columns. Before or after the table is declared, alike: a collection takes its entity class when its table is added, so a Table's own setEntityClass() called after addTable or setTables changes nothing -- this does.

function setEntities(array $entities): void
$entitiesarray
returnsvoid
throwsif a class is not an entity
$repository->setTables(['invoices' => [...], 'invoiceLines' => [...]]); $repository->setEntities(['invoices' => Invoice::class, 'invoiceLines' => InvoiceLine::class]); // from configuration: 'entities' => ['invoices' => 'App\entities\Invoice', ...], beside 'tables'

setTables($tables)

PDORepository.php:115

Declare this repository's tables from configuration, so an application describes its schema in one place instead of building every Table by hand.

function setTables(array $tables): void
$tablesarrayname => [column => sql definition, …], or name => Table for one already built
returnsvoid
'repo' => [ '$' => 'MMO\pdo\PDORepository', 'handler' => ['$' => '#db'], 'tables' => ['user' => ['id' => 'INTEGER PRIMARY KEY AUTOINCREMENT', 'name' => 'VARCHAR(100) NOT NULL']], ]

unregister()

PDORepository.php:78

Detach this repository, and its collections, from the global Commit / Fetch events.

function unregister(): voidA repository subscribes to them on construction ([`aRepository::__wakeup`](https://mmoframework.fr/en/documentation/mmo.core/php/classes/aRepository.md#arepository--wakeup)) and otherwise stays subscribed for the life of the process -- which is right for an application, but leaves every repository ever built listening in a test suite or a long-running worker that swaps connections. A detached repository is simply skipped by later commits.
returnsvoid
Inherited members (26)
PropertiesDeclared byDescription
collectionsaRepository
MethodsDeclared byDescription
addCollection($name, $collection)aRepositoryAdd collection
createCollection($name, $entityClass, $collectionId)aRepositoryCreate and register a plain Collection for entities of $entityClass.
createLog($type, $origin, $collection, $data, $filters, $sorts, $limit, $offset)aRepositoryBuild (but not dispatch) a LogQuery entity describing a data-mutating operation, stored in the repository's LogQuery collection (if one is registered).
forceCommit($origin, $entities)aRepository
getCollection($name)aRepository
getCollectionByClass($entityClass, $allowChildClass)aRepositoryFind the collection whose entities are (an exact match, or optionally a subclass of) $entityClass.
getCollectionName($collection)aRepository
getCollections()aRepository
getDistinct($collection, $key)aRepositoryEvery value one property takes across a collection, once each, in ascending order.
getEntities()aRepository
getIterator()aRepository\IteratorAggregate::getIterator -- iterates name => iCollection
hasCollection($collection)aRepository
hideLogData($collection, $data)aRepositoryWhat a repository logs is what it wrote (mmo.pdo's row, mmo.mongodb's document or its changes), hidden properties (ArrayProperty::isHidden) included, and a log is exported…
logDelete($origin, $collection, $data, $filters, $sorts, $limit, $offset)aRepositoryLog and dispatch a DELETE operation.
logInsert($origin, $collection, $data, $filters, $sorts, $limit, $offset)aRepositoryLog and dispatch an INSERT operation.
logStatus($origin, $collection, $data, $filters, $sorts, $limit, $offset)aRepositoryLog and dispatch a STATUS-change operation.
logUpdate($origin, $collection, $data, $filters, $sorts, $limit, $offset)aRepositoryLog and dispatch an UPDATE operation.
offsetExists($key)aRepository\ArrayAccess::offsetExists
offsetGet($key)aRepository\ArrayAccess::offsetGet
offsetSet($key, $value)aRepository\ArrayAccess::offsetSet -- equivalent to addCollection
offsetUnset($key)aRepository\ArrayAccess::offsetUnset
onFetch($event)aRepositoryListener for Fetch: resolves a repository-uid/collection-id#id (or repository-uid/collection-id#id/nested/path for an embedded value) URL, what Ent…
searchText($query, $text)aRepositoryFull-text search (?search[type]=$text of RepositoryController::routeGet), on top of the query's own filters, its sort and its page.
setCollections($cols)aRepositoryRegister a batch of collections at once, e.g.
__wakeup()aRepositoryRegister this repository's onCommit / onFetch as listeners on the global