What to expect when Doctrine 3 comes out

6a1345d8e6dd15b2c78eff0c331963b1?s=47 Denis Brumann
September 13, 2019

What to expect when Doctrine 3 comes out

Doctrine ORM is probably the most used database abstraction in PHP. With Doctrine 3 on the horizon it's a perfect time to look at how Doctrine has changed and will change and what this means for you as users. This talk looks at some of the already merged features for Doctrine 3 that could have an impact on your code and why the might prevent you from upgrading. I will show approaches for tackling these changes and how your projects might benefit from introducing them already.

6a1345d8e6dd15b2c78eff0c331963b1?s=128

Denis Brumann

September 13, 2019
Tweet

Transcript

  1. WHAT TO EXPECT WHEN DOCTRINE 3 COMES OUT

  2. @dbrumann / denis.brumann@sensiolabs.de 2

  3. @dbrumann / denis.brumann@sensiolabs.de 3

  4. WHEN WILL IT BE RELEASED? @dbrumann / denis.brumann@sensiolabs.de 4

  5. WHAT I WILL COVER… WHAT HAS CHANGED? HOW HAS IT

    CHANGED? HOW TO ADAPT FOR THE CHANGE? @dbrumann / denis.brumann@sensiolabs.de 5
  6. ONLY A SELECTION NO "UNDER THE HOOD"-DETAILS @dbrumann / denis.brumann@sensiolabs.de

    6
  7. THANKS TO 7

  8. denis.brumann@sensiolabs.de 8

  9. ROADMAP MAPPING UUID GENERATOR NAMED QUERIES NAMESPACE ALIASES PROXY OBJECTS

    CLASS METADATA ENTITY MANAGER SECOND LEVEL CACHE @dbrumann / denis.brumann@sensiolabs.de 10
  10. MAPPING @dbrumann / denis.brumann@sensiolabs.de 11

  11. @dbrumann / denis.brumann@sensiolabs.de 12

  12. @dbrumann / denis.brumann@sensiolabs.de 13

  13. @dbrumann / denis.brumann@sensiolabs.de 14

  14. YAML-MAPPING IS DEPRECATED IN 2.6 WILL BE REMOVED IN DOCTRINE

    3 SYMFONY BEST PRACTICES: ANNOTATIONS @dbrumann / denis.brumann@sensiolabs.de 15
  15. use Doctrine\ORM\Mapping as ORM; @dbrumann / denis.brumann@sensiolabs.de 16

  16. use Doctrine\ORM\Annotation as ORM; ANNOTATION-MAPPING @dbrumann / denis.brumann@sensiolabs.de 17

  17. UUID GENERATOR @dbrumann / denis.brumann@sensiolabs.de 18

  18. /** @ORM\Entity */ class Customer { /** * @ORM\Id *

    @ORM\Column(type="guid") * @ORM\GeneratedValue(strategy="UUID") */ private $id; … @dbrumann / denis.brumann@sensiolabs.de 19
  19. composer require ramsey/uuid-doctrine UUID GENERATOR @dbrumann / denis.brumann@sensiolabs.de 20

  20. /** @ORM\Entity */ class Customer { /** * @ORM\Id *

    @ORM\Column(type="uuid") * @ORM\GeneratedValue(strategy="NONE") */ private $id; public function __construct() { $this->id = Uuid::uuid4(); } public function getId(): string { return (string) $this->id; } @dbrumann / denis.brumann@sensiolabs.de 21
  21. WILL BE REMOVED IN DOCTRINE 3 USE UUID-LIB (E.G. RAMSEY/UUID)

    + NONE-GENERATOR UUID GENERATOR @dbrumann / denis.brumann@sensiolabs.de 22
  22. NAMED (NATIVE) QUERIES @dbrumann / denis.brumann@sensiolabs.de 23

  23. 24

  24. 25

  25. class AddressController extends AbstractController { public function index(EntityManagerInterface $entityManager): Response

    { $repository = $entityManager->getRepository(Address::class); $addresses = $repository->createNativeNamedQuery('findAll') ->getResult(); return $this->render('addresses/index.html.twig', [ 'addresses' => $addresses, ]); } } @dbrumann / denis.brumann@sensiolabs.de 26
  26. LIST OF REMOVED CLASSES Doctrine\ORM\Mapping\ NamedQueries NamedQuery NamedNativeQuery ColumnResult FieldResult

    EntityResult SqlResultSetMappings SqlResultSetMapping @dbrumann / denis.brumann@sensiolabs.de 27
  27. Doctrine\ORM\EntityManager:: createNamedQuery createNamedNativeQuery Doctrine\ORM\EntityRepository:: createNamedQuery createNamedNativeQuery LIST OF REMOVED METHODS

    @dbrumann / denis.brumann@sensiolabs.de 28
  28. WORKS FOR NOW WILL BE REMOVED IN DOCTRINE 3 MOVE

    LOGIC (E.G. TO REPOSITORY) NAMED QUERIES @dbrumann / denis.brumann@sensiolabs.de 29
  29. NAMESPACE ALIASES @dbrumann / denis.brumann@sensiolabs.de 30

  30. SELECT p FROM AppBundle:Product p WHERE p.price > :price @dbrumann

    / denis.brumann@sensiolabs.de 31
  31. SELECT p FROM AppBundle\Entity\Product p WHERE p.price > :price' @dbrumann

    / denis.brumann@sensiolabs.de 32
  32. Doctrine\ORM\Configuration:: addEntityNamespace getEntityNamespace setEntityNamespaces getEntityNamespaces Doctrine\ORM\Mappin\AbstractClassMetadataFactory:: getFqcnFromAlias LIST OF REMOVED

    METHODS @dbrumann / denis.brumann@sensiolabs.de 33
  33. POPULAR IN SYMFONY APPS < 3.x WILL BE REMOVED IN

    DOCTRINE 3! USE FULLY QUALIFIED CLASS NAME ::CLASS NAMESPACE ALIASES @dbrumann / denis.brumann@sensiolabs.de 34
  34. PROXY OBJECTS @dbrumann / denis.brumann@sensiolabs.de 35

  35. /** @ORM\Entity(…) */ class Customer { … /** * @ORM\OneToMany(targetEntity="App\Entity\Address",

    mappedBy="customer") */ private $addresses; … public function getAddresses() { return this->addresses; } } @dbrumann / denis.brumann@sensiolabs.de 36
  36. @dbrumann / denis.brumann@sensiolabs.de 37

  37. @dbrumann / denis.brumann@sensiolabs.de 38

  38. @dbrumann / denis.brumann@sensiolabs.de 39

  39. @dbrumann / denis.brumann@sensiolabs.de 40

  40. A proxy object is an object that is put in

    place or used instead of the "real" object. A proxy object can add behavior to the object being proxied without that object being aware of it. In Doctrine 2, proxy objects are used to realize several features but mainly for transparent lazy-loading. https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/advanced-configuration.html#proxy-objects PROXY OBJECTS @dbrumann / denis.brumann@sensiolabs.de 41
  41. @dbrumann / denis.brumann@sensiolabs.de 42

  42. <?php namespace Proxies\__CG__\App\Entity; /** * DO NOT EDIT THIS FILE

    - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR */ class Order extends \App\Entity\Order implements \Doctrine\ORM\Proxy\Proxy { /** * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with * three parameters, being respectively the proxy object to be initialized, the method that triggered the * initialization process and an array of ordered parameters that were passed to that method. * * @see \Doctrine\Common\Persistence\Proxy::__setInitializer */ public $__initializer__; /** * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object * * @see \Doctrine\Common\Persistence\Proxy::__setCloner */ public $__cloner__; /** * @var boolean flag indicating if this object was already initialized * * @see \Doctrine\Common\Persistence\Proxy::__isInitialized */ public $__isInitialized__ = false; /** * @var array properties to be lazy loaded, with keys being the property * names and values being their default values * * @see \Doctrine\Common\Persistence\Proxy::__getLazyProperties */ 43
  43. /** * * @return array */ public function __sleep() {

    if ($this->__isInitialized__) { return ['__isInitialized__', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'id', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'items', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'createdOn']; } return ['__isInitialized__', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'id', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'items', '' . "\0" . 'App\\Entity\\Order' . "\0" . 'createdOn']; } /** * */ public function __wakeup() { if ( ! $this->__isInitialized__) { $this->__initializer__ = function (Order $proxy) { $proxy->__setInitializer(null); $proxy->__setCloner(null); $existingProperties = get_object_vars($proxy); foreach ($proxy->__getLazyProperties() as $property => $defaultValue) { if ( ! array_key_exists($property, $existingProperties)) { $proxy->$property = $defaultValue; } } }; } } /** * */ 44
  44. WILL BE REPLACED WITH PROXYMANAGER-LIB (HOPEFULLY) NO CODE CHANGES ON

    YOUR PART PROXY OBJECTS @dbrumann / denis.brumann@sensiolabs.de 45
  45. NO MORE FATAL ERRORS DUE TO MISSING PROXY CLASSES PROXY

    OBJECTS @dbrumann / denis.brumann@sensiolabs.de 46
  46. final METHODS ARE NOW ALLOWED __clone NO LONGER CALLED BY

    ORM __wakeup NO LONGER CALLED BY ORM serialize() ⚠ RECURSIVE INITIALIZATION ACCESSING PRIVATE STATE WITH REFLECTION TRIGGERS LAZY-LOADING LAZY LOADING @dbrumann / denis.brumann@sensiolabs.de 47
  47. CLASS METADATA @dbrumann / denis.brumann@sensiolabs.de 48

  48. Doctrine\ORM\Mapping\ClassMetadata:: getQuotedColumnName getQuotedTableName getQuotedJoinTableName getQuotedIdentifierColumnNames LIST OF REMOVED METHODS @dbrumann

    / denis.brumann@sensiolabs.de 49
  49. Quoting is now always called. Implement your own Doctrine\ORM\Mapping\NamingStrategy to

    manipulate your schema, tables and column names to your custom desired naming convention. @dbrumann / denis.brumann@sensiolabs.de 50
  50. LOTS MORE CHANGES AROUND CLASSMETADATA MOSTLY REORGANIZATION AND RENAMING CLASS

    METADATA @dbrumann / denis.brumann@sensiolabs.de 52
  51. ENTITY MANAGER @dbrumann / denis.brumann@sensiolabs.de 53

  52. @dbrumann / denis.brumann@sensiolabs.de 54

  53. Doctrine\ORM\EntityManager:: flush($entity = null) merge($entity) detach($entity) copy($entity) ENTITY MANAGER @dbrumann

    / denis.brumann@sensiolabs.de 55
  54. Doctrine\ORM\EntityManager:: flush($entity = null) merge($entity) detach($entity) copy($entity) @dbrumann / denis.brumann@sensiolabs.de

    56
  55. FLUSH() CAN BE COSTLY DATA INTEGRITY BUGS FLUSHING A SINGLE

    ENTITY @dbrumann / denis.brumann@sensiolabs.de 57
  56. USE ENTITYMANAGER::CLEAR() MORE OFTEN USE EXPLICIT CHANGE TRACKING POLICIES @dbrumann

    / denis.brumann@sensiolabs.de 58 FLUSHING A SINGLE ENTITY
  57. DATA INTEGRITY ISSUES MERGE/DETACH @dbrumann / denis.brumann@sensiolabs.de 59

  58. EntityManager::detach() → EntityManager::clear() DETACH @dbrumann / denis.brumann@sensiolabs.de 60

  59. NO ALTERNATIVE PROVIDED SHOULD BE PART OF YOUR BUSINESS DOMAIN

    MERGE @dbrumann / denis.brumann@sensiolabs.de 61
  60. SECOND LEVEL CACHE @dbrumann / denis.brumann@sensiolabs.de 62

  61. The Second Level Cache is designed to reduce the amount

    of necessary database access. It sits between your application and the database to avoid the number of database hits as much as possible. SECOND LEVEL CACHE @dbrumann / denis.brumann@sensiolabs.de 63
  62. The second level cache functionality is marked as experimental for

    now. It is a very complex feature and we cannot guarantee yet that it works stable in all cases. SECOND LEVEL CACHE @dbrumann / denis.brumann@sensiolabs.de 64
  63. QUERY CACHE RESULT CACHE SECOND LEVEL CACHE DIFFERENTIATION @dbrumann /

    denis.brumann@sensiolabs.de 65
  64. COMPONENTS QUERY CACHE CACHED ENTITY PERSISTER CACHED COLLECTION PERSISTER ENTITY

    HYDRATOR COLLECTION HYDRATOR CACHE FACTORY @dbrumann / denis.brumann@sensiolabs.de 66
  65. DATA IS STORED IN DIFFERENT CACHE REGIONS EACH REGION HAS

    SPECIFIC NAMESPACE AND LIFETIME CACHE FOR QUERIES/COLLECTIONS ONLY CONTAINS IDENTIFIERS CACHE REGIONS @dbrumann / denis.brumann@sensiolabs.de 67
  66. CACHES ARE NOT AWARE OF CHANGES TO PERSISTENT STORE BY

    OTHER APPLICATIONS PAGINATION COUNT QUERIES ARE NOT CACHED NOT ALL CACHES WORK IN DISTRIBUTED ENVS LIMITATIONS @dbrumann / denis.brumann@sensiolabs.de 69
  67. 70 @dbrumann / denis.brumann@sensiolabs.de

  68. 71 @dbrumann / denis.brumann@sensiolabs.de

  69. RECAP @dbrumann / denis.brumann@sensiolabs.de 72

  70. RECAP MAPPING UUID GENERATOR NAMED QUERIES NAMESPACE ALIASES PROXY OBJECTS

    CLASS METADATA ENTITY MANAGER SECOND LEVEL CACHE @dbrumann / denis.brumann@sensiolabs.de 73
  71. MAPPING NO MORE YAML-MAPPING @dbrumann / denis.brumann@sensiolabs.de 74

  72. ENTITY MANAGER BEHAVIOR CHANGES FOR FLUSH NO MORE DETACH, MERGE

    & COPY @dbrumann / denis.brumann@sensiolabs.de 75
  73. SECOND LEVEL CACHE "REPLACEMENT" FOR SINGLE ENTITY FLUSH, MERGE, DETACH

    @dbrumann / denis.brumann@sensiolabs.de 76
  74. THANK YOU