diff --git a/Doctrine/Collection.php b/Doctrine/Collection.php index 67213640f..43f8bb077 100644 --- a/Doctrine/Collection.php +++ b/Doctrine/Collection.php @@ -180,6 +180,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $record->rawSet($this->reference_field, $this->reference); } } + } elseif($relation instanceof Doctrine_Association) { + } } /** @@ -249,7 +251,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } elseif($this->relation instanceof Doctrine_Association) { - $asf = $fk->getAssociationFactory(); + $asf = $this->relation->getAssociationFactory(); $query = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local."=".$this->getID(); $table = $fk->getTable(); @@ -411,6 +413,10 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $this->data[$key] = $record; return true; } + + if(in_array($record,$this->data)) { + return false; + } else if(isset($this->generator)) { $key = $this->generator->getIndex($record); diff --git a/Doctrine/Query.php b/Doctrine/Query.php index e2afe5727..d26ea17ec 100644 --- a/Doctrine/Query.php +++ b/Doctrine/Query.php @@ -467,28 +467,36 @@ class Doctrine_Query extends Doctrine_Access { $coll->add($record); } else { $pointer = $this->joins[$name]; + $alias = $this->tables[$pointer]->getAlias($name); + + //print "fetching data : ".$pointer." ".$alias."
"; - $fk = $this->tables[$pointer]->getForeignKey($this->tables[$pointer]->getAlias($name)); + $fk = $this->tables[$pointer]->getForeignKey($alias); + + $last = $prev[$pointer]->getLast(); switch($fk->getType()): case Doctrine_Relation::ONE_COMPOSITE: case Doctrine_Relation::ONE_AGGREGATE: - $last = $prev[$pointer]->getLast(); - $last->rawSet($this->connectors[$name]->getLocal(), $record->getID()); $last->initSingleReference($record); - + $prev[$name] = $record; break; default: // one-to-many relation or many-to-many relation - $last = $prev[$pointer]->getLast(); - - if( ! $last->hasReference($name)) { + + if( ! $last->hasReference($alias)) { + //print "initializing reference : ".$name." ".$alias; $prev[$name] = $this->getCollection($name); $last->initReference($prev[$name],$this->connectors[$name]); + } else { + // previous entry found from identityMap + $prev[$name] = $last->get($alias); } + //print "adding reference : ".$pointer." -> ".$name." as ".$alias."
"; + $last->addReference($record); endswitch; } @@ -930,37 +938,35 @@ class Doctrine_Query extends Doctrine_Access { $this->connectors[$name] = $fk; + switch($mark): + case ":": + $join = 'INNER JOIN '; + break; + case ".": + $join = 'LEFT JOIN '; + break; + default: + throw new Doctrine_Exception("Unknown operator '$mark'"); + endswitch; + + if($fk instanceof Doctrine_ForeignKey || $fk instanceof Doctrine_LocalKey) { - switch($mark): - case ":": - $this->parts["join"][$tname][$tname2] = "INNER JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign(); - break; - case ".": - $this->parts["join"][$tname][$tname2] = "LEFT JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign(); - break; - endswitch; + $this->parts["join"][$tname][$tname2] = $join.$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign(); - $c = $table->getComponentName(); - $this->joins[$name] = $c; } elseif($fk instanceof Doctrine_Association) { $asf = $fk->getAssociationFactory(); - switch($fk->getType()): - case Doctrine_Relation::ONE_AGGREGATE: - case Doctrine_Relation::ONE_COMPOSITE: + $assocTableName = $asf->getTableName(); - break; - case Doctrine_Relation::MANY_AGGREGATE: - case Doctrine_Relation::MANY_COMPOSITE: - - //$this->addWhere("SELECT ".$fk->getLocal()." FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." IN (SELECT ".$fk->getTable()->getComponentName().")"); - $this->parts["from"][$tname] = true; - break; - endswitch; + $this->parts["join"][$tname][$assocTableName] = $join.$assocTableName." ON ".$tname.".id = ".$assocTableName.".".$fk->getLocal(); + $this->parts["join"][$tname][$tname2] = $join.$tname2." ON ".$tname2.".id = ".$assocTableName.".".$fk->getForeign(); } + $c = $table->getComponentName(); + $this->joins[$name] = $c; + $table = $fk->getTable(); } @@ -992,7 +998,7 @@ class Doctrine_Query extends Doctrine_Access { } } catch(Exception $e) { - throw new DQLException($e->getMessage(),$e->getCode()); + throw new DQLException($e->__toString()); } } } diff --git a/Doctrine/Record.php b/Doctrine/Record.php index 42ec0d7ad..9cc9af3ec 100644 --- a/Doctrine/Record.php +++ b/Doctrine/Record.php @@ -98,7 +98,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite */ private static $index = 1; /** - * @var Doctrine_Null $null a Doctrine_Null object used for extremely fast + * @var Doctrine_Null $null a Doctrine_Null object used for extremely fast * null value testing */ private static $null; @@ -153,7 +153,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->prepareIdentifiers($exists); if( ! $exists) { - + if($count > 0) $this->state = Doctrine_Record::STATE_TDIRTY; else @@ -169,7 +169,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->state = Doctrine_Record::STATE_PROXY; } - // listen the onLoad event $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onLoad($this); } @@ -219,6 +218,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $tmp = $this->data; $this->data = array(); + + $count = 0; foreach($this->table->getColumnNames() as $name) { $type = $this->table->getTypeOf($name); @@ -239,9 +240,11 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite break; default: $this->data[$name] = $tmp[$name]; + $count++; endswitch; } } + return $count; } /** * prepares identifiers for later use @@ -973,7 +976,10 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite */ public function initReference(Doctrine_Collection $coll, Doctrine_Relation $connector) { $name = $this->table->getAlias($coll->getTable()->getComponentName()); - $coll->setReference($this, $connector); + + if( ! ($connector instanceof Doctrine_Association)) + $coll->setReference($this, $connector); + $this->references[$name] = $coll; $this->originals[$name] = clone $coll; } diff --git a/Doctrine/Table.php b/Doctrine/Table.php index 4b9253afc..971c7ba41 100644 --- a/Doctrine/Table.php +++ b/Doctrine/Table.php @@ -159,6 +159,7 @@ class Doctrine_Table extends Doctrine_Configurable { $this->primaryKeys[] = "id"; $this->identifier = "id"; $this->identifierType = Doctrine_Identifier::AUTO_INCREMENT; + $this->columnCount++; break; default: if(count($this->primaryKeys) > 1) { @@ -804,7 +805,7 @@ class Doctrine_Table extends Doctrine_Configurable { * @return integer */ final public function getColumnCount() { - return $this->columnCount; + return $this->columnCount; } /** * returns all columns and their definitions diff --git a/Doctrine/Validator.php b/Doctrine/Validator.php index 75488537b..579563fdb 100644 --- a/Doctrine/Validator.php +++ b/Doctrine/Validator.php @@ -120,11 +120,12 @@ class Doctrine_Validator { $column = $columns[$key]; - if($column[0] == 'array' || $column[0] == 'object') { - $value = serialize($value); - } + if($column[0] == 'array' || $column[0] == 'object') + $length = strlen(serialize($value)); + else + $length = strlen($value); - if(strlen($value) > $column[1]) { + if($length > $column[1]) { $err[$key] = Doctrine_Validator::ERR_LENGTH; continue; } diff --git a/Doctrine/Validator/Exception.php b/Doctrine/Validator/Exception.php index 61a4f842b..25bc88996 100644 --- a/Doctrine/Validator/Exception.php +++ b/Doctrine/Validator/Exception.php @@ -2,12 +2,32 @@ Doctrine::autoload('Doctrine_Exception'); class Doctrine_Validator_Exception extends Doctrine_Exception { + /** + * @var Doctrine_Validator $validator + */ private $validator; + /** + * @param Doctrine_Validator $validator + */ public function __construct(Doctrine_Validator $validator) { $this->validator = $validator; } + /** + * returns the error stack + * + * @return array + */ public function getErrorStack() { return $this->validator->getErrorStack(); } + /** + * __toString + * + * @return string + */ + public function __toString() { + $string = "Error stack : ".print_r($this->validator->getErrorStack(), true); + return $string.parent::__toString(); + } } ?> diff --git a/tests/QueryTestCase.php b/tests/QueryTestCase.php index 4ec5808cd..95687a8b5 100644 --- a/tests/QueryTestCase.php +++ b/tests/QueryTestCase.php @@ -14,6 +14,158 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { $this->dbh->query("DROP TABLE IF EXISTS test_entries"); parent::prepareTables(); } + //public function prepareData() { } + + public function testManyToManyFetchingWithColonOperator() { + $query = new Doctrine_Query($this->session); + + $task = new Task(); + $task->name = "T1"; + $task->ResourceAlias[0]->name = "R1"; + $task->ResourceAlias[1]->name = "R2"; + + $task = new Task(); + $task->name = "T2"; + $task->ResourceAlias[0]->name = "R3"; + $task->ResourceAlias[1]->name = "R4"; + $task->ResourceAlias[2]->name = "R5"; + $task->ResourceAlias[3]->name = "R6"; + + $this->assertEqual($task->ResourceAlias[0]->name, "R3"); + $this->assertEqual($task->ResourceAlias[1]->name, "R4"); + $this->assertEqual($task->ResourceAlias[2]->name, "R5"); + $this->assertEqual($task->ResourceAlias[3]->name, "R6"); + + $task = new Task(); + $task->name = "T3"; + $task->ResourceAlias[0]->name = "R7"; + + $task = new Task(); + $task->name = "T4"; + + $this->session->flush(); + + // clear identity maps + $task->getTable()->clear(); + $this->session->getTable('Assignment')->clear(); + $this->session->getTable('Resource')->clear(); + + $tasks[1] = $task->getTable()->find(2); + $this->assertEqual($tasks[1]->ResourceAlias[0]->name, "R3"); + $this->assertEqual($tasks[1]->ResourceAlias[1]->name, "R4"); + $this->assertEqual($tasks[1]->ResourceAlias[2]->name, "R5"); + $this->assertEqual($tasks[1]->ResourceAlias[3]->name, "R6"); + + // clear identity maps + $task->getTable()->clear(); + $this->session->getTable('Assignment')->clear(); + $this->session->getTable('Resource')->clear(); + + $query->from("Task-l:ResourceAlias-l"); + $tasks = $query->execute(); + $this->assertEqual($tasks->count(), 3); + $this->assertTrue($tasks instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[0]->ResourceAlias->count(), 2); + $this->assertTrue($tasks[0]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + + $this->assertEqual($tasks[1]->ResourceAlias->count(), 4); + $this->assertTrue($tasks[1]->ResourceAlias instanceof Doctrine_Collection_Lazy); + // sanity checking + $this->assertEqual($tasks[1]->ResourceAlias[0]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[1]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[2]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[3]->getState(), Doctrine_Record::STATE_PROXY); + + $count = count($this->dbh); + + $this->assertEqual($tasks[1]->ResourceAlias[0]->name, "R3"); + $this->assertEqual($tasks[1]->ResourceAlias[1]->name, "R4"); + $this->assertEqual($tasks[1]->ResourceAlias[2]->name, "R5"); + $this->assertEqual($tasks[1]->ResourceAlias[3]->name, "R6"); + + $this->assertEqual(count($this->dbh), ($count + 4)); + + $this->assertEqual($tasks[2]->ResourceAlias->count(), 1); + $this->assertTrue($tasks[2]->ResourceAlias instanceof Doctrine_Collection_Lazy); + } + public function testManyToManyFetchingWithDotOperator() { + $query = new Doctrine_Query($this->session); + + $this->session->getTable('Task')->clear(); + $this->session->getTable('Assignment')->clear(); + $this->session->getTable('Resource')->clear(); + + $tasks = $query->query("FROM Task-l.ResourceAlias-l"); + $this->assertEqual($tasks->count(), 4); + $this->assertTrue($tasks instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[0]->ResourceAlias->count(), 2); + $this->assertTrue($tasks[0]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[1]->ResourceAlias->count(), 4); + $this->assertTrue($tasks[1]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[1]->ResourceAlias->count(), 4); + $this->assertTrue($tasks[1]->ResourceAlias instanceof Doctrine_Collection_Lazy); + // sanity checking + $this->assertEqual($tasks[1]->ResourceAlias[0]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[1]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[2]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($tasks[1]->ResourceAlias[3]->getState(), Doctrine_Record::STATE_PROXY); + + $count = count($this->dbh); + + $this->assertEqual($tasks[1]->ResourceAlias[0]->name, "R3"); + $this->assertEqual($tasks[1]->ResourceAlias[1]->name, "R4"); + $this->assertEqual($tasks[1]->ResourceAlias[2]->name, "R5"); + $this->assertEqual($tasks[1]->ResourceAlias[3]->name, "R6"); + + $this->assertEqual(count($this->dbh), ($count + 4)); + + $this->assertEqual($tasks[2]->ResourceAlias->count(), 1); + $this->assertTrue($tasks[2]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[3]->ResourceAlias->count(), 0); + $this->assertTrue($tasks[3]->ResourceAlias instanceof Doctrine_Collection); + } + public function testManyToManyFetchingWithDotOperatorAndLoadedIdentityMaps() { + $query = new Doctrine_Query($this->session); + + $tasks = $query->query("FROM Task-l.ResourceAlias-l"); + $this->assertEqual($tasks->count(), 4); + $this->assertTrue($tasks instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[0]->ResourceAlias->count(), 2); + $this->assertTrue($tasks[0]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[1]->ResourceAlias->count(), 4); + $this->assertTrue($tasks[1]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[1]->ResourceAlias->count(), 4); + $this->assertTrue($tasks[1]->ResourceAlias instanceof Doctrine_Collection_Lazy); + // sanity checking + $this->assertEqual($tasks[1]->ResourceAlias[0]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($tasks[1]->ResourceAlias[1]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($tasks[1]->ResourceAlias[2]->getState(), Doctrine_Record::STATE_CLEAN); + $this->assertEqual($tasks[1]->ResourceAlias[3]->getState(), Doctrine_Record::STATE_CLEAN); + + $count = count($this->dbh); + + $this->assertEqual($tasks[1]->ResourceAlias[0]->name, "R3"); + $this->assertEqual($tasks[1]->ResourceAlias[1]->name, "R4"); + $this->assertEqual($tasks[1]->ResourceAlias[2]->name, "R5"); + $this->assertEqual($tasks[1]->ResourceAlias[3]->name, "R6"); + + $this->assertEqual(count($this->dbh), $count); + + $this->assertEqual($tasks[2]->ResourceAlias->count(), 1); + $this->assertTrue($tasks[2]->ResourceAlias instanceof Doctrine_Collection_Lazy); + + $this->assertEqual($tasks[3]->ResourceAlias->count(), 0); + $this->assertTrue($tasks[3]->ResourceAlias instanceof Doctrine_Collection); + } public function testOneToOneSharedRelations() { $status = new Log_Status(); $status->name = 'success'; @@ -330,7 +482,6 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { $this->assertTrue(is_numeric($users[2]->email_id)); $this->assertEqual($count + 1, count($this->dbh)); } - public function testQueryWithComplexAliases() { $q = new Doctrine_Query($this->session); @@ -373,7 +524,7 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { $this->assertEqual($board->Category->getState(), Doctrine_Record::STATE_CLEAN); $this->assertEqual($board->Threads[0]->getState(), Doctrine_Record::STATE_CLEAN); $this->assertTrue($board->Threads[0] instanceof Forum_Thread); - + $this->assertEqual($board->Threads[0]->Entries->count(), 2); $q->from("Forum_Board"); @@ -393,21 +544,25 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { $q->from("Forum_Board-l.Threads-l"); $this->assertEqual($q->getQuery(), "SELECT forum_board.id AS Forum_Board__id, forum_thread.id AS Forum_Thread__id FROM forum_board LEFT JOIN forum_thread ON forum_board.id = forum_thread.board_id"); - $q->from("Forum_Board.Threads.Entries-l"); + //$this->session->clear(); + + $q->from("Forum_Board-l.Threads-l.Entries-l"); $this->assertEqual($q->getQuery(), "SELECT forum_board.id AS Forum_Board__id, forum_thread.id AS Forum_Thread__id, forum_entry.id AS Forum_Entry__id FROM forum_board LEFT JOIN forum_thread ON forum_board.id = forum_thread.board_id LEFT JOIN forum_entry ON forum_thread.id = forum_entry.thread_id"); $boards = $q->execute(); $this->assertEqual($boards->count(), 1); $count = count($this->dbh); $this->assertEqual($boards[0]->Threads->count(), 1); $this->assertEqual(count($this->dbh), $count); - $this->assertEqual($boards[0]->Threads[0]->Entries->count(), 1); - + $this->assertEqual($boards[0]->Threads[0]->Entries->count(), 2); + + $q->from("Forum_Board-l.Threads-l.Entries-i"); $this->assertEqual($boards->count(), 1); $count = count($this->dbh); $this->assertEqual($boards[0]->Threads->count(), 1); $this->assertEqual(count($this->dbh), $count); - $this->assertEqual($boards[0]->Threads[0]->Entries->count(), 1); + $this->assertEqual($boards[0]->Threads[0]->Entries->count(), 2); + } public function testQueryWithAliases() { @@ -693,5 +848,6 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase { } + } ?> diff --git a/tests/run.php b/tests/run.php index bc486415e..508391713 100644 --- a/tests/run.php +++ b/tests/run.php @@ -46,7 +46,7 @@ $test->addTestCase(new Doctrine_CollectionTestCase()); $test->addTestCase(new Doctrine_QueryTestCase()); -$test->addTestCase(new Doctrine_PessimisticLockingTestCase()); +//$test->addTestCase(new Doctrine_PessimisticLockingTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase()); //$test->addTestCase(new Doctrine_Cache_SqliteTestCase());