From 171226d53232d1aefbfc02d9f6539a294f656434 Mon Sep 17 00:00:00 2001 From: romanb Date: Tue, 10 Oct 2006 16:15:43 +0000 Subject: [PATCH] Continued work on the validation component. Ticket: 150 --- lib/Doctrine/Access.php | 232 +-- lib/Doctrine/Record.php | 8 +- lib/Doctrine/Validator/ErrorStack.php | 59 +- lib/Doctrine/Validator/Required.php | 13 - ... Validators - Analyzing the ErrorStack.php | 27 - ...omponents - Validators - Introduction.php} | 0 ...ponents - Validators - More Validation.php | 26 + ...ents - Validators - Valid or Not Valid.php | 28 + ...- Validators - Validating transactions.php | 42 - ... Validators - Analyzing the ErrorStack.php | 0 ...components - Validators - Introduction.php | 17 + ...components - Validators - Intruduction.php | 11 - ...ponents - Validators - More Validation.php | 23 + ...ents - Validators - Valid or Not Valid.php | 22 + ...- Validators - Validating transactions.php | 5 - manual/documentation.php | 1328 ++++++++--------- tests/ValidatorTestCase.php | 34 +- 17 files changed, 936 insertions(+), 939 deletions(-) delete mode 100644 lib/Doctrine/Validator/Required.php delete mode 100644 manual/codes/Advanced components - Validators - Analyzing the ErrorStack.php rename manual/codes/{Advanced components - Validators - Intruduction.php => Advanced components - Validators - Introduction.php} (100%) create mode 100644 manual/codes/Advanced components - Validators - More Validation.php create mode 100644 manual/codes/Advanced components - Validators - Valid or Not Valid.php delete mode 100644 manual/codes/Advanced components - Validators - Validating transactions.php delete mode 100644 manual/docs/Advanced components - Validators - Analyzing the ErrorStack.php create mode 100644 manual/docs/Advanced components - Validators - Introduction.php delete mode 100644 manual/docs/Advanced components - Validators - Intruduction.php create mode 100644 manual/docs/Advanced components - Validators - More Validation.php create mode 100644 manual/docs/Advanced components - Validators - Valid or Not Valid.php delete mode 100644 manual/docs/Advanced components - Validators - Validating transactions.php diff --git a/lib/Doctrine/Access.php b/lib/Doctrine/Access.php index 29968e5f8..8f40559f8 100644 --- a/lib/Doctrine/Access.php +++ b/lib/Doctrine/Access.php @@ -1,116 +1,116 @@ -. - */ -/** - * Doctrine_Access - * - * the purpose of Doctrine_Access is to provice array access - * and property overload interface for subclasses - * - * @package Doctrine ORM - * @url www.phpdoctrine.com - * @license LGPL - */ -abstract class Doctrine_Access implements ArrayAccess { - /** - * setArray - * @param array $array an array of key => value pairs - * @return Doctrine_Access - */ - public function setArray(array $array) { - foreach($array as $k=>$v): - $this->set($k,$v); - endforeach; - - return $this; - } - /** - * __set -- an alias of set() - * @see set, offsetSet - * @param $name - * @param $value - */ - public function __set($name,$value) { - $this->set($name,$value); - } - /** - * __get -- an alias of get() - * @see get, offsetGet - * @param mixed $name - * @return mixed - */ - public function __get($name) { - return $this->get($name); - } - /** - * __isset() - * - * @param string $name - */ - public function __isset($name) { - return $this->contains($name); - } - /** - * __unset() - * - * @param string $name - */ - public function __unset($name) { - return $this->remove($name); - } - /** - * @param mixed $offset - * @return boolean -- whether or not the data has a field $offset - */ - public function offsetExists($offset) { - return $this->contains($offset); - } - /** - * offsetGet -- an alias of get() - * @see get, __get - * @param mixed $offset - * @return mixed - */ - public function offsetGet($offset) { - return $this->get($offset); - } - /** - * sets $offset to $value - * @see set, __set - * @param mixed $offset - * @param mixed $value - * @return void - */ - public function offsetSet($offset, $value) { - if( ! isset($offset)) { - $this->add($value); - } else - $this->set($offset,$value); - } - /** - * unset a given offset - * @see set, offsetSet, __set - * @param mixed $offset - */ - public function offsetUnset($offset) { - return $this->remove($offset); - } -} - +. + */ +/** + * Doctrine_Access + * + * the purpose of Doctrine_Access is to provice array access + * and property overload interface for subclasses + * + * @package Doctrine ORM + * @url www.phpdoctrine.com + * @license LGPL + */ +abstract class Doctrine_Access implements ArrayAccess { + /** + * setArray + * @param array $array an array of key => value pairs + * @return Doctrine_Access + */ + public function setArray(array $array) { + foreach($array as $k=>$v): + $this->set($k,$v); + endforeach; + + return $this; + } + /** + * __set -- an alias of set() + * @see set, offsetSet + * @param $name + * @param $value + */ + public function __set($name,$value) { + $this->set($name,$value); + } + /** + * __get -- an alias of get() + * @see get, offsetGet + * @param mixed $name + * @return mixed + */ + public function __get($name) { + return $this->get($name); + } + /** + * __isset() + * + * @param string $name + */ + public function __isset($name) { + return $this->contains($name); + } + /** + * __unset() + * + * @param string $name + */ + public function __unset($name) { + return $this->remove($name); + } + /** + * @param mixed $offset + * @return boolean -- whether or not the data has a field $offset + */ + public function offsetExists($offset) { + return $this->contains($offset); + } + /** + * offsetGet -- an alias of get() + * @see get, __get + * @param mixed $offset + * @return mixed + */ + public function offsetGet($offset) { + return $this->get($offset); + } + /** + * sets $offset to $value + * @see set, __set + * @param mixed $offset + * @param mixed $value + * @return void + */ + public function offsetSet($offset, $value) { + if( ! isset($offset)) { + $this->add($value); + } else + $this->set($offset,$value); + } + /** + * unset a given offset + * @see set, offsetSet, __set + * @param mixed $offset + */ + public function offsetUnset($offset) { + return $this->remove($offset); + } +} + diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 7b783c585..a628a6fc7 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -236,11 +236,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite public function isValid() { if( ! $this->table->getAttribute(Doctrine::ATTR_VLD)) return true; - + + // Clear the stack from any previous errors. + $this->errorStack->clear(); + + // Run validation process $validator = new Doctrine_Validator(); - // Run validators $validator->validateRecord($this); - // Run custom validation $this->validate(); return $this->errorStack->count() == 0 ? true : false; diff --git a/lib/Doctrine/Validator/ErrorStack.php b/lib/Doctrine/Validator/ErrorStack.php index 8eaf3de3c..9bf0a818d 100644 --- a/lib/Doctrine/Validator/ErrorStack.php +++ b/lib/Doctrine/Validator/ErrorStack.php @@ -27,7 +27,7 @@ * @license LGPL * @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. @@ -49,8 +49,8 @@ class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorA * @param string $invalidFieldName * @param string $errorType */ - public function add($invalidFieldName, $errorType = 'general') { - $this->errors[$invalidFieldName][] = array('type' => $errorType); + public function add($invalidFieldName, $errorCode = 'general') { + $this->errors[$invalidFieldName][] = $errorCode; } /** @@ -70,66 +70,35 @@ class Doctrine_Validator_ErrorStack implements ArrayAccess, Countable, IteratorA * @param unknown_type $name * @return unknown */ - public function get($name) { - return $this[$name]; - } - - /** 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 get($fieldName) { + return isset($this->errors[$fieldName]) ? $this->errors[$fieldName] : null; } /** * Enter description here... * - * @param string $offset - * @param mixed $value - * @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed. + * @param unknown_type $name */ - public function offsetSet($offset, $value) { - throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be added through - Doctrine_Validator_ErrorStack::add()"); + public function set($fieldName, $errorCode) { + $this->add($fieldName, $errorCode); } /** * Enter description here... * - * @param unknown_type $offset + * @return unknown */ - public function offsetExists($offset) { - return isset($this->errors[$offset]); + public function contains($fieldName) { + return array_key_exists($fieldName, $this->errors); } /** - * Enter description here... - * - * @param unknown_type $offset - * @throws Doctrine_Validator_ErrorStack_Exception Always thrown since this operation is not allowed. + * Removes all errors from the stack. */ - public function offsetUnset($offset) { - throw new Doctrine_Validator_ErrorStack_Exception("Errors can only be removed - through Doctrine_Validator_ErrorStack::remove()"); + public function clear() { + $this->errors = array(); } - /** - * Enter description here... - * - * @param unknown_type $stack - */ - /* - public function merge($stack) { - if(is_array($stack)) { - $this->errors = array_merge($this->errors, $stack); - } - }*/ - /** IteratorAggregate implementation */ diff --git a/lib/Doctrine/Validator/Required.php b/lib/Doctrine/Validator/Required.php deleted file mode 100644 index fafed397d..000000000 --- a/lib/Doctrine/Validator/Required.php +++ /dev/null @@ -1,13 +0,0 @@ -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; - } - } -} -?> diff --git a/manual/codes/Advanced components - Validators - Intruduction.php b/manual/codes/Advanced components - Validators - Introduction.php similarity index 100% rename from manual/codes/Advanced components - Validators - Intruduction.php rename to manual/codes/Advanced components - Validators - Introduction.php diff --git a/manual/codes/Advanced components - Validators - More Validation.php b/manual/codes/Advanced components - Validators - More Validation.php new file mode 100644 index 000000000..cba2eec47 --- /dev/null +++ b/manual/codes/Advanced components - Validators - More Validation.php @@ -0,0 +1,26 @@ +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(, ) + $this->errorStack->add('address', 'myCustomValidationTypeError'); + } + } +} +?> diff --git a/manual/codes/Advanced components - Validators - Valid or Not Valid.php b/manual/codes/Advanced components - Validators - Valid or Not Valid.php new file mode 100644 index 000000000..1c4fc8e20 --- /dev/null +++ b/manual/codes/Advanced components - Validators - Valid or Not Valid.php @@ -0,0 +1,28 @@ +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; + } + } +} +?> diff --git a/manual/codes/Advanced components - Validators - Validating transactions.php b/manual/codes/Advanced components - Validators - Validating transactions.php deleted file mode 100644 index 6f4160430..000000000 --- a/manual/codes/Advanced components - Validators - Validating transactions.php +++ /dev/null @@ -1,42 +0,0 @@ -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 -?> diff --git a/manual/docs/Advanced components - Validators - Analyzing the ErrorStack.php b/manual/docs/Advanced components - Validators - Analyzing the ErrorStack.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/manual/docs/Advanced components - Validators - Introduction.php b/manual/docs/Advanced components - Validators - Introduction.php new file mode 100644 index 000000000..ef9d61bd2 --- /dev/null +++ b/manual/docs/Advanced components - Validators - Introduction.php @@ -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).
+
+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. diff --git a/manual/docs/Advanced components - Validators - Intruduction.php b/manual/docs/Advanced components - Validators - Intruduction.php deleted file mode 100644 index 737dc2cd7..000000000 --- a/manual/docs/Advanced components - Validators - Intruduction.php +++ /dev/null @@ -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. -

-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. -

-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. diff --git a/manual/docs/Advanced components - Validators - More Validation.php b/manual/docs/Advanced components - Validators - More Validation.php new file mode 100644 index 000000000..50c0bdb7b --- /dev/null +++ b/manual/docs/Advanced components - Validators - More Validation.php @@ -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.
+
+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:
+
+- 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. +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:
diff --git a/manual/docs/Advanced components - Validators - Valid or Not Valid.php b/manual/docs/Advanced components - Validators - Valid or Not Valid.php new file mode 100644 index 000000000..6478020eb --- /dev/null +++ b/manual/docs/Advanced components - Validators - Valid or Not Valid.php @@ -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.
+
+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. diff --git a/manual/docs/Advanced components - Validators - Validating transactions.php b/manual/docs/Advanced components - Validators - Validating transactions.php deleted file mode 100644 index e8c448553..000000000 --- a/manual/docs/Advanced components - Validators - Validating transactions.php +++ /dev/null @@ -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. -

-Validation errors are being stacked into Doctrine_Validator_Exception. diff --git a/manual/documentation.php b/manual/documentation.php index 148db4c49..e80341b78 100644 --- a/manual/documentation.php +++ b/manual/documentation.php @@ -1,664 +1,664 @@ -".$t."

\n"; - $c = ""; - - if(file_exists("docs/$e.php")) { - rename("docs/$e.php","docs/$title - $t.php"); - } - if(file_exists("docs/$t.php")) { - rename("docs/$t.php","docs/$title - $t.php"); - } - if(file_exists("docs/$title - $t.php")) { - $c = file_get_contents("docs/$title - $t.php"); - if(substr($c,0,5) == "
"; - } - $c = ""; - if(file_exists("codes/$e.php")) { - rename("codes/$e.php","codes/$title - $t.php"); - } - if(file_exists("codes/$t.php")) { - rename("codes/$t.php","codes/$title - $t.php"); - } - if(file_exists("codes/$title - $t.php")) { - print ""; - print ""; - print "
"; - $c = file_get_contents("codes/$title - $t.php"); - $c = trim($c); - - $h->loadString($c); - print $h->toHtml(); - print "
"; - } - print "
"; -} - -function render_block($name) { - - if(file_exists("docs/$name.php")) { - $c = file_get_contents("docs/$name.php"); - if(substr($c,0,5) == "
"; - } - } - if(file_exists("codes/$name.php")) { - $c = file_get_contents("codes/$name.php"); - $c = trim($c); - renderCode($c); - } -} -function renderCode($c = null) { - if( ! empty($c)) { - $h = new PHP_Highlight; - $h->loadString($c); - - print ""; - print ""; - print "
"; - - print $h->toHtml(); - print "
"; - } -} -function array2path($array, $path = '') { - $arrayValues = array(); - - $index = 1; - foreach ($array as $k => $value) { - $p = ($path !== '')?$path.".".$index:$index; - - if (is_scalar($value) || is_resource($value)) { - $arrayValues[$p] = $value; - } elseif (is_array($value)) { - $arrayValues[$p] = $k; - $arrayValues = $arrayValues + array2path($value, $p); - } - $index++; - } - - return $arrayValues; -} -$menu = array("Getting started" => - array( - "Requirements", - "Installation", - "Compiling", - "Starting new project", - "Setting table definition" => array( - "Introduction", - "Table and class naming", - "Field(Column) naming", - "Data types and lengths", - "Constraints and validators", - "Default values", - "Enum emulation", - - ), - - "Record identifiers" => array( - "Introduction", - "Autoincremented", - "Natural", - "Composite", - "Sequential") - ), - /** - "Schema reference" => - array( - "Data types" => array( - "Boolean", - "Integer", - "Float", - "String", - "Array", - "Object", - "Blob", - "Clob", - "Timestamp", - "Date", - "Enum", - "Gzip", - ), - - ), - */ - "Basic Components" => - array( - "Manager" - => array("Introduction", - "Opening a new connection", - "Managing connections"), - "Record" - => array("Introduction", - "Creating new records", - "Retrieving existing records", - "Accessing properties", - "Updating records", - "Deleting records", - "Getting record state", - "Getting object copy", - "Serializing", - "Checking Existence", - "Callbacks"), - "Connection" - => array("Introduction", - "Available drivers", - "Getting a table object", - "Flushing the connection", - "Querying the database", - "Getting connection state"), - - "Collection" - => array("Introduction", - "Accessing elements", - "Adding new elements", - "Getting collection count", - "Saving the collection", - "Deleting collection", - //"Fetching strategies", - "Key mapping", - "Loading related records", - "Collection expanding", - ), - - "Table" => array("Introduction", - "Getting table information", - "Finder methods", - "Custom table classes", - "Custom finders", - "Getting relation objects"), - - "Query" => array("Introduction", - "FROM - selecting tables", - "LIMIT and OFFSET - limiting the query results", - "WHERE - setting query conditions", - "ORDER BY - sorting query results", - //"Fetching strategies", - //"Lazy property fetching", - "Method overloading", - "Relation operators", - "Bound parameters", - "Aggregate functions", - "DQL - SQL conversion"), - "RawSql" => array( - "Introduction", - "Using SQL", - "Adding components", - "Method overloading"), - "DB" => array( - "Introduction", - "Connecting to a database", - "Using event listeners", - "Chaining listeners"), - /** - "Statement - UNDER CONSTRUCTION" => array("Introduction", - "Setting parameters", - "Getting parameters", - "Getting row count", - "Executing the statement"), - */ - "Exceptions" => array( - "Overview", - "List of exceptions" - ) - ), - "Mapping object relations" => - array( - "Introduction", - "Composites and aggregates", - "Relation aliases", - "Foreign key associations" => array( - "One-to-One", - "One-to-Many, Many-to-One", - "Tree structure"), - - "Join table associations" => array( - "One-to-One", - "One-to-Many, Many-to-One", - "Many-to-Many", - "Self-referencing"), - "Dealing with relations" => array( - "Creating related records", - "Retrieving related records", - "Updating related records", - "Deleting related records", - "Working with associations"), - "Inheritance" => - array("One table many classes", - "One table one class", - "Column aggregation" - ), - ), - "Configuration" => - array( - "Introduction", - "Levels of configuration", - "Setting attributes" => array( - "Table creation", - "Fetching strategy", - "Batch size", - "Session lockmode", - "Event listener", - "Validation", - "Offset collection limit" - ) - ), - - - - "Advanced components" => array( - "Eventlisteners" => - array( - "Introduction", - "Creating new listener", - "List of events", - "Listening events", - "Chaining", - "AccessorInvoker", - "Creating a logger", - ), - "Validators" => array( - "Intruduction", - "Validating transactions", - "Analyzing the ErrorStack", - "List of predefined validators" - ), - "View" => array( - "Intoduction", - "Managing views", - "Using views" - ), - "Cache" => array( - "Introduction", - "Query cache"), - - "Locking Manager" => array( - "Introduction", - "Examples", - "Planned", - "Technical Details", - "Maintainer"), - /** - "Debugger" => array( - "Introduction", - "Debugging actions"), - "Library" => array( - "Introduction", - "Using library functions"), - "Iterator" => array( - "Introduction", - "BatchIterator", - "ExpandableIterator", - "OffsetIterator") - */ - ), - "DQL (Doctrine Query Language)" => - - array('Introduction', - 'FROM clause', - 'WHERE clause', - 'Conditional expressions' => - array('Literals', - 'Identification variables', - 'Path expressions', - 'Input parameters', - 'Contidional expression composition', - 'Operators and operator precedence', - 'Between expressions', - 'In expressions', - 'Like Expressions', - 'Null Comparison Expressions', - 'Empty Collection Comparison Expressions', - 'Collection Member Expressions', - 'Exists Expressions', - 'All or Any Expressions', - 'Subqueries'), - 'Functional Expressions' => - array('String functions', - 'Arithmetic functions', - 'Datetime functions', - 'Collection functions'), - - 'GROUP BY, HAVING clauses', - 'ORDER BY clause', - 'LIMIT and OFFSET clauses', - 'Examples', - 'BNF'), - /** - 'Functions' => array( - 'Contains', - 'Regexp', - 'Like'), - 'Operators' => array( - 'Logical operators') - - - */ - - "Transactions" => array( - "Introduction", - "Unit of work", - "Locking strategies" => - array("Pessimistic locking", - "Optimistic locking"), - "Nesting" - ), - /** - "Developer components" => array( - "DataDict" => array( - "Introduction", - "Usage" - ), - "IndexGenerator" => - array( - "Introduction", - "Usage"), - "Relation" => array( - "Introduction", - "Types of relations", - ), - "Null" => array( - "Introduction", - "Extremely fast null value checking" - ), - "Access" => array( - "Introduction", - "Usage" - ), - "Configurable" => array( - "Introduction", - "Usage" - ), - ), - */ - "Technology" => array( - "Architecture", - "Design patterns used", - "Speed", - "Internal optimizations" => - - array("DELETE", - "INSERT", - "UPDATE"), - ), - "Real world examples" => array("User management system","Forum application","Album lister"), - - "Coding standards" => array( - "Overview" => - array( - "Scope", - "Goals" - ), - "PHP File Formatting" => array( - "General", - "Indentation", - "Maximum line length", - "Line termination" - ), - - "Naming Conventions" => array( - "Classes", - "Interfaces", - "Filenames", - "Functions and methods", - "Variables", - "Constants", - "Record columns", - ), - - - "Coding Style" => array( - "PHP code demarcation", - "Strings", - "Arrays", - "Classes", - "Functions and methods", - "Control statements", - "Inline documentation" - ), - ) - ); - - -?> - - - - -
- - - - - - - - - - - - - - -
- Doctrine - PHP Data Persistence and ORM Tool -
-
- $titles) { - print $i.". ".$title."
\n"; - $i2 = 1; - foreach($titles as $k => $t) { - $e = "$i.".$i2.""; - if(is_array($t)) { - print "
".$e." ".$k."
\n"; - - $i3 = 1; - foreach($t as $k2 => $v2) { - $str = ""; - if( ! file_exists("docs/$title - $k - $v2.php")) { - $missing[0]++; - $str .= " [ doc ] "; - touch("docs/$title - $k - $v2.php"); - } - if( ! file_exists("codes/$title - $k - $v2.php")) { - $missing[1]++; - $str .= " [ code ] "; - touch("codes/$title - $k - $v2.php"); - - } - - $e = implode(".",array($i,$i2,$i3)); - print "
".$e." ".$v2."$str
\n"; - $i3++; - } - } else { - $str = ""; - if( ! file_exists("docs/$title - $t.php")) { - $missing[0]++; - $str .= " [ doc ] "; - touch("docs/$title - $t.php"); - } - if( ! file_exists("codes/$title - $t.php")) { - $missing[1]++; - $str .= " [ code ] "; - touch("codes/$title - $t.php"); - } - print "
".$e." ".$t."$str
\n"; - } - $i2++; - } - $i++; - } - } else { - $i = 1; - $ex = explode(".",$_REQUEST["index"]); - - - $paths = array2path($menu); - - if( ! isset($paths[$ex[0]])) - exit; - - $break = false; - $tmp = $paths; - foreach($tmp as $path => $title) { - $e = explode(".", $path); - if(count($e) > 2) { - unset($tmp[$path]); - } - } - $prev = 1; - foreach($tmp as $path => $title) { - if($break) - break; - - if($path == $_REQUEST["index"]) { - $break = true; - } else { - $prev = $path; - } - } - $index = $_REQUEST['index']; - print ""; - print ""; - print ""; - print ""; - print ""; - print ""; - print "
".$paths[$ex[0]]."
PrevNext
 
"; - - - $tmp = $ex; - if(count($tmp) > 2) { - //array_pop($tmp); - } - foreach($tmp as $k => $v) { - $numbers[] = $v; - $curr = implode(".",$numbers); - $stack[] = $paths[$curr]; - } - if(isset($ex[1])) { - $name = implode(" - ", $stack); - - print "".$paths[$curr]."
"; - - $n = $numbers; - - $o = $paths[$n[0]]; - $numpath = implode(".", array($n[0], $n[1])); - $o2 = $paths[$numpath]; - - $value = $menu[$o]; - if( ! is_array($value)) - exit; - - - if(in_array($o2, $value)) { - render_block($name); - } else { - $value = $menu[$o][$o2]; - - if(is_array($value)) { - foreach($value as $k => $title) { - $numpath2 = $numpath . '.' . ($k + 1); - print "
".$title."
"; - - $s = $name." - ".$title; - - render_block($s); - } - } - } - } else { - //if( ! isset($menu[$ex[0]])) - // exit; - $tmp = $paths[$ex[0]]; - $i = 1; - foreach($menu[$tmp] as $title => $value) { - $n = $ex[0].".".$i; - - if(is_scalar($value)) { - print "
".$n.". ".$value."
\n"; - } else { - print "
".$n.". ".$title."
\n"; - } - $i++; - } - } - } - ?> -
- -- index
\n"; - foreach($menu as $title => $titles) { - print "
".$i.". ".$title."
\n"; - $i++; - } - ?> -
-
- $titles) { - if($i == $ex[0]) { - - print $i.". ".$title."

\n"; - $i2 = 1; - foreach($titles as $k => $t) { - $e = "$i.".$i2; - - if(is_array($t)) { - $tmp = "$title - $k"; - - if( ! isset($ex[1]) || $i2 != $ex[1]) { - $i2++; - continue; - } - - print $e." ".$k."


\n"; - foreach($t as $k2 => $t2) { - if( ! isset($ex[1]) || $i2 != $ex[1]) - break; - $e = "$i.".$i2.".".($k2+1); - render($tmp,$t2,$e); - } - } else { - if( ! isset($ex[1])) { - render($title,$t,$e); - } - } - $i2++; - } - } - $i++; - - } - */ - ?> -
-
+".$t."

\n"; + $c = ""; + + if(file_exists("docs/$e.php")) { + rename("docs/$e.php","docs/$title - $t.php"); + } + if(file_exists("docs/$t.php")) { + rename("docs/$t.php","docs/$title - $t.php"); + } + if(file_exists("docs/$title - $t.php")) { + $c = file_get_contents("docs/$title - $t.php"); + if(substr($c,0,5) == "
"; + } + $c = ""; + if(file_exists("codes/$e.php")) { + rename("codes/$e.php","codes/$title - $t.php"); + } + if(file_exists("codes/$t.php")) { + rename("codes/$t.php","codes/$title - $t.php"); + } + if(file_exists("codes/$title - $t.php")) { + print ""; + print ""; + print "
"; + $c = file_get_contents("codes/$title - $t.php"); + $c = trim($c); + + $h->loadString($c); + print $h->toHtml(); + print "
"; + } + print "
"; +} + +function render_block($name) { + + if(file_exists("docs/$name.php")) { + $c = file_get_contents("docs/$name.php"); + if(substr($c,0,5) == "
"; + } + } + if(file_exists("codes/$name.php")) { + $c = file_get_contents("codes/$name.php"); + $c = trim($c); + renderCode($c); + } +} +function renderCode($c = null) { + if( ! empty($c)) { + $h = new PHP_Highlight; + $h->loadString($c); + + print ""; + print ""; + print "
"; + + print $h->toHtml(); + print "
"; + } +} +function array2path($array, $path = '') { + $arrayValues = array(); + + $index = 1; + foreach ($array as $k => $value) { + $p = ($path !== '')?$path.".".$index:$index; + + if (is_scalar($value) || is_resource($value)) { + $arrayValues[$p] = $value; + } elseif (is_array($value)) { + $arrayValues[$p] = $k; + $arrayValues = $arrayValues + array2path($value, $p); + } + $index++; + } + + return $arrayValues; +} +$menu = array("Getting started" => + array( + "Requirements", + "Installation", + "Compiling", + "Starting new project", + "Setting table definition" => array( + "Introduction", + "Table and class naming", + "Field(Column) naming", + "Data types and lengths", + "Constraints and validators", + "Default values", + "Enum emulation", + + ), + + "Record identifiers" => array( + "Introduction", + "Autoincremented", + "Natural", + "Composite", + "Sequential") + ), + /** + "Schema reference" => + array( + "Data types" => array( + "Boolean", + "Integer", + "Float", + "String", + "Array", + "Object", + "Blob", + "Clob", + "Timestamp", + "Date", + "Enum", + "Gzip", + ), + + ), + */ + "Basic Components" => + array( + "Manager" + => array("Introduction", + "Opening a new connection", + "Managing connections"), + "Record" + => array("Introduction", + "Creating new records", + "Retrieving existing records", + "Accessing properties", + "Updating records", + "Deleting records", + "Getting record state", + "Getting object copy", + "Serializing", + "Checking Existence", + "Callbacks"), + "Connection" + => array("Introduction", + "Available drivers", + "Getting a table object", + "Flushing the connection", + "Querying the database", + "Getting connection state"), + + "Collection" + => array("Introduction", + "Accessing elements", + "Adding new elements", + "Getting collection count", + "Saving the collection", + "Deleting collection", + //"Fetching strategies", + "Key mapping", + "Loading related records", + "Collection expanding", + ), + + "Table" => array("Introduction", + "Getting table information", + "Finder methods", + "Custom table classes", + "Custom finders", + "Getting relation objects"), + + "Query" => array("Introduction", + "FROM - selecting tables", + "LIMIT and OFFSET - limiting the query results", + "WHERE - setting query conditions", + "ORDER BY - sorting query results", + //"Fetching strategies", + //"Lazy property fetching", + "Method overloading", + "Relation operators", + "Bound parameters", + "Aggregate functions", + "DQL - SQL conversion"), + "RawSql" => array( + "Introduction", + "Using SQL", + "Adding components", + "Method overloading"), + "DB" => array( + "Introduction", + "Connecting to a database", + "Using event listeners", + "Chaining listeners"), + /** + "Statement - UNDER CONSTRUCTION" => array("Introduction", + "Setting parameters", + "Getting parameters", + "Getting row count", + "Executing the statement"), + */ + "Exceptions" => array( + "Overview", + "List of exceptions" + ) + ), + "Mapping object relations" => + array( + "Introduction", + "Composites and aggregates", + "Relation aliases", + "Foreign key associations" => array( + "One-to-One", + "One-to-Many, Many-to-One", + "Tree structure"), + + "Join table associations" => array( + "One-to-One", + "One-to-Many, Many-to-One", + "Many-to-Many", + "Self-referencing"), + "Dealing with relations" => array( + "Creating related records", + "Retrieving related records", + "Updating related records", + "Deleting related records", + "Working with associations"), + "Inheritance" => + array("One table many classes", + "One table one class", + "Column aggregation" + ), + ), + "Configuration" => + array( + "Introduction", + "Levels of configuration", + "Setting attributes" => array( + "Table creation", + "Fetching strategy", + "Batch size", + "Session lockmode", + "Event listener", + "Validation", + "Offset collection limit" + ) + ), + + + + "Advanced components" => array( + "Eventlisteners" => + array( + "Introduction", + "Creating new listener", + "List of events", + "Listening events", + "Chaining", + "AccessorInvoker", + "Creating a logger", + ), + "Validators" => array( + "Introduction", + "More Validation", + "Valid or Not Valid", + "List of predefined validators" + ), + "View" => array( + "Intoduction", + "Managing views", + "Using views" + ), + "Cache" => array( + "Introduction", + "Query cache"), + + "Locking Manager" => array( + "Introduction", + "Examples", + "Planned", + "Technical Details", + "Maintainer"), + /** + "Debugger" => array( + "Introduction", + "Debugging actions"), + "Library" => array( + "Introduction", + "Using library functions"), + "Iterator" => array( + "Introduction", + "BatchIterator", + "ExpandableIterator", + "OffsetIterator") + */ + ), + "DQL (Doctrine Query Language)" => + + array('Introduction', + 'FROM clause', + 'WHERE clause', + 'Conditional expressions' => + array('Literals', + 'Identification variables', + 'Path expressions', + 'Input parameters', + 'Contidional expression composition', + 'Operators and operator precedence', + 'Between expressions', + 'In expressions', + 'Like Expressions', + 'Null Comparison Expressions', + 'Empty Collection Comparison Expressions', + 'Collection Member Expressions', + 'Exists Expressions', + 'All or Any Expressions', + 'Subqueries'), + 'Functional Expressions' => + array('String functions', + 'Arithmetic functions', + 'Datetime functions', + 'Collection functions'), + + 'GROUP BY, HAVING clauses', + 'ORDER BY clause', + 'LIMIT and OFFSET clauses', + 'Examples', + 'BNF'), + /** + 'Functions' => array( + 'Contains', + 'Regexp', + 'Like'), + 'Operators' => array( + 'Logical operators') + + + */ + + "Transactions" => array( + "Introduction", + "Unit of work", + "Locking strategies" => + array("Pessimistic locking", + "Optimistic locking"), + "Nesting" + ), + /** + "Developer components" => array( + "DataDict" => array( + "Introduction", + "Usage" + ), + "IndexGenerator" => + array( + "Introduction", + "Usage"), + "Relation" => array( + "Introduction", + "Types of relations", + ), + "Null" => array( + "Introduction", + "Extremely fast null value checking" + ), + "Access" => array( + "Introduction", + "Usage" + ), + "Configurable" => array( + "Introduction", + "Usage" + ), + ), + */ + "Technology" => array( + "Architecture", + "Design patterns used", + "Speed", + "Internal optimizations" => + + array("DELETE", + "INSERT", + "UPDATE"), + ), + "Real world examples" => array("User management system","Forum application","Album lister"), + + "Coding standards" => array( + "Overview" => + array( + "Scope", + "Goals" + ), + "PHP File Formatting" => array( + "General", + "Indentation", + "Maximum line length", + "Line termination" + ), + + "Naming Conventions" => array( + "Classes", + "Interfaces", + "Filenames", + "Functions and methods", + "Variables", + "Constants", + "Record columns", + ), + + + "Coding Style" => array( + "PHP code demarcation", + "Strings", + "Arrays", + "Classes", + "Functions and methods", + "Control statements", + "Inline documentation" + ), + ) + ); + + +?> + + + + +
+ + + + + + + + + + + + + + +
+ Doctrine - PHP Data Persistence and ORM Tool +
+
+ $titles) { + print $i.". ".$title."
\n"; + $i2 = 1; + foreach($titles as $k => $t) { + $e = "$i.".$i2.""; + if(is_array($t)) { + print "
".$e." ".$k."
\n"; + + $i3 = 1; + foreach($t as $k2 => $v2) { + $str = ""; + if( ! file_exists("docs/$title - $k - $v2.php")) { + $missing[0]++; + $str .= " [ doc ] "; + touch("docs/$title - $k - $v2.php"); + } + if( ! file_exists("codes/$title - $k - $v2.php")) { + $missing[1]++; + $str .= " [ code ] "; + touch("codes/$title - $k - $v2.php"); + + } + + $e = implode(".",array($i,$i2,$i3)); + print "
".$e." ".$v2."$str
\n"; + $i3++; + } + } else { + $str = ""; + if( ! file_exists("docs/$title - $t.php")) { + $missing[0]++; + $str .= " [ doc ] "; + touch("docs/$title - $t.php"); + } + if( ! file_exists("codes/$title - $t.php")) { + $missing[1]++; + $str .= " [ code ] "; + touch("codes/$title - $t.php"); + } + print "
".$e." ".$t."$str
\n"; + } + $i2++; + } + $i++; + } + } else { + $i = 1; + $ex = explode(".",$_REQUEST["index"]); + + + $paths = array2path($menu); + + if( ! isset($paths[$ex[0]])) + exit; + + $break = false; + $tmp = $paths; + foreach($tmp as $path => $title) { + $e = explode(".", $path); + if(count($e) > 2) { + unset($tmp[$path]); + } + } + $prev = 1; + foreach($tmp as $path => $title) { + if($break) + break; + + if($path == $_REQUEST["index"]) { + $break = true; + } else { + $prev = $path; + } + } + $index = $_REQUEST['index']; + print ""; + print ""; + print ""; + print ""; + print ""; + print ""; + print "
".$paths[$ex[0]]."
PrevNext
 
"; + + + $tmp = $ex; + if(count($tmp) > 2) { + //array_pop($tmp); + } + foreach($tmp as $k => $v) { + $numbers[] = $v; + $curr = implode(".",$numbers); + $stack[] = $paths[$curr]; + } + if(isset($ex[1])) { + $name = implode(" - ", $stack); + + print "".$paths[$curr]."
"; + + $n = $numbers; + + $o = $paths[$n[0]]; + $numpath = implode(".", array($n[0], $n[1])); + $o2 = $paths[$numpath]; + + $value = $menu[$o]; + if( ! is_array($value)) + exit; + + + if(in_array($o2, $value)) { + render_block($name); + } else { + $value = $menu[$o][$o2]; + + if(is_array($value)) { + foreach($value as $k => $title) { + $numpath2 = $numpath . '.' . ($k + 1); + print "
".$title."
"; + + $s = $name." - ".$title; + + render_block($s); + } + } + } + } else { + //if( ! isset($menu[$ex[0]])) + // exit; + $tmp = $paths[$ex[0]]; + $i = 1; + foreach($menu[$tmp] as $title => $value) { + $n = $ex[0].".".$i; + + if(is_scalar($value)) { + print "
".$n.". ".$value."
\n"; + } else { + print "
".$n.". ".$title."
\n"; + } + $i++; + } + } + } + ?> +
+ -- index
\n"; + foreach($menu as $title => $titles) { + print "
".$i.". ".$title."
\n"; + $i++; + } + ?> +
+
+ $titles) { + if($i == $ex[0]) { + + print $i.". ".$title."

\n"; + $i2 = 1; + foreach($titles as $k => $t) { + $e = "$i.".$i2; + + if(is_array($t)) { + $tmp = "$title - $k"; + + if( ! isset($ex[1]) || $i2 != $ex[1]) { + $i2++; + continue; + } + + print $e." ".$k."


\n"; + foreach($t as $k2 => $t2) { + if( ! isset($ex[1]) || $i2 != $ex[1]) + break; + $e = "$i.".$i2.".".($k2+1); + render($tmp,$t2,$e); + } + } else { + if( ! isset($ex[1])) { + render($title,$t,$e); + } + } + $i2++; + } + } + $i++; + + } + */ + ?> +
+
diff --git a/tests/ValidatorTestCase.php b/tests/ValidatorTestCase.php index 45a5ea5b2..09032d3ab 100644 --- a/tests/ValidatorTestCase.php +++ b/tests/ValidatorTestCase.php @@ -94,10 +94,10 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { $this->assertTrue($stack instanceof Doctrine_Validator_ErrorStack); - $this->assertTrue(in_array(array('type' => 'notnull'), $stack['mystring'])); - $this->assertTrue(in_array(array('type' => 'notblank'), $stack['myemail2'])); - $this->assertTrue(in_array(array('type' => 'range'), $stack['myrange'])); - $this->assertTrue(in_array(array('type' => 'regexp'), $stack['myregexp'])); + $this->assertTrue(in_array('notnull', $stack['mystring'])); + $this->assertTrue(in_array('notblank', $stack['myemail2'])); + $this->assertTrue(in_array('range', $stack['myrange'])); + $this->assertTrue(in_array('regexp', $stack['myregexp'])); $test->mystring = 'str'; @@ -127,19 +127,19 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { $stack = $user->getErrorStack(); $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'])); + $this->assertTrue(in_array('length', $stack['loginname'])); + $this->assertTrue(in_array('length', $stack['password'])); + $this->assertTrue(in_array('type', $stack['created'])); $validator->validateRecord($email); $stack = $email->getErrorStack(); - $this->assertTrue(in_array(array('type' => 'email'), $stack['address'])); + $this->assertTrue(in_array('email', $stack['address'])); $email->address = "arnold@example.com"; $validator->validateRecord($email); $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(); $this->assertEqual(count($invalidRecords), 1); $stack = $invalidRecords[0]->getErrorStack(); - $this->assertTrue(in_array(array('type' => 'length'), $stack['name'])); + $this->assertTrue(in_array('length', $stack['name'])); } try { @@ -196,8 +196,8 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { $emailStack = $a[array_search($user->Email, $a)]->getErrorStack(); $userStack = $a[array_search($user, $a)]->getErrorStack(); - $this->assertTrue(in_array(array('type' => 'email'), $emailStack['address'])); - $this->assertTrue(in_array(array('type' => 'length'), $userStack['name'])); + $this->assertTrue(in_array('email', $emailStack['address'])); + $this->assertTrue(in_array('length', $userStack['name'])); $this->manager->setAttribute(Doctrine::ATTR_VLD, false); } @@ -219,8 +219,16 @@ class Doctrine_ValidatorTestCase extends Doctrine_UnitTestCase { $stack = $invalidRecords[0]->getErrorStack(); $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); } }