114 lines
4.2 KiB
Plaintext
114 lines
4.2 KiB
Plaintext
|
Doctrine 2 does not ship with any internal validators, the reason being that
|
||
|
we think all the frameworks out there already ship with quite decents ones that can be integrated
|
||
|
into your Domain easily. What we offer are hooks to execute any kind of validation.
|
||
|
|
||
|
> **Note**
|
||
|
> You don't need to validate your entities in the lifecycle events. Its only
|
||
|
> one of many options. Of course you can also perform validations in value setters
|
||
|
> or any other method of your entities that are used in your code.
|
||
|
|
||
|
Entities can register lifecycle evnt methods with Doctrine that are called on
|
||
|
different occasions. For validation we would need to hook into the
|
||
|
events called before persisting and updating. Even though we don't support
|
||
|
validation out of the box, the implementation is even simpler than in Doctrine 1
|
||
|
and you will get the additional benefit of being able to re-use your validation
|
||
|
in any other part of your domain.
|
||
|
|
||
|
Say we have an `Order` with several `OrderLine` instances. We never want to
|
||
|
allow any customer to order for a larger sum than he is allowed to:
|
||
|
|
||
|
[php]
|
||
|
class Order
|
||
|
{
|
||
|
public function assertCustomerAllowedBuying()
|
||
|
{
|
||
|
$orderLimit = $this->customer->getOrderLimit();
|
||
|
|
||
|
$amount = 0;
|
||
|
foreach ($this->orderLines AS $line) {
|
||
|
$amount += $line->getAmount();
|
||
|
}
|
||
|
|
||
|
if ($amount > $orderLimit) {
|
||
|
throw new CustomerOrderLimitExceededException();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Now this is some pretty important piece of business logic in your code, enforcing
|
||
|
it at any time is important so that customers with a unknown reputation don't
|
||
|
owe your business too much money.
|
||
|
|
||
|
We can enforce this constraint in any of the metadata drivers. First Annotations:
|
||
|
|
||
|
[php]
|
||
|
/**
|
||
|
* @Entity
|
||
|
* @HasLifecycleCallbacks
|
||
|
*/
|
||
|
class Order
|
||
|
{
|
||
|
/**
|
||
|
* @PrePersist @PreUpdate
|
||
|
*/
|
||
|
public function assertCustomerAllowedBuying() {}
|
||
|
}
|
||
|
|
||
|
In XML Mappings:
|
||
|
|
||
|
[xml]
|
||
|
<doctrine-mapping>
|
||
|
<entity name="Order">
|
||
|
<lifecycle-callbacks>
|
||
|
<lifecycle-callback type="prePersist" method="assertCustomerallowedBuying" />
|
||
|
<lifecycle-callback type="preUpdate" method="assertCustomerallowedBuying" />
|
||
|
</lifecycle-callbacks>
|
||
|
</entity>
|
||
|
</doctirne-mapping>
|
||
|
|
||
|
YAML needs some little change yet, to allow multiple lifecycle events for one method,
|
||
|
this will happen before Beta 1 though.
|
||
|
|
||
|
Now validation is performed whenever you call `EntityManager#persist($order)`
|
||
|
or when you call `EntityManager#flush()` and an order is about to be updated.
|
||
|
Any Exception that happens in the lifecycle callbacks will be catched by the
|
||
|
EntityManager and the current transaction is rolled back.
|
||
|
|
||
|
Of course you can do any type of primitive checks, not null, email-validation, string size,
|
||
|
integer and date ranges in your validation callbacks.
|
||
|
|
||
|
[php]
|
||
|
class Order
|
||
|
{
|
||
|
/**
|
||
|
* @PrePersist @PreUpdate
|
||
|
*/
|
||
|
public function validate()
|
||
|
{
|
||
|
if (!($this->plannedShipDate instanceof DateTime)) {
|
||
|
throw new ValidateException();
|
||
|
}
|
||
|
|
||
|
if ($this->plannedShipDate->format('U') < time()) {
|
||
|
throw new ValidateException();
|
||
|
}
|
||
|
|
||
|
if ($this->customer == null) {
|
||
|
throw new OrderRequiresCustomerException();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
What is nice about lifecycle events is, you can also re-use the methods at other places
|
||
|
in your domain, for example in combination with your form library.
|
||
|
Additionally there is no limitation in the number of methods you register
|
||
|
on one particular event, i.e. you can register multiple methods for validation in "PrePersist"
|
||
|
or "PreUpdate" or mix and share them in any combinations between those two events.
|
||
|
|
||
|
There is no limit to what you can and can't validate in "PrePersist" and "PreUpdate" aslong as
|
||
|
you don't create new entity instances. This was already discussed in the previous
|
||
|
blog post on the Versionable extension, which requires another type of event called "onFlush".
|
||
|
|
||
|
Further readings:
|
||
|
|
||
|
* [Doctrine 2 Manual: Events](http://www.doctrine-project.org/documentation/manual/2_0/en/events#lifecycle-events)
|