+++ Introduction Validation in Doctrine is a way to enforce your business rules in the model part of the MVC architecture. You can think of this validation as a gateway that needs to be passed right before data gets into the persistent data store. The definition of these business rules takes place at the record level, that means in your active record model classes (classes derived from {{Doctrine_Record}}). The first thing you need to do to be able to use this kind of validation is to enable it globally. This is done through the {{Doctrine_Manager}} (see the code below). Once you enabled validation, you'll get a bunch of validations automatically: * **Data type validations**: All values assigned to columns are checked for the right type. That means if you specified a column of your record as type 'integer', Doctrine will validate that any values assigned to that column are of this type. This kind of type validation tries to be as smart as possible since PHP is a loosely typed language. For example 2 as well as "7" are both valid integers whilst "3f" is not. Type validations occur on every column (since every column definition needs a type). * **Length validation**: As the name implies, all values assigned to columns are validated to make sure that the value does not exceed the maximum length. // turning on validation Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_VALIDATE, Doctrine::VALIDATE_ALL); You can combine the following constants by using bitwise operations: VALIDATE_ALL, VALIDATE_TYPES, VALIDATE_LENGTHS, VALIDATE_CONSTRAINTS, VALIDATE_NONE. For example to enable all validations except length validations you would use: VALIDATE_ALL & ~VALIDATE_LENGTHS +++ More Validation The type and length validations are handy but most of the time they're not enough. Therefore Doctrine provides some mechanisms that can be used to validate your data in more detail. Validators are an easy way to specify further validations. Doctrine has a lot of predefined validators that are frequently needed such as email, country, ip, range and regexp validators. You find a full list of available validators at the bottom of this page. You can specify which validators apply to which column through the 4th argument of the {{hasColumn()}} method. If that is still not enough and you need some specialized validation that is not yet available as a predefined validator you have three options: * You can write the validator on your own. * You can propose your need for a new validator to a Doctrine developer. * You can use validation hooks. The first two options are advisable if it is likely that the validation is of general use and is potentially applicable in many situations. In that case it is a good idea to implement a new validator. However if the validation is special it is better to use hooks provided by Doctrine: * {{validate()}} (Executed every time the record gets validated) * {{validateOnInsert()}} (Executed when the record is new and gets validated) * {{validateOnUpdate()}} (Executed when the record is not new and gets validated) If you need a special validation in your active record you can simply override one of these methods in your active record class (a descendant of {{Doctrine_Record}}). Within thess methods you can use all the power of PHP to validate your fields. When a field doesnt pass your validation you can then add errors to the record's error stack. The following code snippet shows an example of how to define validators together with custom validation: class User extends Doctrine_Record { public function setUp() { $this->ownsOne('Email', array('local' => 'email_id')); } public function setTableDefinition() { // no special validators used only types // and lengths will be validated $this->hasColumn('name', 'string', 15); $this->hasColumn('email_id', 'integer'); $this->hasColumn('created', 'integer', 11); } // Our own validation protected function validate() { if ($this->name == 'God') { // Blasphemy! Stop that! ;-) // syntax: add(, ) $this->getErrorStack()->add('name', 'forbiddenName'); } } } class Email extends Doctrine_Record { public function setTableDefinition() { // validators 'email' and 'unique' used $this->hasColumn("address","string",150, array("email", "unique")); } } +++ Valid or Not Valid Now that you know how to specify your business rules in your models, it is time to look at how to deal with these rules in the rest of your application. ++++ Implicit validation Whenever a record is going to be saved to the persistent data store (i.e. through calling {{$record->save()}}) the full validation procedure is executed. If errors occur during that process an exception of the type {{Doctrine_Validator_Exception}} will be thrown. You can catch that exception and analyze the errors by using the instance method {{Doctine_Validator_Exception::getInvalidRecords()}}. This method returns an ordinary array with references to all records that did not pass validation. You can then further explore the errors of each record by analyzing the error stack of each record. The error stack of a record can be obtained with the instance method {{Doctrine_Record::getErrorStack()}}. Each error stack is an instance of the class {{Doctrine_Validator_ErrorStack}}. The error stack provides an easy to use interface to inspect the errors. ++++ Explicit validation You can explicitly trigger the validation for any record at any time. For this purpose Doctrine_Record provides the instance method {{Doctrine_Record::isValid()}}. This method returns a boolean value indicating the result of the validation. If the method returns false, you can inspect the error stack in the same way as seen above except that no exception is thrown, so you simply obtain the error stack of the record that didnt pass validation through {{Doctrine_Record::getErrorStack()}}. The following code snippet shows an example of handling implicit validation which caused a {{Doctrine_Validator_Exception}}. try { $user->name = "this is an example of too long name"; $user->Email->address = "drink@@notvalid.."; $user->save(); } catch(Doctrine_Validator_Exception $e) { // Note: you could also use $e->getInvalidRecords(). The direct way // used here is just more simple when you know the records you're dealing with. $userErrors = $user->getErrorStack(); $emailErrors = $user->Email->getErrorStack(); /* Inspect user errors */ foreach($userErrors as $fieldName => $errorCodes) { switch ($fieldName) { case 'name': // $user->name is invalid. inspect the error codes if needed. break; } } /* Inspect email errors */ foreach($emailErrors as $fieldName => $errorCodes) { switch ($fieldName) { case 'address': // $user->Email->address is invalid. inspect the error codes if needed. break; } } }