Patterns Behind Doctrine (Prototype Slides)

Patterns Behind Doctrine (Prototype Slides)

This is the earliest version of my talk about the internal workings of Doctrine ORM and how they relate to your code when using it. I mostly explain things inside the Unit Of Work, e.g. Object Hashes, Change Sets and Commit Order. The live demo showcasing fetch modes and query optimization is not included and the gifs are missing as well (sorry).

Please keep in mind that this is kind of a sneak preview based on the ground work for the "actual" talk and the final slides will look nothing like this. I figured it might be interesting for people to see how the slides will evolve over time.

6a1345d8e6dd15b2c78eff0c331963b1?s=128

Denis Brumann

November 28, 2018
Tweet

Transcript

  1. User Group Berlin powered by

  2. THE PATTERNS BEHIND DOCTRINE Denis Brumann Draft (that will probably

    look nothing like the actual talk)
  3. DOCTRINE 2 DOCTRINE 3

  4. None
  5. /** @ORM\Entity() */ class Order { /** * @ORM\Id() *

    @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\OneToMany(targetEntity="App\Entity\OrderItem", mappedBy="order") */ private $items; /** * @ORM\Column(type="datetime_immutable") */ private $createdOn; }
  6. /** @ORM\Entity() */ class OrderItem { /** * @ORM\Id() *

    @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="items") * @ORM\JoinColumn(nullable=false) */ private $order; /** @ORM\Column() */ private $name; /** @ORM\Column(length=16) */ private $price; }
  7. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $manager->flush();

  8. None
  9. /** @ORM\Entity() */ class OrderItem { /** * @ORM\Id() *

    @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\ManyToOne(targetEntity="App\Entity\Order", inversedBy="items") * @ORM\JoinColumn(nullable=false) */ private $order; /** @ORM\Column() */ private $name; /** @ORM\Column(length=16) */ private $price; }
  10. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  11. None
  12. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  13. None
  14. Unit Of Work A Unit of Work keeps track of

    everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work.
  15. You usually do not directly interact with a UnitOfWork but

    with the EntityManager instead. Unit Of Work
  16. Unit Of Work What changes to write in what order.

    Identity Map Change Sets Cascading Operations (Persist, Delete, ...) Associations Commit Order
  17. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  18. export XDEBUG_CONFIG="idekey=PHPSTORM"; php -d xdebug.remote_enable=1 bin/console …

  19. export XDEBUG_CONFIG="idekey=PHPSTORM"; php -d xdebug.remote_enable=1 bin/console …

  20. $oid = spl_object_hash($entity) This function returns a unique identifier for

    the object. This id can be used as a hash key for storing objects, or for identifying an object, as long as the object is not destroyed. Once the object is destroyed, its hash may be reused for other objects.
  21. $oid = "00000000600cd8330000000053755686"

  22. $item1 = new OrderItem(); $item1->setName('Socks'); $item1->setPrice('999'); $item2 = new OrderItem();

    $item2->setName('Socks'); $item2->setPrice('999'); $manager->persist($item1); $manager->persist($item2);
  23. $item1 == $item2 ! true $item1 === $item2 ! false

  24. Unit Of Work uses object hashes to identify entities, not

    their id (most of the time)
  25. Change Sets Computes all the changes that have been done

    to entities and collections since the last commit and stores these changes in the $entityChangeSet map temporarily for access by the persisters, until the UoW commit is finished.
  26. None
  27. { "00000000085bc321000000004502eb13": { "createdOn": [ null,
 {
 "date": "2018-12-20 20:49:32.824391",


    "timezone_type": 3,
 "timezone": "Europe\/Berlin"
 }
 ] } }
  28. None
  29. None
  30. None
  31. None
  32. Entities will be persisted, when they are part of the

    Change Set, i.e. we explicitly persisted them, or when they are part of a cascaded association.
  33. Doctrine does not save unintentionally persisted associations.

  34. /** @ORM\Entity() */ class Order { /** * @ORM\Id() *

    @ORM\Column(type="integer") * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @ORM\OneToMany(..., cascade={"persist"}) */ private $items; /** * @ORM\Column(type="datetime_immutable") */ private $createdOn; }
  35. { "00000000085bc321000000004502eb13": { "createdOn": [
 null,
 {
 "date": "2018-12-20 20:49:32.824391",


    "timezone_type": 3,
 "timezone": "Europe\/Berlin"
 }
 ] }, "00000000085bc37e000000004502eb13": { "status": [null, 0], "name": [null, "Chewing gums"], "price": [null, "249"], "order": [null, {App\Entity\Order}] } }
  36. { "00000000085bc321000000004502eb13": { "createdOn": [
 null,
 {
 "date": "2018-12-20 20:49:32.824391",


    "timezone_type": 3,
 "timezone": "Europe\/Berlin"
 }
 ] }, "00000000085bc37e000000004502eb13": { "status": [null, 0], "name": [null, "Chewing gums"], "price": [null, "249"], "order": [null, {App\Entity\Order}] } } Order OrderItem
  37. Entity States 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).
  38. Entity States /** * An entity is new if it

    has just been instantiated (i.e. using the "new" operator) * and is not (yet) managed by an EntityManager. */ const STATE_NEW = 2;
  39. Entity States A MANAGED entity instance is an instance with

    a persistent identity that is associated with an EntityManager and whose persistence is thus managed.
  40. Entity States /** * An entity is in MANAGED state

    when its persistence is managed by an EntityManager. */ const STATE_MANAGED = 1;
  41. Entity States A DETACHED entity instance is an instance with

    a persistent identity that is not (or no longer) associated with an EntityManager and a UnitOfWork.
  42. Entity States /** * A detached entity is an instance

    with persistent state and identity that is not * (or no longer) associated with an EntityManager (and a UnitOfWork). */ const STATE_DETACHED = 3;
  43. Entity States 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.
  44. Entity States /** * A removed entity instance is an

    instance with a persistent identity, * associated with an EntityManager, whose persistent state will be deleted * on commit. */ const STATE_REMOVED = 4;
  45. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  46. Managed?

  47. None
  48. None
  49. None
  50. $order = new Order(); $order->setId(1); $this->entityManager->flush();

  51. None
  52. Only entities managed by the Unit Of Work will be

    inserted/updated/ deleted during flush
  53. COMMIT ORDER

  54. None
  55. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  56. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  57. $item = new OrderItem(); $item->setName('Socks'); $item->setPrice('999'); $manager->persist($item); $order = new

    Order(); $order->addItem($item); $manager->persist($order); $manager->flush();
  58. Doctrine will take care of correctly ordering each commit. You

    can persist your entities in any order you like.
  59. New entities either have to be persisted manually or through

    cascaded associations. An ID does not mean the entity is managed by Doctrine. Operations can be ordered by domain logic, rather than persistence logic. Doctrine will figure out the correct order for insert (update or delete).
  60. Merging Objects Fetching Strategies & Lazy Loading Object Hydration