validation-of-entities.rst 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. Validation of Entities
  2. ======================
  3. .. sectionauthor:: Benjamin Eberlei <kontakt@beberlei.de>
  4. Doctrine 2 does not ship with any internal validators, the reason
  5. being that we think all the frameworks out there already ship with
  6. quite decent ones that can be integrated into your Domain easily.
  7. What we offer are hooks to execute any kind of validation.
  8. .. note::
  9. You don't need to validate your entities in the lifecycle
  10. events. Its only one of many options. Of course you can also
  11. perform validations in value setters or any other method of your
  12. entities that are used in your code.
  13. Entities can register lifecycle event methods with Doctrine that
  14. are called on different occasions. For validation we would need to
  15. hook into the events called before persisting and updating. Even
  16. though we don't support validation out of the box, the
  17. implementation is even simpler than in Doctrine 1 and you will get
  18. the additional benefit of being able to re-use your validation in
  19. any other part of your domain.
  20. Say we have an ``Order`` with several ``OrderLine`` instances. We
  21. never want to allow any customer to order for a larger sum than he
  22. is allowed to:
  23. .. code-block:: php
  24. <?php
  25. class Order
  26. {
  27. public function assertCustomerAllowedBuying()
  28. {
  29. $orderLimit = $this->customer->getOrderLimit();
  30. $amount = 0;
  31. foreach ($this->orderLines AS $line) {
  32. $amount += $line->getAmount();
  33. }
  34. if ($amount > $orderLimit) {
  35. throw new CustomerOrderLimitExceededException();
  36. }
  37. }
  38. }
  39. Now this is some pretty important piece of business logic in your
  40. code, enforcing it at any time is important so that customers with
  41. a unknown reputation don't owe your business too much money.
  42. We can enforce this constraint in any of the metadata drivers.
  43. First Annotations:
  44. .. code-block:: php
  45. <?php
  46. /**
  47. * @Entity
  48. * @HasLifecycleCallbacks
  49. */
  50. class Order
  51. {
  52. /**
  53. * @PrePersist @PreUpdate
  54. */
  55. public function assertCustomerAllowedBuying() {}
  56. }
  57. In XML Mappings:
  58. .. code-block:: xml
  59. <doctrine-mapping>
  60. <entity name="Order">
  61. <lifecycle-callbacks>
  62. <lifecycle-callback type="prePersist" method="assertCustomerallowedBuying" />
  63. <lifecycle-callback type="preUpdate" method="assertCustomerallowedBuying" />
  64. </lifecycle-callbacks>
  65. </entity>
  66. </doctrine-mapping>
  67. YAML needs some little change yet, to allow multiple lifecycle
  68. events for one method, this will happen before Beta 1 though.
  69. Now validation is performed whenever you call
  70. ``EntityManager#persist($order)`` or when you call
  71. ``EntityManager#flush()`` and an order is about to be updated. Any
  72. Exception that happens in the lifecycle callbacks will be cached by
  73. the EntityManager and the current transaction is rolled back.
  74. Of course you can do any type of primitive checks, not null,
  75. email-validation, string size, integer and date ranges in your
  76. validation callbacks.
  77. .. code-block:: php
  78. <?php
  79. class Order
  80. {
  81. /**
  82. * @PrePersist @PreUpdate
  83. */
  84. public function validate()
  85. {
  86. if (!($this->plannedShipDate instanceof DateTime)) {
  87. throw new ValidateException();
  88. }
  89. if ($this->plannedShipDate->format('U') < time()) {
  90. throw new ValidateException();
  91. }
  92. if ($this->customer == null) {
  93. throw new OrderRequiresCustomerException();
  94. }
  95. }
  96. }
  97. What is nice about lifecycle events is, you can also re-use the
  98. methods at other places in your domain, for example in combination
  99. with your form library. Additionally there is no limitation in the
  100. number of methods you register on one particular event, i.e. you
  101. can register multiple methods for validation in "PrePersist" or
  102. "PreUpdate" or mix and share them in any combinations between those
  103. two events.
  104. There is no limit to what you can and can't validate in
  105. "PrePersist" and "PreUpdate" as long as you don't create new entity
  106. instances. This was already discussed in the previous blog post on
  107. the Versionable extension, which requires another type of event
  108. called "onFlush".
  109. Further readings: :doc:`Lifecycle Events <../reference/events>`