Unit of Work & Identity Map
This content is for v1.0. Switch to the latest version for up-to-date documentation.
EntityManager tracks entity state (NEW → MANAGED → REMOVED) via UnitOfWork. Changes are collected by a ChangeAggregator and flushed as a batch. Each context (EntityManager instance) has its own IdentityMap — no process-wide singletons.
Lifecycle of a flush
Section titled “Lifecycle of a flush”- Entities are
persist()-ed or loaded viafind()/ a repository, entering theIdentityMapasMANAGED. - Property mutations are tracked by the
ChangeAggregatoragainst a change-tracking snapshot taken at load/persist time. flush()wraps all pending work in one transaction and computes the minimal set ofINSERT/UPDATE/DELETEstatements.- On success, the transaction commits and in-memory state (generated IDs, bumped version columns) is reconciled with what was written.
- On any throw, the transaction rolls back. In-memory mutations that happened inside the flush loop must be revertable, or the entity is left poisoned relative to the (reverted) database state.
Memory-efficient unit of work
Section titled “Memory-efficient unit of work”- Clear entities from memory that are no longer needed within specific operations via
$em->clear(). - Different units of work can track their own entities independently.
EntityManagercombines all unit-of-work changes into minimal database queries during flush.
Useful for processing large datasets, complex business operations spanning multiple contexts, and long-running processes with varying entity lifecycles.
Read replicas via separate contexts
Section titled “Read replicas via separate contexts”There’s no built-in read/write routing — intentionally. Let infrastructure handle it (PgBouncer, ProxySQL, RDS Proxy), or use the per-context design directly:
// Write context → primary$primary = new EntityManager($primaryConnection, ...);$primary->persist($entity);$primary->flush();
// Read context → replica$replica = new EntityManager($replicaConnection, ...);$users = $replica->findAll(User::class);Each EntityManager owns its IdentityMap and UnitOfWork — calling flush() on a replica-backed instance is a design smell, not a supported write path.
See also
Section titled “See also”- Performance guide — identity map, second-level cache, and batch iteration in practice.
- Optimistic Locking guide — recovery after a flush conflict without a poisoned unit of work.