You are browsing a version that is no longer maintained.

Architecture

This chapter gives an overview of the overall architecture, terminology and constraints of Doctrine ORM. It is recommended to read this chapter carefully.

Using an Object-Relational Mapper

As the term ORM already hints at, Doctrine ORM aims to simplify the translation between database rows and the PHP object model. The primary use case for Doctrine are therefore applications that utilize the Object-Oriented Programming Paradigm. For applications that do not primarily work with objects Doctrine ORM is not suited very well.

Requirements

Doctrine ORM requires a minimum of PHP 7.1. For greatly improved performance it is also recommended that you use APC with PHP.

Doctrine ORM Packages

Doctrine ORM is divided into three main packages.

  • Common
  • DBAL (includes Common)
  • ORM (includes DBAL+Common)

This manual mainly covers the ORM package, sometimes touching parts of the underlying DBAL and Common packages. The Doctrine code base is split in to these packages for a few reasons and they are to...

  • ...make things more maintainable and decoupled
  • ...allow you to use the code in Doctrine Common without the ORM or DBAL
  • ...allow you to use the DBAL without the ORM

The Common Package

The Common package contains highly reusable components that have no dependencies beyond the package itself (and PHP, of course). The root namespace of the Common package is Doctrine\Common.

The DBAL Package

The DBAL package contains an enhanced database abstraction layer on top of PDO but is not strongly bound to PDO. The purpose of this layer is to provide a single API that bridges most of the differences between the different RDBMS vendors. The root namespace of the DBAL package is Doctrine\DBAL.

The ORM Package

The ORM package contains the object-relational mapping toolkit that provides transparent relational persistence for plain PHP objects. The root namespace of the ORM package is Doctrine\ORM.

Terminology

Entities

An entity is a lightweight, persistent domain object. An entity can be any regular PHP class observing the following restrictions:

  • An entity class must not be final or contain final methods.
  • All persistent properties/field of any entity class should always be private or protected, otherwise lazy-loading might not work as expected. In case you serialize entities (for example Session) properties should be protected (See Serialize section below).
  • An entity class must not implement __clone or do so safely.
  • An entity class must not implement __wakeup or do so safely. You can also consider implementing Serializable, but be aware that it is deprecated since PHP 8.1. We do not recommend its usage.
  • PHP 7.4 introduces the new magic method __unserialize, which changes the execution priority between __wakeup and itself when used. This can cause unexpected behaviour in an Entity.
  • Any two entity classes in a class hierarchy that inherit directly or indirectly from one another must not have a mapped property with the same name. That is, if B inherits from A then B must not have a mapped field with the same name as an already mapped field that is inherited from A.
  • An entity cannot make use of func_get_args() to implement variable parameters. Generated proxies do not support this for performance reasons and your code might actually fail to work when violating this restriction.
  • Entity cannot access private/protected properties/methods of another entity of the same class or do so safely.

Entities support inheritance, polymorphic associations, and polymorphic queries. Both abstract and concrete classes can be entities. Entities may extend non-entity classes as well as entity classes, and non-entity classes may extend entity classes.

The constructor of an entity is only ever invoked when you construct a new instance with the new keyword. Doctrine never calls entity constructors, thus you are free to use them as you wish and even have it require arguments of any type.

Entity states

An entity instance can be characterized as being NEW, MANAGED, DETACHED or REMOVED.

  • A NEW entity instance has no persistent identity, and is not yet associated with an EntityManager and a UnitOfWork (i.e. those just created with the "new" operator).
  • A MANAGED entity instance is an instance with a persistent identity that is associated with an EntityManager and whose persistence is thus managed.
  • A DETACHED entity instance is an instance with a persistent identity that is not (or no longer) associated with an EntityManager and a UnitOfWork.
  • A REMOVED entity instance is an instance with a persistent identity, associated with an EntityManager, that will be removed from the database upon transaction commit.

Persistent fields

The persistent state of an entity is represented by instance variables. An instance variable must be directly accessed only from within the methods of the entity by the entity instance itself. Instance variables must not be accessed by clients of the entity. The state of the entity is available to clients only through the entity’s methods, i.e. accessor methods (getter/setter methods) or other business methods.

Collection-valued persistent fields and properties must be defined in terms of the Doctrine\Common\Collections\Collection interface. The collection implementation type may be used by the application to initialize fields or properties before the entity is made persistent. Once the entity becomes managed (or detached), subsequent access must be through the interface type.

Serializing entities

Serializing entities can be problematic and is not really recommended, at least not as long as an entity instance still holds references to proxy objects or is still managed by an EntityManager. If you intend to serialize (and unserialize) entity instances that still hold references to proxy objects you may run into problems with private properties because of technical limitations. Proxy objects implement __sleep and it is not possible for __sleep to return names of private properties in parent classes. On the other hand it is not a solution for proxy objects to implement Serializable because Serializable does not work well with any potential cyclic object references (at least we did not find a way yet, if you did, please contact us). The Serializable interface is also deprecated beginning with PHP 8.1.

The EntityManager

The EntityManager class is a central access point to the functionality provided by Doctrine ORM. The EntityManager API is used to manage the persistence of your objects and to query for persistent objects.

Transactional write-behind

An EntityManager and the underlying UnitOfWork employ a strategy called transactional write-behind that delays the execution of SQL statements in order to execute them in the most efficient way and to execute them at the end of a transaction so that all write locks are quickly released. You should see Doctrine as a tool to synchronize your in-memory objects with the database in well defined units of work. Work with your objects and modify them as usual and when you're done call EntityManager#flush() to make your changes persistent.

The Unit of Work

Internally an EntityManager uses a UnitOfWork, which is a typical implementation of the Unit of Work pattern, to keep track of all the things that need to be done the next time flush is invoked. You usually do not directly interact with a UnitOfWork but with the EntityManager instead.