Commit b0f05370 authored by romanb's avatar romanb

Necessary changes to the validation components. Further improvements and docs...

Necessary changes to the validation components. Further improvements and docs will follow in the next days.
Ticket: 150
parent 499da8f9
...@@ -101,7 +101,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite ...@@ -101,7 +101,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
/** /**
* @var Doctrine_Validator_ErrorStack error stack object * @var Doctrine_Validator_ErrorStack error stack object
*/ */
private $errorStack; protected $errorStack;
/** /**
* @var integer $index this index is used for creating object identifiers * @var integer $index this index is used for creating object identifiers
*/ */
...@@ -238,14 +238,20 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite ...@@ -238,14 +238,20 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
return true; return true;
$validator = new Doctrine_Validator(); $validator = new Doctrine_Validator();
// Run validators
$validator->validateRecord($this);
// Run custom validation
$this->validate();
if($validator->validateRecord($this)) return $this->errorStack->count() == 0 ? true : false;
return true; //$this->errorStack->merge($validator->getErrorStack());
$this->errorStack->merge($validator->getErrorStack());
return false;
} }
/**
* Emtpy template method to provide concrete Record classes with the possibility
* to hook into the validation procedure, doing any custom / specialized
* validations that are neccessary.
*/
protected function validate() {}
/** /**
* getErrorStack * getErrorStack
* *
...@@ -644,6 +650,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite ...@@ -644,6 +650,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
* @return mixed * @return mixed
*/ */
public function get($name, $invoke = true) { public function get($name, $invoke = true) {
$listener = $this->table->getAttribute(Doctrine::ATTR_LISTENER); $listener = $this->table->getAttribute(Doctrine::ATTR_LISTENER);
$value = self::$null; $value = self::$null;
$lower = strtolower($name); $lower = strtolower($name);
...@@ -834,12 +841,10 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite ...@@ -834,12 +841,10 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$saveLater = $conn->saveRelated($this); $saveLater = $conn->saveRelated($this);
$this->isValid(); if ($this->isValid()) {
if($this->errorStack->count() > 0) {
$conn->getTransaction()->addInvalid($this);
} else {
$conn->save($this); $conn->save($this);
} else {
$conn->getTransaction()->addInvalid($this);
} }
foreach($saveLater as $fk) { foreach($saveLater as $fk) {
......
...@@ -27,10 +27,6 @@ ...@@ -27,10 +27,6 @@
* @license LGPL * @license LGPL
*/ */
class Doctrine_Validator { class Doctrine_Validator {
/**
* @var array $stack error stack
*/
private $stack = array();
/** /**
* @var array $validators an array of validator objects * @var array $validators an array of validator objects
*/ */
...@@ -78,6 +74,8 @@ class Doctrine_Validator { ...@@ -78,6 +74,8 @@ class Doctrine_Validator {
$columns = $record->getTable()->getColumns(); $columns = $record->getTable()->getColumns();
$component = $record->getTable()->getComponentName(); $component = $record->getTable()->getComponentName();
$errorStack = $record->getErrorStack();
switch($record->getState()): switch($record->getState()):
case Doctrine_Record::STATE_TDIRTY: case Doctrine_Record::STATE_TDIRTY:
case Doctrine_Record::STATE_TCLEAN: case Doctrine_Record::STATE_TCLEAN:
...@@ -102,7 +100,7 @@ class Doctrine_Validator { ...@@ -102,7 +100,7 @@ class Doctrine_Validator {
$value = $record->getTable()->enumIndex($key, $value); $value = $record->getTable()->enumIndex($key, $value);
if($value === false) { if($value === false) {
$err[$key] = 'enum'; $errorStack->add($key, 'enum');
continue; continue;
} }
} }
...@@ -113,7 +111,7 @@ class Doctrine_Validator { ...@@ -113,7 +111,7 @@ class Doctrine_Validator {
$length = strlen($value); $length = strlen($value);
if($length > $column[1]) { if($length > $column[1]) {
$err[$key] = 'length'; $errorStack->add($key, 'length');
continue; continue;
} }
...@@ -144,7 +142,7 @@ class Doctrine_Validator { ...@@ -144,7 +142,7 @@ class Doctrine_Validator {
if( ! $validator->validate($record, $key, $value, $args)) { if( ! $validator->validate($record, $key, $value, $args)) {
$err[$key] = $name; $errorStack->add($key, $name);
//$err[$key] = 'not valid'; //$err[$key] = 'not valid';
...@@ -153,17 +151,10 @@ class Doctrine_Validator { ...@@ -153,17 +151,10 @@ class Doctrine_Validator {
} }
} }
if( ! self::isValidType($value, $column[0])) { if( ! self::isValidType($value, $column[0])) {
$err[$key] = 'type'; $errorStack->add($key, 'type');
continue; continue;
} }
} }
if( ! empty($err)) {
$this->stack = $err;
return false;
}
return true;
} }
/** /**
* whether or not this validator has errors * whether or not this validator has errors
...@@ -173,14 +164,6 @@ class Doctrine_Validator { ...@@ -173,14 +164,6 @@ class Doctrine_Validator {
public function hasErrors() { public function hasErrors() {
return (count($this->stack) > 0); return (count($this->stack) > 0);
} }
/**
* returns the error stack
*
* @return array
*/
public function getErrorStack() {
return $this->stack;
}
/** /**
* converts a doctrine type to native php type * converts a doctrine type to native php type
* *
......
...@@ -18,38 +18,138 @@ ...@@ -18,38 +18,138 @@
* and is licensed under the LGPL. For more information, see * and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>. * <http://www.phpdoctrine.com>.
*/ */
Doctrine::autoload('Doctrine_Access');
/** /**
* Doctrine_Validator_ErrorStack * Doctrine_Validator_ErrorStack
* *
* @author Konsta Vesterinen * @author Konsta Vesterinen
* @author Roman Borschel
* @license LGPL * @license LGPL
* @package Doctrine * @package Doctrine
*/ */
class Doctrine_Validator_ErrorStack extends Doctrine_Access implements Countable, IteratorAggregate { class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorAggregate {
private $errors = array(); /**
* The errors of the error stack.
*
* @var array
*/
protected $errors = array();
public function merge($stack) { /**
if(is_array($stack)) { * Constructor
$this->errors = array_merge($this->errors, $stack); *
*/
public function __construct()
{}
/**
* Adds an error to the stack.
*
* @param string $invalidFieldName
* @param string $errorType
*/
public function add($invalidFieldName, $errorType = 'general') {
$this->errors[$invalidFieldName][] = array('type' => $errorType);
}
/**
* Removes all existing errors for the specified field from the stack.
*
* @param string $fieldName
*/
public function remove($fieldName) {
if (isset($this->errors[$fieldName])) {
unset($this->errors[$fieldName]);
} }
} }
/**
* Enter description here...
*
* @param unknown_type $name
* @return unknown
*/
public function get($name) { public function get($name) {
if(isset($this->errors[$name])) return $this[$name];
return $this->errors[$name]; }
return 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;
} }
public function set($name, $value) { /**
$this->errors[$name] = $value; * Enter description here...
*
* @param string $offset
* @param mixed $value
* @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed.
*/
public function offsetSet($offset, $value) {
throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be added through
Doctrine_Validator_ErrorStack::add()");
} }
/**
* Enter description here...
*
* @param unknown_type $offset
*/
public function offsetExists($offset) {
return isset($this->errors[$offset]);
}
/**
* Enter description here...
*
* @param unknown_type $offset
* @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed.
*/
public function offsetUnset($offset) {
throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be removed
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 */
/**
* Enter description here...
*
* @return unknown
*/
public function getIterator() { public function getIterator() {
return new ArrayIterator($this->errors); return new ArrayIterator($this->errors);
} }
/** Countable implementation */
/**
* Enter description here...
*
* @return unknown
*/
public function count() { public function count() {
return count($this->errors); return count($this->errors);
} }
......
<?php <?php
/**
* TestCase for Doctrine's validation component.
*
* @todo More tests to cover the full interface of Doctrine_Validator_ErrorStack.
*/
class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
public function prepareTables() { public function prepareTables() {
$this->tables[] = "ValidatorTest"; $this->tables[] = "ValidatorTest";
parent::prepareTables(); parent::prepareTables();
} }
/**
* Tests correct type detection.
*/
public function testIsValidType() { public function testIsValidType() {
$var = "123"; $var = "123";
$this->assertTrue(Doctrine_Validator::isValidType($var,"string")); $this->assertTrue(Doctrine_Validator::isValidType($var,"string"));
...@@ -70,6 +78,9 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -70,6 +78,9 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$this->assertTrue(Doctrine_Validator::isValidType($var,"object")); $this->assertTrue(Doctrine_Validator::isValidType($var,"object"));
} }
/**
* Tests Doctrine_Validator::validateRecord()
*/
public function testValidate2() { public function testValidate2() {
$test = new ValidatorTest(); $test = new ValidatorTest();
$test->mymixed = "message"; $test->mymixed = "message";
...@@ -79,22 +90,23 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -79,22 +90,23 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$validator = new Doctrine_Validator(); $validator = new Doctrine_Validator();
$validator->validateRecord($test); $validator->validateRecord($test);
$stack = $validator->getErrorStack(); $stack = $test->getErrorStack();
$this->assertTrue(is_array($stack)); $this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
$this->assertEqual($stack['mystring'], 'notnull'); $this->assertTrue(in_array(array('type' => 'notnull'), $stack['mystring']));
$this->assertEqual($stack['myemail2'], 'notblank'); $this->assertTrue(in_array(array('type' => 'notblank'), $stack['myemail2']));
$this->assertEqual($stack['myrange'], 'range'); $this->assertTrue(in_array(array('type' => 'range'), $stack['myrange']));
$this->assertEqual($stack['myregexp'], 'regexp'); $this->assertTrue(in_array(array('type' => 'regexp'), $stack['myregexp']));
$test->mystring = 'str'; $test->mystring = 'str';
$test->save(); $test->save();
} }
public function testEmailValidation() {
}
/**
* Tests Doctrine_Validator::validateRecord()
*/
public function testValidate() { public function testValidate() {
$user = $this->connection->getTable("User")->find(4); $user = $this->connection->getTable("User")->find(4);
...@@ -112,29 +124,27 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -112,29 +124,27 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$validator->validateRecord($user); $validator->validateRecord($user);
$stack = $validator->getErrorStack(); $stack = $user->getErrorStack();
$this->assertTrue(is_array($stack));
$this->assertEqual($stack["loginname"], 'length');
$this->assertEqual($stack["password"], 'length');
$this->assertEqual($stack["created"], 'type');
$this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack);
$this->assertTrue(in_array(array('type' => 'length'), $stack['loginname']));
$this->assertTrue(in_array(array('type' => 'length'), $stack['password']));
$this->assertTrue(in_array(array('type' => 'type'), $stack['created']));
$validator->validateRecord($email); $validator->validateRecord($email);
$stack = $validator->getErrorStack(); $stack = $email->getErrorStack();
$this->assertEqual($stack["address"], 'email'); $this->assertTrue(in_array(array('type' => 'email'), $stack['address']));
$email->address = "arnold@example.com"; $email->address = "arnold@example.com";
$validator->validateRecord($email); $validator->validateRecord($email);
$stack = $validator->getErrorStack(); $stack = $email->getErrorStack();
$this->assertEqual($stack["address"], 'unique');
$email->isValid();
$this->assertTrue($email->getErrorStack() instanceof Doctrine_Validator_ErrorStack); $this->assertTrue(in_array(array('type' => 'unique'), $stack['address']));
} }
/**
* Tests the Email validator. (Doctrine_Validator_Email)
*/
public function testIsValidEmail() { public function testIsValidEmail() {
$validator = new Doctrine_Validator_Email(); $validator = new Doctrine_Validator_Email();
...@@ -153,6 +163,9 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -153,6 +163,9 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
} }
/**
* Tests saving records with invalid attributes.
*/
public function testSave() { public function testSave() {
$this->manager->setAttribute(Doctrine::ATTR_VLD, true); $this->manager->setAttribute(Doctrine::ATTR_VLD, true);
$user = $this->connection->getTable("User")->find(4); $user = $this->connection->getTable("User")->find(4);
...@@ -161,6 +174,10 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -161,6 +174,10 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$user->save(); $user->save();
} catch(Doctrine_Validator_Exception $e) { } catch(Doctrine_Validator_Exception $e) {
$this->assertEqual($e->count(), 1); $this->assertEqual($e->count(), 1);
$invalidRecords = $e->getInvalidRecords();
$this->assertEqual(count($invalidRecords), 1);
$stack = $invalidRecords[0]->getErrorStack();
$this->assertTrue(in_array(array('type' => 'length'), $stack['name']));
} }
try { try {
...@@ -179,10 +196,32 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { ...@@ -179,10 +196,32 @@ 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->assertEqual($emailStack["address"], 'email'); $this->assertTrue(in_array(array('type' => 'email'), $emailStack['address']));
$this->assertEqual($userStack["name"], 'length'); $this->assertTrue(in_array(array('type' => 'length'), $userStack['name']));
$this->manager->setAttribute(Doctrine::ATTR_VLD, false); $this->manager->setAttribute(Doctrine::ATTR_VLD, false);
} }
/**
* Tests whether custom validation through template methods works correctly
* in descendants of Doctrine_Record.
*/
public function testCustomValidation() {
$this->manager->setAttribute(Doctrine::ATTR_VLD, true);
$user = $this->connection->getTable("User")->find(4);
try {
$user->name = "I'm not The Saint";
$user->save();
} catch(Doctrine_Validator_Exception $e) {
$this->assertEqual($e->count(), 1);
$invalidRecords = $e->getInvalidRecords();
$this->assertEqual(count($invalidRecords), 1);
$stack = $invalidRecords[0]->getErrorStack();
$this->assertEqual($stack->count(), 1);
$this->assertTrue(in_array(array('type' => 'notTheSaint'), $stack['name']));
}
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
}
} }
?> ?>
...@@ -97,6 +97,13 @@ class User extends Entity { ...@@ -97,6 +97,13 @@ class User extends Entity {
$this->hasMany("Group","Groupuser.group_id"); $this->hasMany("Group","Groupuser.group_id");
$this->setInheritanceMap(array("type"=>0)); $this->setInheritanceMap(array("type"=>0));
} }
/** Custom validation */
public function validate() {
// Allow only one name!
if ($this->name !== 'The Saint') {
$this->errorStack->add('name', 'notTheSaint');
}
}
} }
class Groupuser extends Doctrine_Record { class Groupuser extends Doctrine_Record {
public function setTableDefinition() { public function setTableDefinition() {
...@@ -541,4 +548,5 @@ class BoardWithPosition extends Doctrine_Record { ...@@ -541,4 +548,5 @@ class BoardWithPosition extends Doctrine_Record {
$this->hasOne("CategoryWithPosition as Category", "BoardWithPosition.category_id"); $this->hasOne("CategoryWithPosition as Category", "BoardWithPosition.category_id");
} }
} }
?> ?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment