Index

PDORepository

MMO\pdo · 1 symbol · generated 5 Oct 2026

class

PDORepository

PDORepository.php:37

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://mcp.mmoframework.fr/documentation/mmo.pdo/php/PDORepository.md#pdorepository-addtable) registers both at once; [`setCollections`](https://mcp.mmoframework.fr/documentation/mmo.core/php/classes/aRepository.md#arepository-setcollections) still works for a collection whose table was already declared on the handler.

Index

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.
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)
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.
queryColumns($query, $table)The columns a query should actually SELECT.
rowId($class, $row)
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()
unknownColumns($filter, $table, $unknown)Collect the filter's column references that this table does not have.
unregister()Detach this repository, and its collections, from the global Commit / Fetch events.
__construct($handler, $collections)

Properties4

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://mcp.mmoframework.fr/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.

Methods25

addTable($table, $name)

PDORepository.php:149

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:485

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://mcp.mmoframework.fr/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://mcp.mmoframework.fr/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:213
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://mcp.mmoframework.fr/documentation/mmo.pdo/php/PDORepository.md#pdorepository-nextpendingid)) that [`commitEntity`](https://mcp.mmoframework.fr/documentation/mmo.pdo/php/PDORepository.md#pdorepository-commitentity) swaps for the real one; any other single primary key is generated here ([`generateId`](https://mcp.mmoframework.fr/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:169

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:328

Run a query against the database.

function executeQuery(MMO\core\entities\Query $query, array $opts = []): arrayWARNING: called by the [`Query`](https://mcp.mmoframework.fr/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

exportRow($table, $values)

PDORepository.php:424
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:414

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:248

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:161
function getTable(MMO\core\interfaces\iCollection|string $collection): MMO\pdo\helpers\Table
$collectioniCollection | string
returnsTablethe table behind a collection
throwsif there is none

install()

PDORepository.php:131

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:200
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:369

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:59
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:441

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

queryColumns($query, $table)

PDORepository.php:280

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://mcp.mmoframework.fr/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://mcp.mmoframework.fr/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

setTables($tables)

PDORepository.php:110

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']], ]

unknownColumns($filter, $table, $unknown)

PDORepository.php:306

Collect the filter's column references that this table does not have.

protected function unknownColumns( MMO\core\interfaces\iFilter $filter, MMO\pdo\helpers\Table $table, array &$unknown = [] ): arrayA qualified name (`t2.x`) is left alone: it belongs to a joined table, which this cannot see.
$filteriFilter
$tableTable
$unknown = []array
returnsarray

unregister()

PDORepository.php:73

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

function unregister(): voidA repository subscribes to them on construction ([`aRepository::__wakeup`](https://mcp.mmoframework.fr/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. Not implemented at this level -- a concrete repository backed by a search-capable store should override this.
setCollections($cols)aRepositoryRegister a batch of collections at once, e.g.
__wakeup()aRepositoryRegister this repository's onCommit / onFetch as listeners on the global