Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Doctrine ORM - a (more) practical introduction

Doctrine ORM - a (more) practical introduction

An introduction to Doctrine ORM focussing on how to set up the ORM, configure entities and associations and an overview of other available features like DQL, lifecycle event or migrations.

Torsten Heinrich

July 18, 2017
Tweet

More Decks by Torsten Heinrich

Other Decks in Programming

Transcript

  1. Part 1
 Doctrine ORM Providing developers with a powerful alternative

    to SQL that maintains flexibility without requiring unnecessary code duplication
  2. What is an ORM? Object relational mapper (ORM) for PHP

    that sits on top of a powerful database abstraction layer (DBAL). One of its key features is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility without requiring unnecessary code duplication. (http://www.doctrine-project.org/projects.html) 4
  3. What is Doctrine? The Doctrine Project is the home to

    several PHP libraries primarily focused on database storage and object mapping. The core projects are a Object Relational Mapper (ORM) and the Database Abstraction Layer (DBAL) it is built upon. Doctrine has greatly benefited from concepts of the Hibernate ORM and has adapted them to fit the PHP language. (http://www.doctrine-project.org/index.html) • Around since 2006 with very stable, high-quality codebase • Support for both high-level and low-level database programming for all your use-cases • Large community and integrations with many different frameworks (Zend Framework, Symfony and more) 5
  4. What is Doctrine? • Doctrine DBAL (Database Abstract Layer): http://www.doctrine-project.org/projects/dbal.html

    • Doctrine Cache: http://www.doctrine-project.org/projects/cache.html • Doctrine Annotations: http://www.doctrine-project.org/projects/annotations.html • Doctrine Collections: http://www.doctrine-project.org/projects/collections.html • Doctrine Common: http://www.doctrine-project.org/projects/common.html • Doctrine MongoDB ODM (Object Document Manager): http://www.doctrine-project.org/projects/mongodb-odm.html • Doctrine CouchDB ODM: http://www.doctrine-project.org/projects/couchdb-odm.html 6
  5. Installation • Packages are available via Composer/Packagist • For Doctrine

    ORM use the package doctrine/orm • The current version is 2.5 • For Symfony support use the package doctrine/doctrine-bundle • The current version is 1.6 • For Doctrine migrations use the package doctrine/doctrine-migrations-bundle • The current version is 1.2 7
  6. Configuration • Multiple possibilities: XML, YAML, plain PHP • XML

    • Verbose • Schema validation, IDE support • YAML • Lean • Support will be removed with 3.0 (https://github.com/doctrine/doctrine2/pull/5932) 8
  7. DBAL configuration • Allows you to configure connections, driver-specific options,

    logging/profiling, slaves, custom types and more • If in doubt, use the configuration reference: https://symfony.com/doc/current/reference/ configuration/doctrine.html 9
  8. ORM configuration • Allows you to configure entity managers, caching,

    mappings, DQL functions, proxy classes and more • Each entity manager requires a DBAL connection • If in doubt, use the configuration reference: https://symfony.com/doc/current/reference/ configuration/doctrine.html 10
  9. Entities An object defined primarily by its identity is called

    an entity. Entities have special modelling and design considerations. They have life cycles that can radically change their form and content, but a thread of continuity must be maintained. Their identities must be defined so that they can be effectively tracked. Their class definitions, responsibilities, attributes and associations should evolve around who they are, rather than the particular attributes they carry. (Eric Evans, Domain-Driven Design - Tackling Complexity in the Heart of Software) 11
  10. Configuration • Multiple possibilities: Annotations, XML, YAML, plain PHP •

    Describing the structure using mapping metadata to determine how an entity should be stored in the database • For CRUD/REST services, annotation are sufficient • For rich domain models, XML/YAML is recommended • If in doubt, use the annotation reference: http://docs.doctrine-project.org/projects/doctrine-orm/en/ latest/reference/annotations-reference.html 12
  11. @Entity • Class-level annotation • Required to manage an entity

    with Doctrine ORM • Properties: • repositoryClass: FQCN of a custom repository class, recommended • readOnly: no change-tracking, persisting/removing still works, defaults to false 13
  12. @Table • Class-level annotation • Maps an entity to the

    table where it is persisted • Properties: • name: the name of the table, defaults to the class name • indexes: a list of indexes, defaults to empty • uniqueConstraints: a list of unique indexes, defaults to empty • schema: the name of the schema the table lives in 14
  13. @Id • Property-level annotation • Marks a property as the

    primary key • For composite primary keys, simply annotate multiple properties 15
  14. @GeneratedValue • Property-level annotation • Hints to the strategy that

    is used to create primary keys • Possible strategies are AUTO (MySQL, SQLite), SEQUENCE (Oracle, PostgreSQL), UUID, NONE and more • Properties: • strategy: the name of the strategy, defaults to AUTO 16
  15. @Column • Property-level annotation • Maps a property to a

    column in a database table • Properties: • type: the data type, defaults to string • name: the column name, defaults to the property name • unique: whether the column values are unique, defaults to false • nullable: whether the column values are NOT NULL, default to false • options: a list of additional options (DEFAULT, UNSIGNED, COLLATE) 17
  16. Associations • Map entities to other entities or collections of

    entities • Can be uni-directional or bi-directional • Bi-directional associations have an owning and an inverse side, uni-directional associations only have an owning side • Doctrine only checks the owning side for changes • Collections of entities have to be instances of Doctrine\Common\Collections\Collection 18
  17. @OneToOne • Property-level annotation • One entity refers to exactly

    one other entity • Can also be self-referencing • Properties: • targetEntity: the other reference entity • mappedBy: the owning side of the association • inversedBy: the inverse side of the association • cascade: which operations (persist, remove and more) should be cascaded to referenced entities 19
  18. @OneToMany • Property-level annotation • One entity refers to many

    other entities • Represents the inverse side of the association • Can also be self-referencing • Properties: • targetEntity: the other reference entity • mappedBy: the owning side of the association • cascade: which operations (persist, remove and more) should be cascaded to referenced entities 20
  19. @ManyToOne • Property-level annotation • The counter part to @OneToMany

    • Represents the owning side of the association • Properties: • targetEntity: the other reference entity • cascade: which operations (persist, remove and more) should be cascaded to referenced entities 21
  20. @ManyToMany • Property-level annotation • Many entities refers to many

    other entities • Requires a join table containing the primary keys from each side of the association • Can also be self-referencing • Properties: • targetEntity: the other reference entity • mappedBy: the owning side of the association • inversedBy: the inverse side of the association • cascade: which operations (persist, remove and more) should be cascaded to referenced entities 22
  21. Indexed associations • By default, collections of entities are indexed

    by numerical keys starting with 0 • DQL offers the INDEX BY keyword to index collections • The indexBy property lets you define indexes implicitly • Only use this feature with primary keys or columns with unique values 23
  22. Lazy associations • Associations are lazy by default • Use

    the fetch property to change this behaviour • Use EAGER to always load associations together with the entity • Can be slow/costly for huge collections, affects hydration performance • Use EXTRA_LAZY to optimise the performance for huge collections • Certain collection methods (contains, count, get, slice and more) trigger single SELECT statements instead of fully loading the collection • Once the collection is fully loaded, the default behaviour is used again 24
  23. Proxies • Doctrine generates and uses proxy classes for all

    referenced entities to enable lazy-loading functionality • Proxies extend the original entity class and implement Doctrine\ORM\Proxy\Proxy • Use Doctrine\Common\Util\ClassUtils to gather information about underlying classes • Be careful with using var_dump(), print_r() and others on such an object graph • Automatically generated in the development environment • In production, use bin/doctrine orm:generate-proxies 25
  24. Unit of Work • Doctrine keeps track of object reference

    using an identity map • Methods like persist() or remove() only tell Doctrine to track entities/changes • Only after calling flush() are changes executed against the database • Usually, Doctrine wraps all changes in a single transaction • Changes are executed in the correct order taking foreign-key constraints into consideration • Support for explicit transaction handling is available (explicit boundaries, closure-wrapping) • Support for optimistic and pessimistic locking is available 26
  25. Repositories A repository represents all objects of a certain type

    as a conceptual set. It acts like a collection, except with more elaborate querying ability. Objects of the appropriate type are added and removed, and the machinery behind the repository inserts them or deletes them from the database. This definition gathers a cohesive set of responsibilities for providing access to the roots of aggregates from early life cycle through the end. (Eric Evans, Domain-Driven Design - Tackling Complexity in the Heart of Software) 27
  26. Custom repositories • Repositories are created by using the $entityManager->getRepository()

    method • They offer generics methods like find(), findAll(), findBy() and more to retrieve entities • Custom repositories can be created using the repositoryClass property of the @Entity annotation • Classes should extend Doctrine\ORM\EntityRepository • Use factories to make them available in the DIC • Injection of a Doctrine\Common\Persistence\ManagerRegistry is also possible • No inheritance needed 28
  27. DQL • DQL (Doctrine Query Language) provides powerful querying capabilities

    over your object model • Supports SELECT, UPDATE and DELETE queries • Supports JOINs • Supports partial hydration and different hydration modes (HYDRATE_OBJECT, HYDRATE_ARRAY, HYDRATE_SCALAR and more) • DQL SELECT queries allow you to fetch parts that are otherwise not accessible (partials, aggregates and more) or to fetch entities and their associations in a more performant manner • DQL UPDATE/DELETE queries allow you to execute bulk changes 29
  28. Query Builder • Helps building DQL queries in several steps

    • Should be used in favour of writing string-based queries • Provides methods for statements like select(), from(), join(), where(), orderBy(), groupBy() and more • Allows for complex WHERE clauses using the expr() helper methods (orX(), andX(), eq(), lt(), gt() and more) • Allows for parameter binding using numeric placeholders (?1, ?2) and named placeholders (:id, :name) 30
  29. Events • The entity manager and the unit of work

    trigger a bunch of global and lifecycle events • Examples are prePersist/postPersist, preUpdate/postUpdate, preFlush/onFlush/postFlush and more • Entities have to be annotated with @HasLifecycleCallbacks • Each callback method has to be annotated with @PrePersist/@PostPersist etc. • Event arguments are available to access the entity manager and the unit of work • Entities can also be annotated with @EntityListeners • Each callback receives the entity and the event arguments as arguments 31
  30. Caching • Metadata cache • Query cache • Result cache

    • Second level cache (experimental) • Multiple backends available in the Doctrine Cache library (Memcache, Memcached, Redis, XCache and more) • Use ArrayCache for debugging/testing 32
  31. Metadata cache • Prevents parsing the class metadata on every

    request • Use bin/console doctrine:cache:clear-metadata to clear the cache 33
  32. Query cache • Prevents parsing DQL queries to their SQL

    counterparts multiple times • Use bin/console doctrine:cache:clear-query to clear the cache 34
  33. Migration • Migrations allow you to version your database schema

    and easily deploy changes to it • Changes are implemented as an ordered list of version files • Each version file must be named like Version.{1,255}\.php and contain up() and down() methods • Use bin/console doctrine:migrations:status to show the current migration status • Use bin/console doctrine:migrations:migrate to run migrations • Optional argument version, defaults to the latest version • Use aliases first, prev, next and latest to easily jump between versions 35