DQL : Multiple join bug fixed
DQL : Changed cartesian product fetching to inner join fetching
This commit is contained in:
parent
fcf706182d
commit
47e1cf515e
@ -22,6 +22,8 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
private $collections = array();
|
||||
|
||||
private $joined = array();
|
||||
|
||||
private $joins = array();
|
||||
/**
|
||||
* @var array $data fetched data
|
||||
*/
|
||||
@ -101,6 +103,7 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
$this->connectors = array();
|
||||
$this->collections = array();
|
||||
$this->joined = array();
|
||||
$this->joins = array();
|
||||
}
|
||||
/**
|
||||
* loadFields
|
||||
@ -167,6 +170,9 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
|
||||
$this->parts[$name] = $args[0];
|
||||
break;
|
||||
case "from":
|
||||
$this->parts['columns'] = array();
|
||||
$this->joins = array();
|
||||
default:
|
||||
$this->parts[$name] = array();
|
||||
$this->$method($args[0]);
|
||||
@ -209,6 +215,9 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
|
||||
$this->parts[$name] = $value;
|
||||
break;
|
||||
case "from":
|
||||
$this->parts['columns'] = array();
|
||||
$this->joins = array();
|
||||
default:
|
||||
$this->parts[$name] = array();
|
||||
$this->$method($value);
|
||||
@ -230,14 +239,15 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
// build the basic query
|
||||
$q = "SELECT ".implode(", ",$this->parts["columns"]).
|
||||
" FROM ";
|
||||
|
||||
foreach($this->parts["from"] as $tname => $bool) {
|
||||
$str = $tname;
|
||||
if(isset($this->parts["join"][$tname]))
|
||||
$str .= " ".$this->parts["join"][$tname];
|
||||
|
||||
$a[] = $str;
|
||||
$a[] = $tname;
|
||||
}
|
||||
$q .= implode(", ",$a);
|
||||
|
||||
if( ! empty($this->parts['join']))
|
||||
$q .= " ".implode(' ', $this->parts["join"]);
|
||||
|
||||
$this->applyInheritance();
|
||||
if( ! empty($this->parts["where"]))
|
||||
$q .= " WHERE ".implode(" ",$this->parts["where"]);
|
||||
@ -345,7 +355,7 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
final public function getData($key) {
|
||||
if(isset($this->data[$key]) && is_array($this->data[$key]))
|
||||
return $this->data[$key];
|
||||
|
||||
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
@ -395,17 +405,18 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
|
||||
$previd = array();
|
||||
|
||||
$coll = $this->getCollection($root);
|
||||
$coll = $this->getCollection($root);
|
||||
$prev[$root] = $coll;
|
||||
|
||||
$array = $this->parseData($stmt);
|
||||
|
||||
$colls = array();
|
||||
|
||||
foreach($array as $data):
|
||||
|
||||
foreach($array as $data) {
|
||||
/**
|
||||
* remove duplicated data rows and map data into objects
|
||||
*/
|
||||
foreach($data as $key => $row):
|
||||
foreach($data as $key => $row) {
|
||||
if(empty($row))
|
||||
continue;
|
||||
|
||||
@ -416,24 +427,31 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
|
||||
|
||||
if($previd[$name] !== $row) {
|
||||
// set internal data
|
||||
$this->tables[$name]->setData($row);
|
||||
|
||||
// initialize a new record
|
||||
$record = $this->tables[$name]->getRecord();
|
||||
|
||||
if($name == $root) {
|
||||
// add record into root collection
|
||||
$coll->add($record);
|
||||
} else {
|
||||
$last = $coll->getLast();
|
||||
|
||||
if( ! $last->hasReference($name))
|
||||
$last->initReference($this->getCollection($name),$this->connectors[$name]);
|
||||
$pointer = $this->joins[$name];
|
||||
|
||||
$last = $prev[$pointer]->getLast();
|
||||
|
||||
if( ! $last->hasReference($name)) {
|
||||
$prev[$name] = $this->getCollection($name);
|
||||
$last->initReference($prev[$name],$this->connectors[$name]);
|
||||
}
|
||||
$last->addReference($record);
|
||||
}
|
||||
}
|
||||
|
||||
$previd[$name] = $row;
|
||||
endforeach;
|
||||
endforeach;
|
||||
}
|
||||
}
|
||||
|
||||
return $coll;
|
||||
endswitch;
|
||||
@ -866,18 +884,20 @@ class Doctrine_Query extends Doctrine_Access {
|
||||
switch($fk->getType()):
|
||||
case Doctrine_Relation::ONE_AGGREGATE:
|
||||
case Doctrine_Relation::ONE_COMPOSITE:
|
||||
|
||||
$this->parts["where"][] = "(".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign().")";
|
||||
$this->parts["from"][$tname] = true;
|
||||
$this->parts["from"][$tname2] = true;
|
||||
//$this->parts["where"][] = "(".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign().")";
|
||||
//$this->parts["from"][$tname] = true;
|
||||
//$this->parts["from"][$tname2] = true;
|
||||
$this->parts["join"][$tname] = "INNER JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
|
||||
break;
|
||||
case Doctrine_Relation::MANY_AGGREGATE:
|
||||
case Doctrine_Relation::MANY_COMPOSITE:
|
||||
$this->parts["join"][$tname] = "LEFT JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
|
||||
|
||||
$this->joined[] = $tname2;
|
||||
$this->parts["from"][$tname] = true;
|
||||
break;
|
||||
endswitch;
|
||||
$c = $objTable->getComponentName();
|
||||
$this->joins[$name] = $c;
|
||||
} elseif($fk instanceof Doctrine_Association) {
|
||||
$asf = $fk->getAssociationFactory();
|
||||
|
||||
|
@ -205,7 +205,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
case Doctrine_Identifier::SEQUENCE:
|
||||
if($exists) {
|
||||
$name = $this->table->getIdentifier();
|
||||
|
||||
|
||||
if(isset($this->data[$name]))
|
||||
$this->id = $this->data[$name];
|
||||
|
||||
@ -246,14 +246,16 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
$this->data[$k] = array();
|
||||
}
|
||||
return array_keys(get_object_vars($this));
|
||||
|
||||
}
|
||||
/**
|
||||
* __wakeup
|
||||
* unseralize
|
||||
* this method is automatically called everytime a Doctrine_Record object is unserialized
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __wakeup() {
|
||||
|
||||
$this->modified = array();
|
||||
$this->state = Doctrine_Record::STATE_CLEAN;
|
||||
|
||||
@ -301,21 +303,27 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
}
|
||||
/**
|
||||
* hasCollections
|
||||
* @return boolean whether or not this dao is part of a collection
|
||||
* whether or not this record is part of a collection
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
final public function hasCollections() {
|
||||
return (! empty($this->collections));
|
||||
}
|
||||
/**
|
||||
* getState
|
||||
* returns the current state of the object
|
||||
*
|
||||
* @see Doctrine_Record::STATE_* constants
|
||||
* @return integer the current state
|
||||
* @return integer
|
||||
*/
|
||||
final public function getState() {
|
||||
return $this->state;
|
||||
}
|
||||
/**
|
||||
* refresh refresh internal data from the database
|
||||
* refresh
|
||||
* refresh internal data from the database
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
final public function refresh() {
|
||||
@ -377,7 +385,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite
|
||||
}
|
||||
/**
|
||||
* get
|
||||
* returns a value of a property or related component
|
||||
* returns a value of a property or a related component
|
||||
*
|
||||
* @param $name name of the property or related component
|
||||
* @throws InvalidKeyException
|
||||
|
@ -32,13 +32,15 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
|
||||
$thread = $board->Threads[0];
|
||||
$thread->Entries[0]->topic = "My first topic";
|
||||
$thread->Entries[1]->topic = "My second topic";
|
||||
$this->assertEqual($thread->Entries[0]->topic, "My first topic");
|
||||
$this->assertEqual($thread->Entries[0]->getState(), Doctrine_Record::STATE_TDIRTY);
|
||||
$this->assertTrue($thread->Entries[0] instanceof Forum_Entry);
|
||||
|
||||
$this->session->flush();
|
||||
|
||||
$q = new Doctrine_Query($this->session);
|
||||
$board->getTable()->clear();
|
||||
|
||||
$board = $board->getTable()->find($board->getID());
|
||||
$this->assertEqual($board->Threads->count(), 1);
|
||||
$this->assertEqual($board->name, "Doctrine Forum");
|
||||
@ -48,7 +50,7 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
$this->assertTrue($board->Threads[0] instanceof Forum_Thread);
|
||||
|
||||
|
||||
$q = new Doctrine_Query($this->session);
|
||||
|
||||
$q->from("Forum_Board");
|
||||
$coll = $q->execute();
|
||||
$this->assertEqual($coll->count(), 1);
|
||||
@ -56,6 +58,20 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
$q->from("Forum_Board, Forum_Board.Threads");
|
||||
$coll = $q->execute();
|
||||
$this->assertEqual($coll->count(), 1);
|
||||
|
||||
|
||||
$q->from("Forum_Board-l, Forum_Board.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-l, Forum_Board.Threads-l, Forum_Board.Threads.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(count($this->dbh), $count);
|
||||
}
|
||||
|
||||
public function testQueryWithAliases() {
|
||||
@ -141,9 +157,10 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
}
|
||||
public function testOrderBy() {
|
||||
$query = new Doctrine_Query($this->session);
|
||||
$users = $query->query("FROM User-b ORDER BY User.name ASC, User.Email.address");
|
||||
$query->from("User-b")->orderby("User.name ASC, User.Email.address");
|
||||
$users = $query->execute();
|
||||
$this->assertEqual(trim($query->getQuery()),
|
||||
"SELECT entity.id AS User__id FROM entity, email WHERE (entity.email_id = email.id) AND (entity.type = 0) ORDER BY entity.name ASC, email.address");
|
||||
"SELECT entity.id AS User__id FROM entity INNER JOIN email ON entity.email_id = email.id WHERE (entity.type = 0) ORDER BY entity.name ASC, email.address");
|
||||
$this->assertEqual($users->count(),8);
|
||||
$this->assertTrue($users[0]->name == "Arnold Schwarzenegger");
|
||||
}
|
||||
@ -286,11 +303,12 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
|
||||
$users = $query->query("FROM User-b, User.Email-b");
|
||||
$this->assertEqual(trim($query->getQuery()),
|
||||
"SELECT entity.id AS User__id, email.id AS Email__id FROM entity, email WHERE (entity.email_id = email.id) AND (entity.type = 0)");
|
||||
"SELECT entity.id AS User__id, email.id AS Email__id FROM entity INNER JOIN email ON entity.email_id = email.id WHERE (entity.type = 0)");
|
||||
|
||||
$this->assertEqual($users->count(),8);
|
||||
|
||||
$users = $query->query("FROM Email-b WHERE Email.address LIKE '%@example%'");
|
||||
|
||||
$this->assertEqual($query->getQuery(),
|
||||
"SELECT email.id AS Email__id FROM email WHERE (email.address LIKE '%@example%')");
|
||||
$this->assertEqual($users->count(),8);
|
||||
@ -323,7 +341,7 @@ class Doctrine_QueryTestCase extends Doctrine_UnitTestCase {
|
||||
$this->assertTrue(is_array($values));
|
||||
$this->assertTrue(isset($values['users']));
|
||||
$this->assertTrue(isset($values['max']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
@ -52,10 +52,8 @@ class Doctrine_UnitTestCase extends UnitTestCase {
|
||||
$this->prepareTables();
|
||||
$this->prepareData();
|
||||
}
|
||||
public function prepareTables() {
|
||||
|
||||
public function prepareTables() {
|
||||
foreach($this->tables as $name) {
|
||||
|
||||
$this->dbh->query("DROP TABLE IF EXISTS ".strtolower($name));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user