change-tracking-policies.rst 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. Change Tracking Policies
  2. ========================
  3. Change tracking is the process of determining what has changed in
  4. managed entities since the last time they were synchronized with
  5. the database.
  6. Doctrine provides 3 different change tracking policies, each having
  7. its particular advantages and disadvantages. The change tracking
  8. policy can be defined on a per-class basis (or more precisely,
  9. per-hierarchy).
  10. Deferred Implicit
  11. ~~~~~~~~~~~~~~~~~
  12. The deferred implicit policy is the default change tracking policy
  13. and the most convenient one. With this policy, Doctrine detects the
  14. changes by a property-by-property comparison at commit time and
  15. also detects changes to entities or new entities that are
  16. referenced by other managed entities ("persistence by
  17. reachability"). Although the most convenient policy, it can have
  18. negative effects on performance if you are dealing with large units
  19. of work (see "Understanding the Unit of Work"). Since Doctrine
  20. can't know what has changed, it needs to check all managed entities
  21. for changes every time you invoke EntityManager#flush(), making
  22. this operation rather costly.
  23. Deferred Explicit
  24. ~~~~~~~~~~~~~~~~~
  25. The deferred explicit policy is similar to the deferred implicit
  26. policy in that it detects changes through a property-by-property
  27. comparison at commit time. The difference is that Doctrine 2 only
  28. considers entities that have been explicitly marked for change detection
  29. through a call to EntityManager#persist(entity) or through a save
  30. cascade. All other entities are skipped. This policy therefore
  31. gives improved performance for larger units of work while
  32. sacrificing the behavior of "automatic dirty checking".
  33. Therefore, flush() operations are potentially cheaper with this
  34. policy. The negative aspect this has is that if you have a rather
  35. large application and you pass your objects through several layers
  36. for processing purposes and business tasks you may need to track
  37. yourself which entities have changed on the way so you can pass
  38. them to EntityManager#persist().
  39. This policy can be configured as follows:
  40. .. code-block:: php
  41. <?php
  42. /**
  43. * @Entity
  44. * @ChangeTrackingPolicy("DEFERRED_EXPLICIT")
  45. */
  46. class User
  47. {
  48. // ...
  49. }
  50. Notify
  51. ~~~~~~
  52. This policy is based on the assumption that the entities notify
  53. interested listeners of changes to their properties. For that
  54. purpose, a class that wants to use this policy needs to implement
  55. the ``NotifyPropertyChanged`` interface from the Doctrine
  56. namespace. As a guideline, such an implementation can look as
  57. follows:
  58. .. code-block:: php
  59. <?php
  60. use Doctrine\Common\NotifyPropertyChanged,
  61. Doctrine\Common\PropertyChangedListener;
  62. /**
  63. * @Entity
  64. * @ChangeTrackingPolicy("NOTIFY")
  65. */
  66. class MyEntity implements NotifyPropertyChanged
  67. {
  68. // ...
  69. private $_listeners = array();
  70. public function addPropertyChangedListener(PropertyChangedListener $listener)
  71. {
  72. $this->_listeners[] = $listener;
  73. }
  74. }
  75. Then, in each property setter of this class or derived classes, you
  76. need to notify all the ``PropertyChangedListener`` instances. As an
  77. example we add a convenience method on ``MyEntity`` that shows this
  78. behaviour:
  79. .. code-block:: php
  80. <?php
  81. // ...
  82. class MyEntity implements NotifyPropertyChanged
  83. {
  84. // ...
  85. protected function _onPropertyChanged($propName, $oldValue, $newValue)
  86. {
  87. if ($this->_listeners) {
  88. foreach ($this->_listeners as $listener) {
  89. $listener->propertyChanged($this, $propName, $oldValue, $newValue);
  90. }
  91. }
  92. }
  93. public function setData($data)
  94. {
  95. if ($data != $this->data) {
  96. $this->_onPropertyChanged('data', $this->data, $data);
  97. $this->data = $data;
  98. }
  99. }
  100. }
  101. You have to invoke ``_onPropertyChanged`` inside every method that
  102. changes the persistent state of ``MyEntity``.
  103. The check whether the new value is different from the old one is
  104. not mandatory but recommended. That way you also have full control
  105. over when you consider a property changed.
  106. The negative point of this policy is obvious: You need implement an
  107. interface and write some plumbing code. But also note that we tried
  108. hard to keep this notification functionality abstract. Strictly
  109. speaking, it has nothing to do with the persistence layer and the
  110. Doctrine ORM or DBAL. You may find that property notification
  111. events come in handy in many other scenarios as well. As mentioned
  112. earlier, the ``Doctrine\Common`` namespace is not that evil and
  113. consists solely of very small classes and interfaces that have
  114. almost no external dependencies (none to the DBAL and none to the
  115. ORM) and that you can easily take with you should you want to swap
  116. out the persistence layer. This change tracking policy does not
  117. introduce a dependency on the Doctrine DBAL/ORM or the persistence
  118. layer.
  119. The positive point and main advantage of this policy is its
  120. effectiveness. It has the best performance characteristics of the 3
  121. policies with larger units of work and a flush() operation is very
  122. cheap when nothing has changed.