Continued work on the validation component.
Ticket: 150
This commit is contained in:
parent
d81a4245b7
commit
171226d532
@ -237,10 +237,12 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
|||||||
if( ! $this->table->getAttribute(Doctrine::ATTR_VLD))
|
if( ! $this->table->getAttribute(Doctrine::ATTR_VLD))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Clear the stack from any previous errors.
|
||||||
|
$this->errorStack->clear();
|
||||||
|
|
||||||
|
// Run validation process
|
||||||
$validator = new Doctrine_Validator();
|
$validator = new Doctrine_Validator();
|
||||||
// Run validators
|
|
||||||
$validator->validateRecord($this);
|
$validator->validateRecord($this);
|
||||||
// Run custom validation
|
|
||||||
$this->validate();
|
$this->validate();
|
||||||
|
|
||||||
return $this->errorStack->count() == 0 ? true : false;
|
return $this->errorStack->count() == 0 ? true : false;
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
* @license LGPL
|
* @license LGPL
|
||||||
* @package Doctrine
|
* @package Doctrine
|
||||||
*/
|
*/
|
||||||
class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorAggregate {
|
class Doctrine_Validator_ErrorStack extends Doctrine_Access implements Countable, IteratorAggregate {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The errors of the error stack.
|
* The errors of the error stack.
|
||||||
@ -49,8 +49,8 @@ class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorA
|
|||||||
* @param string $invalidFieldName
|
* @param string $invalidFieldName
|
||||||
* @param string $errorType
|
* @param string $errorType
|
||||||
*/
|
*/
|
||||||
public function add($invalidFieldName, $errorType = 'general') {
|
public function add($invalidFieldName, $errorCode = 'general') {
|
||||||
$this->errors[$invalidFieldName][] = array('type' => $errorType);
|
$this->errors[$invalidFieldName][] = $errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,66 +70,35 @@ class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorA
|
|||||||
* @param unknown_type $name
|
* @param unknown_type $name
|
||||||
* @return unknown
|
* @return unknown
|
||||||
*/
|
*/
|
||||||
public function get($name) {
|
public function get($fieldName) {
|
||||||
return $this[$name];
|
return isset($this->errors[$fieldName]) ? $this->errors[$fieldName] : null;
|
||||||
}
|
|
||||||
|
|
||||||
/** ArrayAccess implementation */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all errors that occured for the specified field.
|
|
||||||
*
|
|
||||||
* @param string $offset
|
|
||||||
* @return The array containing the errors or NULL if no errors were found.
|
|
||||||
*/
|
|
||||||
public function offsetGet($offset) {
|
|
||||||
return isset($this->errors[$offset]) ? $this->errors[$offset] : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter description here...
|
* Enter description here...
|
||||||
*
|
*
|
||||||
* @param string $offset
|
* @param unknown_type $name
|
||||||
* @param mixed $value
|
|
||||||
* @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed.
|
|
||||||
*/
|
*/
|
||||||
public function offsetSet($offset, $value) {
|
public function set($fieldName, $errorCode) {
|
||||||
throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be added through
|
$this->add($fieldName, $errorCode);
|
||||||
Doctrine_Validator_ErrorStack::add()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter description here...
|
* Enter description here...
|
||||||
*
|
*
|
||||||
* @param unknown_type $offset
|
* @return unknown
|
||||||
*/
|
*/
|
||||||
public function offsetExists($offset) {
|
public function contains($fieldName) {
|
||||||
return isset($this->errors[$offset]);
|
return array_key_exists($fieldName, $this->errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enter description here...
|
* Removes all errors from the stack.
|
||||||
*
|
|
||||||
* @param unknown_type $offset
|
|
||||||
* @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed.
|
|
||||||
*/
|
*/
|
||||||
public function offsetUnset($offset) {
|
public function clear() {
|
||||||
throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be removed
|
$this->errors = array();
|
||||||
through Doctrine_Validator_ErrorStack::remove()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enter description here...
|
|
||||||
*
|
|
||||||
* @param unknown_type $stack
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
public function merge($stack) {
|
|
||||||
if(is_array($stack)) {
|
|
||||||
$this->errors = array_merge($this->errors, $stack);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
/** IteratorAggregate implementation */
|
/** IteratorAggregate implementation */
|
||||||
|
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
<?php
|
|
||||||
class Doctrine_Validator_Required {
|
|
||||||
/**
|
|
||||||
* @param Doctrine_Record $record
|
|
||||||
* @param string $key
|
|
||||||
* @param mixed $value
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function validate(Doctrine_Record $record, $key, $value) {
|
|
||||||
return ($value === null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
try {
|
|
||||||
$user->name = "this is an example of too long name";
|
|
||||||
$user->Email->address = "drink@@notvalid..";
|
|
||||||
$user->save();
|
|
||||||
} catch(Doctrine_Validator_Exception $e) {
|
|
||||||
$stack = $e->getErrorStack();
|
|
||||||
foreach($stack as $component => $err) {
|
|
||||||
foreach($err as $field => $type) {
|
|
||||||
switch($type):
|
|
||||||
case Doctrine_Validator::ERR_TYPE:
|
|
||||||
print $field." is not right type";
|
|
||||||
break;
|
|
||||||
case Doctrine_Validator::ERR_UNIQUE:
|
|
||||||
print $field." is not unique";
|
|
||||||
break;
|
|
||||||
case Doctrine_Validator::ERR_VALID:
|
|
||||||
print $field." is not valid";
|
|
||||||
break;
|
|
||||||
case Doctrine_Validator::ERR_LENGTH:
|
|
||||||
print $field." is too long";
|
|
||||||
break;
|
|
||||||
endswitch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
class User extends Doctrine_Record {
|
||||||
|
public function setUp() {
|
||||||
|
$this->ownsOne("Email","User.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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(<fieldName>, <error identifier>)
|
||||||
|
$this->errorStack->add('address', 'myCustomValidationTypeError');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
try {
|
||||||
|
$user->name = "this is an example of too long name";
|
||||||
|
$user->Email->address = "drink@@notvalid..";
|
||||||
|
$user->save();
|
||||||
|
} catch(Doctrine_Validator_Exception $e) {
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
class User extends Doctrine_Record {
|
|
||||||
public function setUp() {
|
|
||||||
$this->ownsOne("Email","User.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class Email extends Doctrine_Record {
|
|
||||||
public function setTableDefinition() {
|
|
||||||
// specialized validators 'email' and 'unique' used
|
|
||||||
$this->hasColumn("address","string",150,"email|unique");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$conn = Doctrine_Manager::getInstance()->openConnection(new PDO("dsn","username","password"));
|
|
||||||
$user = new User();
|
|
||||||
$user->name = "this is an example of too long name";
|
|
||||||
|
|
||||||
$user->save(); // throws a Doctrine_Validator_Exception
|
|
||||||
|
|
||||||
$user->name = "valid name";
|
|
||||||
$user->created = "not valid"; // not valid type
|
|
||||||
$user->save(); // throws a Doctrine_Validator_Exception
|
|
||||||
|
|
||||||
|
|
||||||
$user->created = time();
|
|
||||||
$user->Email->address = "drink@.."; // not valid email address
|
|
||||||
$user->save(); // throws a Doctrine_Validator_Exception
|
|
||||||
|
|
||||||
$user->Email->address = "drink@drinkmore.info";
|
|
||||||
$user->save(); // saved
|
|
||||||
|
|
||||||
|
|
||||||
$user = $conn->create("User");
|
|
||||||
$user->Email->address = "drink@drinkmore.info"; // not unique!
|
|
||||||
$user->save(); // throws a Doctrine_Validator_Exception
|
|
||||||
?>
|
|
@ -0,0 +1,17 @@
|
|||||||
|
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).<br />
|
||||||
|
<br />
|
||||||
|
Once you enabled validation, you'll get a bunch of validations automatically:<br />
|
||||||
|
<br />
|
||||||
|
- 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).<br /><br />
|
||||||
|
- 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.
|
@ -1,11 +0,0 @@
|
|||||||
With Doctrine validators you can validate a whole transaction and get info of everything
|
|
||||||
that went wrong. Some Doctrine validators also act as a database level constraints. For example
|
|
||||||
adding a unique validator to column 'name' also adds a database level unique constraint into that
|
|
||||||
column.
|
|
||||||
<br \><br \>
|
|
||||||
Validators are added as a 4 argument for hasColumn() method. Validators should be separated
|
|
||||||
by '|' mark. For example email|unique would validate a value using Doctrine_Validator_Email
|
|
||||||
and Doctrine_Validator_Unique.
|
|
||||||
<br \><br \>
|
|
||||||
Doctrine has many predefined validators (see chapter 13.3). If you wish to use
|
|
||||||
custom validators just write *Validator classes and doctrine will automatically use them.
|
|
@ -0,0 +1,23 @@
|
|||||||
|
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.<br />
|
||||||
|
<br />
|
||||||
|
Validators: 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:<br />
|
||||||
|
<br />
|
||||||
|
- You can write the validator on your own.<br />
|
||||||
|
- You can propose your need for a new validator to a Doctrine developer.<br />
|
||||||
|
- You can use validation hooks.<br />
|
||||||
|
<br />
|
||||||
|
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
|
||||||
|
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:<br />
|
@ -0,0 +1,22 @@
|
|||||||
|
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.<br />
|
||||||
|
<br />
|
||||||
|
Implicit validation:<br />
|
||||||
|
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.<br />
|
||||||
|
<br />
|
||||||
|
Explicit validation:<br />
|
||||||
|
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().<br />
|
||||||
|
<br />
|
||||||
|
The following code snippet shows an example of handling implicit validation which caused a Doctrine_Validator_Exception.
|
@ -1,5 +0,0 @@
|
|||||||
When the validation attribute is set as true all transactions will be validated, so whenever Doctrine_Record::save(),
|
|
||||||
Doctrine_Connection::flush() or any other saving method is used all the properties of all records in that transaction will have their values
|
|
||||||
validated.
|
|
||||||
<br \><br \>
|
|
||||||
Validation errors are being stacked into Doctrine_Validator_Exception.
|
|
@ -270,9 +270,9 @@ $menu = array("Getting started" =>
|
|||||||
"Creating a logger",
|
"Creating a logger",
|
||||||
),
|
),
|
||||||
"Validators" => array(
|
"Validators" => array(
|
||||||
"Intruduction",
|
"Introduction",
|
||||||
"Validating transactions",
|
"More Validation",
|
||||||
"Analyzing the ErrorStack",
|
"Valid or Not Valid",
|
||||||
"List of predefined validators"
|
"List of predefined validators"
|
||||||
),
|
),
|
||||||
"View" => array(
|
"View" => array(
|
||||||
|
@ -94,10 +94,10 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
|
|||||||
|
|
||||||
$this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
|
$this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
|
||||||
|
|
||||||
$this->assertTrue(in_array(array('type' => 'notnull'), $stack['mystring']));
|
$this->assertTrue(in_array('notnull', $stack['mystring']));
|
||||||
$this->assertTrue(in_array(array('type' => 'notblank'), $stack['myemail2']));
|
$this->assertTrue(in_array('notblank', $stack['myemail2']));
|
||||||
$this->assertTrue(in_array(array('type' => 'range'), $stack['myrange']));
|
$this->assertTrue(in_array('range', $stack['myrange']));
|
||||||
$this->assertTrue(in_array(array('type' => 'regexp'), $stack['myregexp']));
|
$this->assertTrue(in_array('regexp', $stack['myregexp']));
|
||||||
$test->mystring = 'str';
|
$test->mystring = 'str';
|
||||||
|
|
||||||
|
|
||||||
@ -127,19 +127,19 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
|
|||||||
$stack = $user->getErrorStack();
|
$stack = $user->getErrorStack();
|
||||||
|
|
||||||
$this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
|
$this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
|
||||||
$this->assertTrue(in_array(array('type' => 'length'), $stack['loginname']));
|
$this->assertTrue(in_array('length', $stack['loginname']));
|
||||||
$this->assertTrue(in_array(array('type' => 'length'), $stack['password']));
|
$this->assertTrue(in_array('length', $stack['password']));
|
||||||
$this->assertTrue(in_array(array('type' => 'type'), $stack['created']));
|
$this->assertTrue(in_array('type', $stack['created']));
|
||||||
|
|
||||||
$validator->validateRecord($email);
|
$validator->validateRecord($email);
|
||||||
$stack = $email->getErrorStack();
|
$stack = $email->getErrorStack();
|
||||||
$this->assertTrue(in_array(array('type' => 'email'), $stack['address']));
|
$this->assertTrue(in_array('email', $stack['address']));
|
||||||
$email->address = "arnold@example.com";
|
$email->address = "arnold@example.com";
|
||||||
|
|
||||||
$validator->validateRecord($email);
|
$validator->validateRecord($email);
|
||||||
$stack = $email->getErrorStack();
|
$stack = $email->getErrorStack();
|
||||||
|
|
||||||
$this->assertTrue(in_array(array('type' => 'unique'), $stack['address']));
|
$this->assertTrue(in_array('unique', $stack['address']));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,7 +177,7 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
|
|||||||
$invalidRecords = $e->getInvalidRecords();
|
$invalidRecords = $e->getInvalidRecords();
|
||||||
$this->assertEqual(count($invalidRecords), 1);
|
$this->assertEqual(count($invalidRecords), 1);
|
||||||
$stack = $invalidRecords[0]->getErrorStack();
|
$stack = $invalidRecords[0]->getErrorStack();
|
||||||
$this->assertTrue(in_array(array('type' => 'length'), $stack['name']));
|
$this->assertTrue(in_array('length', $stack['name']));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -196,8 +196,8 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
|
|||||||
$emailStack = $a[array_search($user->Email, $a)]->getErrorStack();
|
$emailStack = $a[array_search($user->Email, $a)]->getErrorStack();
|
||||||
$userStack = $a[array_search($user, $a)]->getErrorStack();
|
$userStack = $a[array_search($user, $a)]->getErrorStack();
|
||||||
|
|
||||||
$this->assertTrue(in_array(array('type' => 'email'), $emailStack['address']));
|
$this->assertTrue(in_array('email', $emailStack['address']));
|
||||||
$this->assertTrue(in_array(array('type' => 'length'), $userStack['name']));
|
$this->assertTrue(in_array('length', $userStack['name']));
|
||||||
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
|
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,8 +219,16 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
|
|||||||
$stack = $invalidRecords[0]->getErrorStack();
|
$stack = $invalidRecords[0]->getErrorStack();
|
||||||
|
|
||||||
$this->assertEqual($stack->count(), 1);
|
$this->assertEqual($stack->count(), 1);
|
||||||
$this->assertTrue(in_array(array('type' => 'notTheSaint'), $stack['name']));
|
$this->assertTrue(in_array('notTheSaint', $stack['name']));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user->name = "The Saint";
|
||||||
|
$user->save();
|
||||||
|
} catch(Doctrine_Validator_Exception $e) {
|
||||||
|
$this->fail();
|
||||||
|
}
|
||||||
|
|
||||||
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
|
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user