Commit 67da38b0 authored by zYne's avatar zYne

Validators updated, *NOTE* this breaks BC:

- validators codes are now string instead of integers (constants)
- validating is now handled in record level
- added new component Doctrine_Validator_ErrorStack
- new methods Doctrine_Record::isValid() and Doctrine_Record::getErrorStack()
- errorStack now as a record instance variable
parent 847bd1ad
......@@ -535,18 +535,18 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
$record->loadReference($alias);
}
$r = Doctrine_Relation::getDeleteOperations($this->originals[$alias],$new);
$operations = Doctrine_Relation::getDeleteOperations($this->originals[$alias],$new);
foreach($r as $record) {
foreach($operations as $r) {
$query = "DELETE FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." = ?"
." AND ".$fk->getLocal()." = ?";
$this->table->getConnection()->execute($query, array($record->getIncremented(),$this->getIncremented()));
$this->table->getConnection()->execute($query, array($r->getIncremented(),$record->getIncremented()));
}
$r = Doctrine_Relation::getInsertOperations($this->originals[$alias],$new);
foreach($r as $record) {
$operations = Doctrine_Relation::getInsertOperations($this->originals[$alias],$new);
foreach($operations as $r) {
$reldao = $asf->create();
$reldao->set($fk->getForeign(),$record);
$reldao->set($fk->getForeign(),$r);
$reldao->set($fk->getLocal(),$this);
$reldao->save();
......@@ -569,15 +569,15 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun
$new = $this->references[$alias];
if( ! isset($this->originals[$alias]))
$this->loadReference($alias);
$record->loadReference($alias);
$r = Doctrine_Relation::getDeleteOperations($this->originals[$alias], $new);
$operations = Doctrine_Relation::getDeleteOperations($this->originals[$alias], $new);
foreach($r as $record) {
$record->delete();
foreach($operations as $r) {
$r->delete();
}
$this->originals[$alias] = clone $this->references[$alias];
$record->assignOriginals($alias, clone $this->references[$alias]);
}
break;
endswitch;
......
......@@ -60,6 +60,10 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
* @var Doctrine_Validator $validator transaction validator
*/
private $validator;
/**
* @var array $invalid an array containing all invalid records within this transaction
*/
protected $invalid = array();
/**
* @var array $update two dimensional pending update list, the records in
* this list will be updated when transaction is committed
......@@ -159,11 +163,13 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
throw new Doctrine_Exception($e->__toString());
}
if($this->conn->getAttribute(Doctrine::ATTR_VLD)) {
if($this->validator->hasErrors()) {
$this->rollback();
throw new Doctrine_Validator_Exception($this->validator);
}
if(count($this->invalid) > 0) {
$this->rollback();
$tmp = $this->invalid;
$this->invalid = array();
throw new Doctrine_Validator_Exception($tmp);
}
$this->conn->getDBH()->commit();
......@@ -172,6 +178,7 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
$this->state = Doctrine_Connection_Transaction::STATE_OPEN;
$this->validator = null;
$this->invalid = array();
} elseif($this->transaction_level == 1)
$this->state = Doctrine_Connection_Transaction::STATE_ACTIVE;
......@@ -361,6 +368,21 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
public function addDelete(Doctrine_Record $record) {
$name = $record->getTable()->getComponentName();
$this->delete[$name][] = $record;
}
/**
* addInvalid
* adds record into invalid records list
*
* @param Doctrine_Record $record
* @return boolean false if record already existed in invalid records list,
* otherwise true
*/
public function addInvalid(Doctrine_Record $record) {
if(in_array($record, $this->invalid))
return false;
$this->invalid[] = $record;
return true;
}
/**
* returns the pending insert list
......@@ -387,6 +409,12 @@ class Doctrine_Connection_Transaction implements Countable, IteratorAggregate {
return $this->delete;
}
public function getIterator() { }
public function count() { }
/**
* an alias for getTransactionLevel
*
* @return integer returns the nesting level of this transaction
*/
public function count() {
return $this->transaction_level;
}
}
......@@ -65,26 +65,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
* a Doctrine_Record turns into deleted state when it is deleted
*/
const STATE_DELETED = 6;
/**
* CALLBACK CONSTANTS
*/
/**
* RAW CALLBACK
*
* when using a raw callback and the property if a record is changed using this callback the
* record state remains untouched
*/
const CALLBACK_RAW = 1;
/**
* STATE-WISE CALLBACK
*
* state-wise callback means that when callback is used and the property is changed the
* record state is also updated
*/
const CALLBACK_STATEWISE = 2;
/**
* @var object Doctrine_Table $table the factory that created this data access object
*/
......@@ -119,9 +99,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
*/
private $originals = array();
/**
* @var array $filters
* @var Doctrine_Validator_ErrorStack error stack object
*/
private $filters = array();
private $errorStack;
/**
* @var integer $index this index is used for creating object identifiers
*/
......@@ -155,7 +135,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
}
// Check if the current connection has the records table in its registry
// If not this is record is only used for creating table definition and setting up
// If not this record is only used for creating table definition and setting up
// relations.
if($this->table->getConnection()->hasTable($this->table->getComponentName())) {
......@@ -210,6 +190,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onLoad($this);
}
$this->errorStack = new Doctrine_Validator_ErrorStack();
$repository = $this->table->getRepository();
$repository->add($this);
}
......@@ -218,6 +200,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
* initNullObject
*
* @param Doctrine_Null $null
* @return void
*/
public static function initNullObject(Doctrine_Null $null) {
self::$null = $null;
......@@ -245,11 +228,37 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
public function getOID() {
return $this->oid;
}
/**
* isValid
*
* @return boolean whether or not this record passes all column validations
*/
public function isValid() {
if( ! $this->table->getAttribute(Doctrine::ATTR_VLD))
return true;
$validator = new Doctrine_Validator();
if($validator->validateRecord($this))
return true;
$this->errorStack->merge($validator->getErrorStack());
return false;
}
/**
* getErrorStack
*
* @return Doctrine_Validator_ErrorStack returns the errorStack associated with this record
*/
public function getErrorStack() {
return $this->errorStack;
}
/**
* setDefaultValues
* sets the default values for records internal data
*
* @param boolean $overwrite whether or not to overwrite the already set values
* @param boolean $overwrite whether or not to overwrite the already set values
* @return boolean
*/
public function setDefaultValues($overwrite = false) {
......@@ -721,7 +730,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$value = $this->table->invokeSet($this, $name, $value);
$value = $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onSetProperty($this, $name, $value);
if($value === null)
$value = self::$null;
......@@ -825,7 +834,11 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$saveLater = $conn->saveRelated($this);
$conn->save($this);
if( ! $this->isValid()) {
$conn->getTransaction()->addInvalid($this);
} else {
$conn->save($this);
}
foreach($saveLater as $fk) {
$table = $fk->getTable();
......@@ -1105,6 +1118,16 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
$this->modified = array();
}
}
/**
* assignOriginals
*
* @param string $alias
* @param Doctrine_Collection $coll
* @return void
*/
public function assignOriginals($alias, Doctrine_Collection $coll) {
$this->originals[$alias] = $coll;
}
/**
* returns the primary keys of this object
*
......
......@@ -26,62 +26,15 @@
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Validator {
/**
* ERROR CONSTANTS
*/
/**
* constant for length validation error
*/
const ERR_LENGTH = 0;
/**
* constant for type validation error
*/
const ERR_TYPE = 1;
/**
* constant for general validation error
*/
const ERR_VALID = 2;
/**
* constant for unique validation error
*/
const ERR_UNIQUE = 3;
/**
* constant for blank validation error
*/
const ERR_NOTBLANK = 4;
/**
* constant for date validation error
*/
const ERR_DATE = 5;
/**
* constant for null validation error
*/
const ERR_NOTNULL = 6;
/**
* constant for enum validation error
*/
const ERR_ENUM = 7;
/**
* constant for range validation error
*/
const ERR_RANGE = 8;
/**
* constant for regexp validation error
*/
const ERR_REGEXP = 9;
class Doctrine_Validator {
/**
* @var array $stack error stack
*/
private $stack = array();
private $stack = array();
/**
* @var array $validators an array of validator objects
*/
private static $validators = array();
private static $validators = array();
/**
* @var Doctrine_Null $null a Doctrine_Null object used for extremely fast
* null value testing
......@@ -140,6 +93,8 @@ class Doctrine_Validator {
foreach($data as $key => $value) {
if($value === self::$null)
$value = null;
elseif($value instanceof Doctrine_Record)
$value = $value->getIncremented();
$column = $columns[$key];
......@@ -147,7 +102,7 @@ class Doctrine_Validator {
$value = $record->getTable()->enumIndex($key, $value);
if($value === false) {
$err[$key] = Doctrine_Validator::ERR_ENUM;
$err[$key] = 'enum';
continue;
}
}
......@@ -158,9 +113,10 @@ class Doctrine_Validator {
$length = strlen($value);
if($length > $column[1]) {
$err[$key] = Doctrine_Validator::ERR_LENGTH;
$err[$key] = 'length';
continue;
}
if( ! is_array($column[2]))
$e = explode("|",$column[2]);
else
......@@ -187,25 +143,23 @@ class Doctrine_Validator {
$validator = self::getValidator($name);
if( ! $validator->validate($record, $key, $value, $args)) {
$constant = 'Doctrine_Validator::ERR_'.strtoupper($name);
if(defined($constant))
$err[$key] = constant($constant);
else
$err[$key] = Doctrine_Validator::ERR_VALID;
$err[$key] = $name;
//$err[$key] = 'not valid';
// errors found quit validation looping for this column
break;
}
}
if( ! self::isValidType($value, $column[0])) {
$err[$key] = Doctrine_Validator::ERR_TYPE;
$err[$key] = 'type';
continue;
}
}
if( ! empty($err)) {
$this->stack[$component][] = $err;
$this->stack = $err;
return false;
}
......
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
*/
Doctrine::autoload('Doctrine_Access');
/**
* Doctrine_Validator_ErrorStack
*
* @author Konsta Vesterinen
* @license LGPL
* @package Doctrine
*/
class Doctrine_Validator_ErrorStack extends Doctrine_Access {
private $errors = array();
public function merge($stack) {
if(is_array($stack)) {
$this->errors = array_merge($this->errors, $stack);
}
}
public function get($name) {
if(isset($this->errors[$name]))
return $this->errors[$name];
return null;
}
public function set($name, $value) {
$this->errors[$name] = $value;
}
}
<?php
/*
* $Id$
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.phpdoctrine.com>.
*/
Doctrine::autoload('Doctrine_Exception');
class Doctrine_Validator_Exception extends Doctrine_Exception {
/**
* Doctrine_Validator_Exception
*
* @author Konsta Vesterinen
* @license LGPL
* @package Doctrine
*/
class Doctrine_Validator_Exception extends Doctrine_Exception implements Countable, IteratorAggregate {
/**
* @var Doctrine_Validator $validator
* @var array $invalid
*/
private $validator;
private $invalid = array();
/**
* @param Doctrine_Validator $validator
*/
public function __construct(Doctrine_Validator $validator) {
$this->validator = $validator;
public function __construct(array $invalid) {
$this->invalid = $invalid;
}
/**
* returns the error stack
*
* @return array
*/
public function getErrorStack() {
return $this->validator->getErrorStack();
public function getInvalidRecords() {
return $this->invalid;
}
public function getIterator() {
return new ArrayIterator($this->invalid);
}
public function count() {
return count($this->invalid);
}
/**
* __toString
......@@ -26,8 +55,8 @@ class Doctrine_Validator_Exception extends Doctrine_Exception {
* @return string
*/
public function __toString() {
$string = "Error stack : ".print_r($this->validator->getErrorStack(), true);
return $string.parent::__toString();
return parent::__toString();
}
}
......@@ -82,14 +82,11 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$stack = $validator->getErrorStack();
$this->assertTrue(is_array($stack));
$this->assertTrue(isset($stack['ValidatorTest'][0]));
$stack = $stack['ValidatorTest'][0];
$this->assertEqual($stack['mystring'], Doctrine_Validator::ERR_NOTNULL);
$this->assertEqual($stack['myemail2'], Doctrine_Validator::ERR_NOTBLANK);
$this->assertEqual($stack['myrange'], Doctrine_Validator::ERR_RANGE);
$this->assertEqual($stack['myregexp'], Doctrine_Validator::ERR_REGEXP);
$this->assertEqual($stack['mystring'], 'notnull');
$this->assertEqual($stack['myemail2'], 'notblank');
$this->assertEqual($stack['myrange'], 'range');
$this->assertEqual($stack['myregexp'], 'regexp');
$test->mystring = 'str';
......@@ -113,22 +110,29 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$validator = new Doctrine_Validator();
$validator->validateRecord($user);
$validator->validateRecord($email);
$stack = $validator->getErrorStack();
$this->assertTrue(is_array($stack));
$this->assertEqual($stack["User"][0]["loginname"], Doctrine_Validator::ERR_LENGTH);
$this->assertEqual($stack["User"][0]["password"], Doctrine_Validator::ERR_LENGTH);
$this->assertEqual($stack["User"][0]["created"], Doctrine_Validator::ERR_TYPE);
$this->assertEqual($stack["loginname"], 'length');
$this->assertEqual($stack["password"], 'length');
$this->assertEqual($stack["created"], 'type');
$this->assertEqual($stack["Email"][0]["address"], Doctrine_Validator::ERR_VALID);
$validator->validateRecord($email);
$stack = $validator->getErrorStack();
$this->assertEqual($stack["address"], 'email');
$email->address = "arnold@example.com";
$validator->validateRecord($email);
$stack = $validator->getErrorStack();
$this->assertEqual($stack["Email"][1]["address"], Doctrine_Validator::ERR_UNIQUE);
$this->assertEqual($stack["address"], 'unique');
$email->isValid();
$this->assertTrue($email->getErrorStack() instanceof Doctrine_Validator_ErrorStack);
}
public function testIsValidEmail() {
......@@ -156,7 +160,7 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$user->name = "this is an example of too long name not very good example but an example nevertheless";
$user->save();
} catch(Doctrine_Validator_Exception $e) {
$this->assertEqual($e->getErrorStack(),array("User" => array(array("name" => 0))));
$this->assertEqual($e->count(), 1);
}
try {
......@@ -164,12 +168,19 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase {
$user->Email->address = "jackdaniels@drinkmore.info...";
$user->name = "this is an example of too long user name not very good example but an example nevertheles";
$user->save();
$this->fail();
} catch(Doctrine_Validator_Exception $e) {
$a = $e->getErrorStack();
$this->pass();
$a = $e->getInvalidRecords();
}
$this->assertTrue(is_array($a));
$this->assertEqual($a["Email"][0]["address"], Doctrine_Validator::ERR_VALID);
$this->assertEqual($a["User"][0]["name"], Doctrine_Validator::ERR_LENGTH);
$emailStack = $a[array_search($user->Email, $a)]->getErrorStack();
$userStack = $a[array_search($user, $a)]->getErrorStack();
$this->assertEqual($emailStack["address"], 'email');
$this->assertEqual($userStack["name"], 'length');
$this->manager->setAttribute(Doctrine::ATTR_VLD, false);
}
......
......@@ -48,9 +48,12 @@ require_once("DataDictSqliteTestCase.php");
require_once("CustomResultSetOrderTestCase.php");
error_reporting(E_ALL);
print "<pre>";
$test = new GroupTest("Doctrine Framework Unit Tests");
$test->addTestCase(new Doctrine_ValidatorTestCase());
$test->addTestCase(new Doctrine_Query_MultiJoin_TestCase());
$test->addTestCase(new Doctrine_Relation_TestCase());
......@@ -75,8 +78,6 @@ $test->addTestCase(new Doctrine_BatchIteratorTestCase());
$test->addTestCase(new Doctrine_ConfigurableTestCase());
$test->addTestCase(new Doctrine_ValidatorTestCase());
$test->addTestCase(new Doctrine_Collection_OffsetTestCase());
$test->addTestCase(new Doctrine_PessimisticLockingTestCase());
......@@ -124,6 +125,7 @@ $test->addTestCase(new Doctrine_Query_ComponentAlias_TestCase());
$test->addTestCase(new Doctrine_Query_Subquery_TestCase());
$test->addTestCase(new Doctrine_Record_Filter_TestCase());
//$test->addTestCase(new Doctrine_Cache_FileTestCase());
//$test->addTestCase(new Doctrine_Cache_SqliteTestCase());
......
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