diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index a628a6fc7..315de3cdd 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -244,6 +244,11 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $validator = new Doctrine_Validator(); $validator->validateRecord($this); $this->validate(); + if ($this->state == self::STATE_TDIRTY || $this->state == self::STATE_TCLEAN) { + $this->validateOnInsert(); + } else { + $this->validateOnUpdate(); + } return $this->errorStack->count() == 0 ? true : false; //$this->errorStack->merge($validator->getErrorStack()); @@ -254,6 +259,18 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * validations that are neccessary. */ protected function validate() {} + /** + * Empty tempalte method to provide concrete Record classes with the possibility + * to hook into the validation procedure only when the record is going to be + * updated. + */ + protected function validateOnUpdate() {} + /** + * Empty tempalte method to provide concrete Record classes with the possibility + * to hook into the validation procedure only when the record is going to be + * inserted into the data store the first time. + */ + protected function validateOnInsert() {} /** * getErrorStack * @@ -840,7 +857,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $conn = $this->table->getConnection(); } $conn->beginTransaction(); - + $saveLater = $conn->saveRelated($this); if ($this->isValid()) { diff --git a/lib/Doctrine/Validator/Regexp.php b/lib/Doctrine/Validator/Regexp.php index a40683b6a..36be5942e 100644 --- a/lib/Doctrine/Validator/Regexp.php +++ b/lib/Doctrine/Validator/Regexp.php @@ -1,25 +1,25 @@ -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->errorStack->add('name', 'forbiddenName'); + } + } } class Email extends Doctrine_Record { public function setTableDefinition() { // validators 'email' and 'unique' used - $this->hasColumn("address","string",150, array("email", "unique" => true)); - } - protected function validate() { - if ($this->address !== 'the-only-allowed-mail@address.com') { - // syntax: add(, ) - $this->errorStack->add('address', 'myCustomErrorCode'); - } + $this->hasColumn("address","string",150, array("email", "unique")); } } ?> diff --git a/manual/codes/Advanced components - Validators - Valid or Not Valid.php b/manual/codes/Advanced components - Validators - Valid or Not Valid.php index 1c4fc8e20..53b17737f 100644 --- a/manual/codes/Advanced components - Validators - Valid or Not Valid.php +++ b/manual/codes/Advanced components - Validators - Valid or Not Valid.php @@ -4,6 +4,8 @@ try { $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(); diff --git a/manual/docs/Advanced components - Validators - More Validation.php b/manual/docs/Advanced components - Validators - More Validation.php index 50c0bdb7b..d44722616 100644 --- a/manual/docs/Advanced components - Validators - More Validation.php +++ b/manual/docs/Advanced components - Validators - More Validation.php @@ -14,10 +14,15 @@ a predefined validator you have three options:

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. -One of these hooks is the validate() method. If you need a special validation in your active record -you can simply override validate() in your active record class (a descendant of Doctrine_Record). -Within this method you can use all the power of PHP to validate your fields. When a field +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:
diff --git a/tests/ValidatorTestCase.php b/tests/ValidatorTestCase.php index 09032d3ab..45efb97da 100644 --- a/tests/ValidatorTestCase.php +++ b/tests/ValidatorTestCase.php @@ -202,14 +202,17 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { } /** - * Tests whether custom validation through template methods works correctly + * Tests whether the validate() callback works correctly * in descendants of Doctrine_Record. */ - public function testCustomValidation() { + public function testValidationHooks() { $this->manager->setAttribute(Doctrine::ATTR_VLD, true); - $user = $this->connection->getTable("User")->find(4); + + // Tests validate() and validateOnInsert() + $user = new User(); try { $user->name = "I'm not The Saint"; + $user->password = "1234"; $user->save(); } catch(Doctrine_Validator_Exception $e) { $this->assertEqual($e->count(), 1); @@ -217,16 +220,49 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { $this->assertEqual(count($invalidRecords), 1); $stack = $invalidRecords[0]->getErrorStack(); - - $this->assertEqual($stack->count(), 1); - $this->assertTrue(in_array('notTheSaint', $stack['name'])); + + $this->assertEqual($stack->count(), 2); + $this->assertTrue(in_array('notTheSaint', $stack['name'])); // validate() hook constraint + $this->assertTrue(in_array('pwNotTopSecret', $stack['password'])); // validateOnInsert() hook constraint } + // Tests validateOnUpdate() + $user = $this->connection->getTable("User")->find(4); try { - $user->name = "The Saint"; + $user->name = "The Saint"; // Set correct name + $user->password = "Top Secret"; // Set correct password + $user->loginname = "Somebody"; // Wrong login name! $user->save(); - } catch(Doctrine_Validator_Exception $e) { $this->fail(); + } catch(Doctrine_Validator_Exception $e) { + $invalidRecords = $e->getInvalidRecords(); + $this->assertEqual(count($invalidRecords), 1); + + $stack = $invalidRecords[0]->getErrorStack(); + + $this->assertEqual($stack->count(), 1); + $this->assertTrue(in_array('notNobody', $stack['loginname'])); // validateOnUpdate() hook constraint + } + + $this->manager->setAttribute(Doctrine::ATTR_VLD, false); + } + + /** + * Tests whether the validateOnInsert() callback works correctly + * in descendants of Doctrine_Record. + */ + public function testHookValidateOnInsert() { + $this->manager->setAttribute(Doctrine::ATTR_VLD, true); + + $user = new User(); + $user->password = "1234"; + + try { + $user->save(); + $this->fail(); + } catch (Doctrine_Validator_Exception $ex) { + $errors = $user->getErrorStack(); + $this->assertTrue(in_array('pwNotTopSecret', $errors['password'])); } $this->manager->setAttribute(Doctrine::ATTR_VLD, false); diff --git a/tests/classes.php b/tests/classes.php index a92980dfa..9078e9dac 100644 --- a/tests/classes.php +++ b/tests/classes.php @@ -9,7 +9,7 @@ class Entity extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn("id","integer",20,"autoincrement|primary"); $this->hasColumn("name","string",50); - $this->hasColumn("loginname","string",20,"unique"); + $this->hasColumn("loginname","string",20, array("unique")); $this->hasColumn("password","string",16); $this->hasColumn("type","integer",1); $this->hasColumn("created","integer",11); @@ -104,6 +104,16 @@ class User extends Entity { $this->errorStack->add('name', 'notTheSaint'); } } + public function validateOnInsert() { + if ($this->password !== 'Top Secret') { + $this->errorStack->add('password', 'pwNotTopSecret'); + } + } + public function validateOnUpdate() { + if ($this->loginname !== 'Nobody') { + $this->errorStack->add('loginname', 'notNobody'); + } + } } class Groupuser extends Doctrine_Record { public function setTableDefinition() { @@ -421,7 +431,7 @@ class ValidatorTest extends Doctrine_Record { $this->hasColumn("myobject", "object", 1000); $this->hasColumn("myinteger", "integer", 11); $this->hasColumn("myrange", "integer", 11, array('range' => array(4,123))); - $this->hasColumn("myregexp", "string", 5, array('regexp' => '^[0-9]+$')); + $this->hasColumn("myregexp", "string", 5, array('regexp' => '/^[0-9]+$/')); $this->hasColumn("myemail", "string", 100, "email"); $this->hasColumn("myemail2", "string", 100, "email|notblank");