diff --git a/Doctrine/Cache/Query/Sqlite.php b/Doctrine/Cache/Query/Sqlite.php index 8571cb074..db0c9c398 100644 --- a/Doctrine/Cache/Query/Sqlite.php +++ b/Doctrine/Cache/Query/Sqlite.php @@ -12,10 +12,6 @@ class Doctrine_Cache_Query_Sqlite implements Countable { * @var PDO $dbh database handler */ private $dbh; - /** - * @var array $fetched an array of fetched primary keys - */ - private $fetched = array(); /** * constructor * diff --git a/Doctrine/Collection.php b/Doctrine/Collection.php index 3d2442c37..a12627f17 100644 --- a/Doctrine/Collection.php +++ b/Doctrine/Collection.php @@ -434,7 +434,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator * @param Doctrine_Query $query * @param integer $key */ - public function populate(Doctrine_Query $query) { + public function populate(Doctrine_Hydrate $query) { $name = $this->table->getComponentName(); if($this instanceof Doctrine_Collection_Immediate || diff --git a/Doctrine/Form.php b/Doctrine/Form.php index 464e23a80..6f527a644 100644 --- a/Doctrine/Form.php +++ b/Doctrine/Form.php @@ -47,7 +47,7 @@ class Doctrine_Form implements Iterator { } elseif($length <= 4000) { $elements[$column] = "\n"; } else { - $elements[$column] = "\n"; + $elements[$column] = "\n"; } } return $elements[$column]; diff --git a/Doctrine/Hydrate.php b/Doctrine/Hydrate.php new file mode 100644 index 000000000..9a6085986 --- /dev/null +++ b/Doctrine/Hydrate.php @@ -0,0 +1,429 @@ + array(), + "from" => array(), + "join" => array(), + "where" => array(), + "groupby" => array(), + "having" => array(), + "orderby" => array(), + "limit" => false, + "offset" => false, + ); + /** + * constructor + * + * @param Doctrine_Session $session + */ + public function __construct(Doctrine_Session $session) { + $this->session = $session; + } + /** + * clear + * resets all the variables + * + * @return void + */ + protected function clear() { + $this->fetchModes = array(); + $this->tables = array(); + $this->parts = array( + "select" => array(), + "from" => array(), + "join" => array(), + "where" => array(), + "groupby" => array(), + "having" => array(), + "orderby" => array(), + "limit" => false, + "offset" => false, + ); + $this->inheritanceApplied = false; + $this->aggregate = false; + $this->data = array(); + $this->collections = array(); + $this->joins = array(); + $this->tableIndexes = array(); + $this->tableAliases = array(); + } + /** + * @return Doctrine_Session + */ + public function getSession() { + return $this->session; + } + /** + * setView + * sets a database view this query object uses + * this method should only be called internally by doctrine + * + * @param Doctrine_View $view database view + * @return void + */ + public function setView(Doctrine_View $view) { + $this->view = $view; + } + /** + * getView + * + * @return Doctrine_View + */ + public function getView() { + return $this->view; + } + /** + * getTableAlias + * + * @param string $path + * @return string + */ + final public function getTableAlias($path) { + if( ! isset($this->tableAliases[$path])) + return false; + + return $this->tableAliases[$path]; + } + /** + * getCollection + * + * @parma string $name component name + * @param integer $index + */ + private function getCollection($name) { + $table = $this->tables[$name]; + switch($this->fetchModes[$name]): + case Doctrine::FETCH_BATCH: + $coll = new Doctrine_Collection_Batch($table); + break; + case Doctrine::FETCH_LAZY: + $coll = new Doctrine_Collection_Lazy($table); + break; + case Doctrine::FETCH_OFFSET: + $coll = new Doctrine_Collection_Offset($table); + break; + case Doctrine::FETCH_IMMEDIATE: + $coll = new Doctrine_Collection_Immediate($table); + break; + case Doctrine::FETCH_LAZY_OFFSET: + $coll = new Doctrine_Collection_LazyOffset($table); + break; + default: + throw new Doctrine_Exception("Unknown fetchmode"); + endswitch; + + $coll->populate($this); + return $coll; + } + /** + * getData + * @param $key the component name + * @return array the data row for the specified component + */ + final public function getData($key) { + if(isset($this->data[$key]) && is_array($this->data[$key])) + return $this->data[$key]; + + return array(); + } + /** + * execute + * executes the dql query and populates all collections + * + * @param string $params + * @return Doctrine_Collection the root collection + */ + public function execute($params = array()) { + $this->data = array(); + $this->collections = array(); + + if( ! $this->view) + $query = $this->getQuery(); + else + $query = $this->view->getSelectSql(); + + switch(count($this->tables)): + case 0: + throw new Doctrine_Exception("No tables selected"); + break; + case 1: + $keys = array_keys($this->tables); + + $name = $this->tables[$keys[0]]->getComponentName(); + $stmt = $this->session->execute($query,$params); + + while($data = $stmt->fetch(PDO::FETCH_ASSOC)): + foreach($data as $key => $value): + $e = explode("__",$key); + if(count($e) > 1) { + $data[$e[1]] = $value; + } else { + $data[$e[0]] = $value; + } + unset($data[$key]); + endforeach; + $this->data[$name][] = $data; + endwhile; + + return $this->getCollection($keys[0]); + break; + default: + $keys = array_keys($this->tables); + $root = $keys[0]; + + $stmt = $this->session->execute($query,$params); + + $previd = array(); + + $coll = $this->getCollection($root); + $prev[$root] = $coll; + + $array = $this->parseData($stmt); + + + $colls = array(); + + foreach($array as $data) { + /** + * remove duplicated data rows and map data into objects + */ + foreach($data as $key => $row) { + if(empty($row)) + continue; + + + $ids = $this->tables[$key]->getIdentifier(); + + $emptyID = false; + if(is_array($ids)) { + foreach($ids as $id) { + if($row[$id] == null) { + $emptyID = true; + break; + } + } + } else { + if( ! isset($row[$ids])) + $emptyID = true; + } + + + $name = $key; + + if($emptyID) { + + + $pointer = $this->joins[$name]; + $path = array_search($name, $this->tableAliases); + $tmp = explode(".", $path); + $alias = end($tmp); + unset($tmp); + $fk = $this->tables[$pointer]->getForeignKey($alias); + + if( ! isset($prev[$pointer]) ) + continue; + + $last = $prev[$pointer]->getLast(); + + switch($fk->getType()): + case Doctrine_Relation::ONE_COMPOSITE: + case Doctrine_Relation::ONE_AGGREGATE: + + break; + default: + if($last instanceof Doctrine_Record) { + if( ! $last->hasReference($alias)) { + $prev[$name] = $this->getCollection($name); + $last->initReference($prev[$name],$fk); + } + } + endswitch; + + continue; + } + + + if( ! isset($previd[$name])) + $previd[$name] = array(); + + 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); + unset($previd); + + } else { + + $pointer = $this->joins[$name]; + $path = array_search($name, $this->tableAliases); + $tmp = explode(".", $path); + $alias = end($tmp); + unset($tmp); + $fk = $this->tables[$pointer]->getForeignKey($alias); + $last = $prev[$pointer]->getLast(); + + switch($fk->getType()): + case Doctrine_Relation::ONE_COMPOSITE: + case Doctrine_Relation::ONE_AGGREGATE: + + // one-to-one relation + + $last->internalSet($fk->getLocal(), $record->getIncremented()); + + $last->initSingleReference($record, $fk); + + $prev[$name] = $record; + break; + default: + + // one-to-many relation or many-to-many relation + + if( ! $last->hasReference($alias)) { + $prev[$name] = $this->getCollection($name); + $last->initReference($prev[$name], $fk); + } else { + // previous entry found from identityMap + $prev[$name] = $last->get($alias); + } + + $last->addReference($record, $fk); + endswitch; + } + } + + $previd[$name] = $row; + } + } + + return $coll; + endswitch; + } + /** + * applyInheritance + * applies column aggregation inheritance to DQL query + * + * @return string + */ + final public function applyInheritance() { + // get the inheritance maps + $array = array(); + + foreach($this->tables as $alias => $table): + $array[$alias][] = $table->getInheritanceMap(); + endforeach; + + // apply inheritance maps + $str = ""; + $c = array(); + + $index = 0; + foreach($array as $tname => $maps) { + $a = array(); + foreach($maps as $map) { + $b = array(); + foreach($map as $field => $value) { + if($index > 0) + $b[] = "(".$tname.".$field = $value OR $tname.$field IS NULL)"; + else + $b[] = $tname.".$field = $value"; + } + if( ! empty($b)) $a[] = implode(" AND ",$b); + } + if( ! empty($a)) $c[] = implode(" AND ",$a); + $index++; + } + + $str .= implode(" AND ",$c); + + return $str; + } + /** + * parseData + * parses the data returned by PDOStatement + * + * @return array + */ + public function parseData(PDOStatement $stmt) { + $array = array(); + + while($data = $stmt->fetch(PDO::FETCH_ASSOC)): + /** + * parse the data into two-dimensional array + */ + foreach($data as $key => $value): + $e = explode("__",$key); + + if(count($e) > 1) { + $data[$e[0]][$e[1]] = $value; + } else { + $data[0][$e[0]] = $value; + } + unset($data[$key]); + endforeach; + $array[] = $data; + endwhile; + $stmt->closeCursor(); + return $array; + } + /** + * returns a Doctrine_Table for given name + * + * @param string $name component name + * @return Doctrine_Table + */ + public function getTable($name) { + return $this->tables[$name]; + } +} +?> diff --git a/Doctrine/Query.php b/Doctrine/Query.php index 013110f36..61a2b4f06 100644 --- a/Doctrine/Query.php +++ b/Doctrine/Query.php @@ -8,129 +8,8 @@ require_once("Access.php"); * @license LGPL * @version 1.0 alpha */ -class Doctrine_Query extends Doctrine_Access { - /** - * @var array $fetchmodes an array containing all fetchmodes - */ - private $fetchModes = array(); - /** - * @var array $tables an array containing all the tables used in the query - */ - private $tables = array(); - /** - * @var array $collections an array containing all collections this parser has created/will create - */ - private $collections = array(); - /** - * @var array $joins an array containing all table joins - */ - private $joins = array(); - /** - * @var array $data fetched data - */ - private $data = array(); - /** - * @var Doctrine_Session $session Doctrine_Session object - */ - private $session; - /** - * @var Doctrine_View $view Doctrine_View object - */ - private $view; - +class Doctrine_Query extends Doctrine_Hydrate { - private $inheritanceApplied = false; - - private $aggregate = false; - /** - * @var array $tableAliases - */ - private $tableAliases = array(); - /** - * @var array $tableIndexes - */ - private $tableIndexes = array(); - /** - * @var array $paths - */ - private $paths = array(); - /** - * @var array $parts SQL query string parts - */ - protected $parts = array( - "columns" => array(), - "from" => array(), - "join" => array(), - "where" => array(), - "groupby" => array(), - "having" => array(), - "orderby" => array(), - "limit" => false, - "offset" => false, - ); - /** - * constructor - * - * @param Doctrine_Session $session - */ - public function __construct(Doctrine_Session $session) { - $this->session = $session; - } - /** - * @return Doctrine_Session - */ - public function getSession() { - return $this->session; - } - /** - * setView - * sets a database view this query object uses - * this method should only be called internally by doctrine - * - * @param Doctrine_View $view database view - * @return void - */ - public function setView(Doctrine_View $view) { - $this->view = $view; - } - /** - * getView - * - * @return Doctrine_View - */ - public function getView() { - return $this->view; - } - - /** - * clear - * resets all the variables - * - * @return void - */ - private function clear() { - $this->fetchModes = array(); - $this->tables = array(); - - $this->parts = array( - "columns" => array(), - "from" => array(), - "join" => array(), - "where" => array(), - "groupby" => array(), - "having" => array(), - "orderby" => array(), - "limit" => false, - "offset" => false, - ); - $this->inheritanceApplied = false; - $this->aggregate = false; - $this->data = array(); - $this->collections = array(); - $this->joins = array(); - $this->tableIndexes = array(); - $this->tableAliases = array(); - } /** * loadFields * loads fields for a given table and @@ -144,7 +23,7 @@ class Doctrine_Query extends Doctrine_Access { * @param array $names fields to be loaded (only used in lazy property loading) * @return void */ - private function loadFields(Doctrine_Table $table, $fetchmode, array $names, $cpath) { + protected function loadFields(Doctrine_Table $table, $fetchmode, array $names, $cpath) { $name = $table->getComponentName(); switch($fetchmode): @@ -175,9 +54,9 @@ class Doctrine_Query extends Doctrine_Access { foreach($names as $name) { if($count == 0) { - $this->parts["columns"][] = $tablename.".".$name; + $this->parts["select"][] = $tablename.".".$name; } else { - $this->parts["columns"][] = $tablename.".".$name." AS ".$tablename."__".$name; + $this->parts["select"][] = $tablename.".".$name." AS ".$tablename."__".$name; } } } @@ -216,7 +95,7 @@ class Doctrine_Query extends Doctrine_Access { switch($name): case "from": $this->parts['from'] = array(); - $this->parts['columns'] = array(); + $this->parts['select'] = array(); $this->parts['join'] = array(); $this->joins = array(); $this->tables = array(); @@ -290,7 +169,7 @@ class Doctrine_Query extends Doctrine_Access { $this->parts[$name] = $value; break; case "from": - $this->parts['columns'] = array(); + $this->parts['select'] = array(); $this->parts['join'] = array(); $this->joins = array(); $this->tables = array(); @@ -312,11 +191,11 @@ class Doctrine_Query extends Doctrine_Access { * @return string */ final public function getQuery() { - if(empty($this->parts["columns"]) || empty($this->parts["from"])) + if(empty($this->parts["select"]) || empty($this->parts["from"])) return false; // build the basic query - $q = "SELECT ".implode(", ",$this->parts["columns"]). + $q = "SELECT ".implode(", ",$this->parts["select"]). " FROM "; foreach($this->parts["from"] as $tname => $bool) { @@ -352,303 +231,8 @@ class Doctrine_Query extends Doctrine_Access { return $q; } - /** - * applyInheritance - * applies column aggregation inheritance to DQL query - * - * @return string - */ - final public function applyInheritance() { - // get the inheritance maps - $array = array(); - - foreach($this->tables as $alias => $table): - $array[$alias][] = $table->getInheritanceMap(); - endforeach; - - // apply inheritance maps - $str = ""; - $c = array(); - - $index = 0; - foreach($array as $tname => $maps) { - $a = array(); - foreach($maps as $map) { - $b = array(); - foreach($map as $field => $value) { - if($index > 0) - $b[] = "(".$tname.".$field = $value OR $tname.$field IS NULL)"; - else - $b[] = $tname.".$field = $value"; - } - if( ! empty($b)) $a[] = implode(" AND ",$b); - } - if( ! empty($a)) $c[] = implode(" AND ",$a); - $index++; - } - - $str .= implode(" AND ",$c); - - return $str; - } - /** - * getData - * @param $key the component name - * @return array the data row for the specified component - */ - final public function getData($key) { - if(isset($this->data[$key]) && is_array($this->data[$key])) - return $this->data[$key]; - - return array(); - } - /** - * execute - * executes the dql query and populates all collections - * - * @param string $params - * @return Doctrine_Collection the root collection - */ - public function execute($params = array()) { - $this->data = array(); - $this->collections = array(); - - if( ! $this->view) - $query = $this->getQuery(); - else - $query = $this->view->getSelectSql(); - - switch(count($this->tables)): - case 0: - throw new DQLException(); - break; - case 1: - $keys = array_keys($this->tables); - - $name = $this->tables[$keys[0]]->getComponentName(); - $stmt = $this->session->execute($query,$params); - - while($data = $stmt->fetch(PDO::FETCH_ASSOC)): - foreach($data as $key => $value): - $e = explode("__",$key); - if(count($e) > 1) { - $data[$e[1]] = $value; - } else { - $data[$e[0]] = $value; - } - unset($data[$key]); - endforeach; - $this->data[$name][] = $data; - endwhile; - - return $this->getCollection($keys[0]); - break; - default: - $keys = array_keys($this->tables); - $root = $keys[0]; - - $stmt = $this->session->execute($query,$params); - - $previd = array(); - - $coll = $this->getCollection($root); - $prev[$root] = $coll; - - $array = $this->parseData($stmt); - $colls = array(); - - foreach($array as $data) { - /** - * remove duplicated data rows and map data into objects - */ - foreach($data as $key => $row) { - if(empty($row)) - continue; - - - $ids = $this->tables[$key]->getIdentifier(); - - $emptyID = false; - if(is_array($ids)) { - foreach($ids as $id) { - if($row[$id] == null) { - $emptyID = true; - break; - } - } - } else { - if($row[$ids] === null) - $emptyID = true; - } - - - $name = $key; - - if($emptyID) { - - - $pointer = $this->joins[$name]; - $path = array_search($name, $this->tableAliases); - $tmp = explode(".", $path); - $alias = end($tmp); - unset($tmp); - $fk = $this->tables[$pointer]->getForeignKey($alias); - - if( ! isset($prev[$pointer]) ) - continue; - - $last = $prev[$pointer]->getLast(); - - switch($fk->getType()): - case Doctrine_Relation::ONE_COMPOSITE: - case Doctrine_Relation::ONE_AGGREGATE: - - break; - default: - if($last instanceof Doctrine_Record) { - if( ! $last->hasReference($alias)) { - $prev[$name] = $this->getCollection($name); - $last->initReference($prev[$name],$fk); - } - } - endswitch; - - continue; - } - - - if( ! isset($previd[$name])) - $previd[$name] = array(); - - 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); - unset($previd); - - } else { - - $pointer = $this->joins[$name]; - $path = array_search($name, $this->tableAliases); - $tmp = explode(".", $path); - $alias = end($tmp); - unset($tmp); - $fk = $this->tables[$pointer]->getForeignKey($alias); - $last = $prev[$pointer]->getLast(); - - switch($fk->getType()): - case Doctrine_Relation::ONE_COMPOSITE: - case Doctrine_Relation::ONE_AGGREGATE: - - // one-to-one relation - - $last->internalSet($fk->getLocal(), $record->getIncremented()); - - $last->initSingleReference($record, $fk); - - $prev[$name] = $record; - break; - default: - - // one-to-many relation or many-to-many relation - - if( ! $last->hasReference($alias)) { - $prev[$name] = $this->getCollection($name); - $last->initReference($prev[$name], $fk); - } else { - // previous entry found from identityMap - $prev[$name] = $last->get($alias); - } - - $last->addReference($record, $fk); - endswitch; - } - } - - $previd[$name] = $row; - } - } - - return $coll; - endswitch; - } - /** - * parseData - * parses the data returned by PDOStatement - * - * @return array - */ - public function parseData(PDOStatement $stmt) { - $array = array(); - - while($data = $stmt->fetch(PDO::FETCH_ASSOC)): - /** - * parse the data into two-dimensional array - */ - foreach($data as $key => $value): - $e = explode("__",$key); - - if(count($e) > 1) { - $data[$e[0]][$e[1]] = $value; - } else { - $data[0][$e[0]] = $value; - } - unset($data[$key]); - endforeach; - $array[] = $data; - endwhile; - $stmt->closeCursor(); - return $array; - } - /** - * returns a Doctrine_Table for given name - * - * @param string $name component name - * @return Doctrine_Table - */ - public function getTable($name) { - return $this->tables[$name]; - } - /** - * getCollection - * - * @parma string $name component name - * @param integer $index - */ - private function getCollection($name) { - $table = $this->tables[$name]; - switch($this->fetchModes[$name]): - case Doctrine::FETCH_BATCH: - $coll = new Doctrine_Collection_Batch($table); - break; - case Doctrine::FETCH_LAZY: - $coll = new Doctrine_Collection_Lazy($table); - break; - case Doctrine::FETCH_OFFSET: - $coll = new Doctrine_Collection_Offset($table); - break; - case Doctrine::FETCH_IMMEDIATE: - $coll = new Doctrine_Collection_Immediate($table); - break; - case Doctrine::FETCH_LAZY_OFFSET: - $coll = new Doctrine_Collection_LazyOffset($table); - break; - default: - throw new Doctrine_Exception("Unknown fetchmode"); - endswitch; - - $coll->populate($this); - return $coll; - } /** * query the database with DQL (Doctrine Query Language) * @@ -681,7 +265,7 @@ class Doctrine_Query extends Doctrine_Access { * @param string $query DQL query * @return void */ - final public function parseQuery($query) { + public function parseQuery($query) { $this->clear(); $e = self::bracketExplode($query," ","(",")"); @@ -850,18 +434,7 @@ class Doctrine_Query extends Doctrine_Access { return $tableName; } } - /** - * getTableAlias - * - * @param string $path - * @return string - */ - final public function getTableAlias($path) { - if( ! isset($this->tableAliases[$path])) - return false; - return $this->tableAliases[$path]; - } /** * loads a component * diff --git a/Doctrine/RawSql.php b/Doctrine/RawSql.php new file mode 100644 index 000000000..3c9204a26 --- /dev/null +++ b/Doctrine/RawSql.php @@ -0,0 +1,178 @@ +parts[$name])) + throw new Doctrine_Exception("Unknown overload method"); + + if($name == 'select') { + preg_match_all('/{([^}{]*)}/U', $args[0], $m); + + $this->fields = $m[1]; + $this->parts["select"] = array(); + } else + $this->parts[$name][] = $args[0]; + + return $this; + } + + /** + * parseQuery + * + * @param string $query + * @return void + */ + public function parseQuery($query) { + preg_match_all('/{([^}{]*)}/U', $query, $m); + + $this->fields = $m[1]; + $this->clear(); + + $e = explode(" ", $query); + + foreach($e as $k => $part): + $low = strtolower($part); + switch(strtolower($part)): + case "select": + case "from": + case "where": + case "limit": + case "offset": + case "having": + $p = $low; + $parts[$low] = array(); + break; + case "order": + case "group": + $i = ($k + 1); + if(isset($e[$i]) && strtolower($e[$i]) === "by") { + $p = $part; + $parts[$low] = array(); + } else + $parts[$p][] = $part; + break; + case "by": + continue; + default: + $parts[$p][] = $part; + endswitch; + endforeach; + + $this->parts = $parts; + $this->parts["select"] = array(); + } + /** + * getQuery + * + * + * @return string + */ + public function getQuery() { + $q = array(); + + foreach($this->fields as $field) { + $e = explode(".", $field); + if( ! isset($e[1])) + throw new Doctrine_Exception("All selected fields in Sql query must be in format tableAlias.fieldName"); + + if( ! isset($this->tables[$e[0]])) { + try { + $this->addComponent($e[0], ucwords($e[0])); + } catch(Doctrine_Exception $e) { + throw new Doctrine_Exception("The associated component for tablealias $e[0] couldn't be found."); + } + } + + if($e[1] == '*') { + foreach($this->tables[$e[0]]->getColumnNames() as $name) { + $field = $e[0].".".$name; + $this->parts["select"][$field] = $field." AS ".$e[0]."__".$name; + } + } else { + $field = $e[0].".".$e[1]; + $this->parts["select"][$field] = $field." AS ".$e[0]."__".$e[1]; + } + } + + // force-add all primary key fields + + foreach($this->tableAliases as $alias) { + foreach($this->tables[$alias]->getPrimaryKeys() as $key) { + $field = $alias.".".$key; + if( ! isset($this->parts["select"][$field])) + $this->parts["select"][$field] = $field." AS ".$alias."__".$key; + } + } + + $q[] = "SELECT ".implode(', ', $this->parts['select']); + + $string = $this->applyInheritance(); + if( ! empty($string)) + $this->parts["where"][] = $string; + + $copy = $this->parts; + unset($copy['select']); + foreach($copy as $name => $part) { + if(empty($part)) + continue; + + $q[] = strtoupper($name).' '.implode(' ',$part); + } + return implode(' ', $q); + } + /** + * getFields + * + * @return array + */ + public function getFields() { + return $this->fields; + } + /** + * addComponent + * + * @param string $tableAlias + * @param string $componentName + * @return void + */ + public function addComponent($tableAlias, $componentName) { + $e = explode(".", $componentName); + + $currPath = ''; + + foreach($e as $k => $component) { + $currPath .= '.'.$component; + if($k == 0) + $currPath = substr($currPath,1); + + if(isset($this->tableAliases[$currPath])) + $alias = $this->tableAliases[$currPath]; + else + $alias = $tableAlias; + + $table = $this->session->getTable($component); + $this->tables[$alias] = $table; + $this->fetchModes[$alias] = Doctrine::FETCH_IMMEDIATE; + $this->tableAliases[$currPath] = $alias; + + if($k !== 0) + $this->joins[$alias] = $prevAlias; + + $prevAlias = $alias; + $prevPath = $currPath; + } + } + +} +?> diff --git a/Doctrine/Record.php b/Doctrine/Record.php index 62efee316..18d5a0a85 100644 --- a/Doctrine/Record.php +++ b/Doctrine/Record.php @@ -67,11 +67,11 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite /** * @var integer $id the primary keys of this object */ - protected $id = array(); + protected $id = array(); /** * @var array $data the record data */ - protected $data = array(); + protected $data = array(); /** * @var integer $state the state of this record * @see STATE_* constants @@ -80,23 +80,27 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite /** * @var array $modified an array containing properties that have been modified */ - protected $modified = array(); + protected $modified = array(); /** * @var array $collections the collections this record is in */ - private $collections = array(); + private $collections = array(); /** - * @var mixed $references an array containing all the references + * @var array $references an array containing all the references */ - private $references = array(); + private $references = array(); /** - * @var mixed $originals an array containing all the original references + * @var array $originals an array containing all the original references */ - private $originals = array(); + private $originals = array(); + /** + * @var array $filters + */ + private $filters = array(); /** * @var integer $index this index is used for creating object identifiers */ - private static $index = 1; + private static $index = 1; /** * @var Doctrine_Null $null a Doctrine_Null object used for extremely fast * null value testing @@ -1108,6 +1112,20 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite break; endswitch; } + /** + * filterRelated + * lazy initializes a new filter instance for given related component + * + * @param $componentName name of the related component + * @return Doctrine_Filter + */ + final public function filterRelated($componentName) { + if( ! isset($this->filters[$componentName])) { + $this->filters[$componentName] = new Doctrine_Filter($componentName); + } + + return $this->filters[$componentName]; + } /** * sets enumerated value array for given field * diff --git a/Doctrine/Relation.php b/Doctrine/Relation.php index 07474583d..d7c13c100 100644 --- a/Doctrine/Relation.php +++ b/Doctrine/Relation.php @@ -67,34 +67,35 @@ class Doctrine_Relation { /** * @return string the relation alias */ - public function getAlias() { + final public function getAlias() { return $this->alias; } /** * @return integer the relation type, either 0 or 1 */ - public function getType() { + final public function getType() { return $this->type; } /** * @return object Doctrine_Table foreign factory object */ - public function getTable() { + final public function getTable() { return $this->table; } /** * @return string the name of the local column */ - public function getLocal() { + final public function getLocal() { return $this->local; } /** * @return string the name of the foreignkey column where * the localkey column is pointing at */ - public function getForeign() { + final public function getForeign() { return $this->foreign; } + /** * getDeleteOperations * @@ -171,6 +172,8 @@ class Doctrine_Relation { } /** * __toString + * + * @return string */ public function __toString() { $r[] = "
"; diff --git a/tests/CompositePrimaryKeyTestCase.php b/tests/CompositePrimaryKeyTestCase.php index 791a01bbb..417436688 100644 --- a/tests/CompositePrimaryKeyTestCase.php +++ b/tests/CompositePrimaryKeyTestCase.php @@ -1,3 +1,14 @@ tables = array(); + $this->tables[] = "CPK_Test"; + $this->tables[] = "CPK_Test2"; + $this->tables[] = "CPK_Association"; + + parent::prepareTables(); + } +} ?> diff --git a/tests/RawSqlTestCase.php b/tests/RawSqlTestCase.php new file mode 100644 index 000000000..23329e97a --- /dev/null +++ b/tests/RawSqlTestCase.php @@ -0,0 +1,116 @@ +session); + $query->parseQuery("SELECT {entity.*} FROM entity"); + $fields = $query->getFields(); + + $this->assertEqual($fields, array("entity.*")); + + $query->addComponent("entity", "Entity"); + + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 11); + } + + public function testLazyPropertyLoading() { + $query = new Doctrine_RawSql($this->session); + $this->session->clear(); + + // selecting proxy objects (lazy property loading) + + $query->parseQuery("SELECT {entity.name}, {entity.id} FROM entity"); + $fields = $query->getFields(); + + $this->assertEqual($fields, array("entity.name", "entity.id")); + $query->addComponent("entity", "Entity"); + + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 11); + + $this->assertEqual($coll[0]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($coll[3]->getState(), Doctrine_Record::STATE_PROXY); + } + + public function testSmartMapping() { + $query = new Doctrine_RawSql($this->session); + // smart component mapping (no need for additional addComponent call + + $query->parseQuery("SELECT {entity.name}, {entity.id} FROM entity"); + $fields = $query->getFields(); + + $this->assertEqual($fields, array("entity.name", "entity.id")); + + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 11); + + $this->assertEqual($coll[0]->getState(), Doctrine_Record::STATE_PROXY); + $this->assertEqual($coll[3]->getState(), Doctrine_Record::STATE_PROXY); + } + + public function testMultipleComponents() { + $query = new Doctrine_RawSql($this->session); + // multi component fetching + + $query->parseQuery("SELECT {entity.name}, {entity.id}, {phonenumber.*} FROM entity LEFT JOIN phonenumber ON phonenumber.entity_id = entity.id"); + + $query->addComponent("entity", "Entity"); + $query->addComponent("phonenumber", "Entity.Phonenumber"); + + $coll = $query->execute(); + $this->assertEqual($coll->count(), 11); + + $count = $this->dbh->count(); + + $coll[4]->Phonenumber[0]->phonenumber; + $this->assertEqual($count, $this->dbh->count()); + + $coll[5]->Phonenumber[0]->phonenumber; + $this->assertEqual($count, $this->dbh->count()); + } + public function testPrimaryKeySelectForcing() { + // forcing the select of primary key fields + + $query = new Doctrine_RawSql($this->session); + + $query->parseQuery("SELECT {entity.name} FROM entity"); + + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 11); + $this->assertTrue(is_numeric($coll[0]->id)); + $this->assertTrue(is_numeric($coll[3]->id)); + $this->assertTrue(is_numeric($coll[7]->id)); + } + public function testMethodOverloading() { + $query = new Doctrine_RawSql($this->session); + $query->select('{entity.name}')->from('entity'); + $query->addComponent("entity", "User"); + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 8); + $this->assertTrue(is_numeric($coll[0]->id)); + $this->assertTrue(is_numeric($coll[3]->id)); + $this->assertTrue(is_numeric($coll[7]->id)); + } + public function testColumnAggregationInheritance() { + // forcing the select of primary key fields + + $query = new Doctrine_RawSql($this->session); + + $query->parseQuery("SELECT {entity.name} FROM entity"); + $query->addComponent("entity", "User"); + $coll = $query->execute(); + + $this->assertEqual($coll->count(), 8); + $this->assertTrue(is_numeric($coll[0]->id)); + $this->assertTrue(is_numeric($coll[3]->id)); + $this->assertTrue(is_numeric($coll[7]->id)); + } +} +?> diff --git a/tests/classes.php b/tests/classes.php index 5ba6ccf4d..52ebf78ff 100644 --- a/tests/classes.php +++ b/tests/classes.php @@ -310,7 +310,7 @@ class ORM_AccessControlsGroups extends Doctrine_Record { $this->hasColumn("accessControlID", "integer", 11); $this->hasColumn("accessGroupID", "integer", 11); - $this->setPrimaryKey(array("accessControlID", "accessGroupID")); + $this->setPrimaryKey(array("accessControlID", "accessGroupID")); } } class EnumTest extends Doctrine_Record { @@ -328,7 +328,28 @@ class Log_Entry extends Doctrine_Record { $this->hasOne("Log_Status", "Log_Entry.status_id"); } } - +class CPK_Test extends Doctrine_Record { + public function setTableDefinition() { + $this->hasColumn("name", "string", 255); + } + public function setUp() { + $this->hasMany("CPK_Test2 as Test","CPK_Association.test2_id"); + } +} +class CPK_Test2 extends Doctrine_Record { + public function setTableDefinition() { + $this->hasColumn("name", "string", 255); + } + public function setUp() { + $this->hasMany("CPK_Test as Test","CPK_Association.test1_id"); + } +} +class CPK_Association extends Doctrine_Record { + public function setTableDefinition() { + $this->hasColumn("test1_id", "integer", 11, "primary"); + $this->hasColumn("test2_id", "integer", 11, "primary"); + } +} class Log_Status extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn("name", "string", 255); diff --git a/tests/run.php b/tests/run.php index db87128e5..af8876434 100644 --- a/tests/run.php +++ b/tests/run.php @@ -19,6 +19,7 @@ require_once("CollectionOffsetTestCase.php"); require_once("QueryTestCase.php"); require_once("CacheQuerySqliteTestCase.php"); require_once("ViewTestCase.php"); +require_once("RawSqlTestCase.php"); error_reporting(E_ALL); @@ -54,6 +55,7 @@ $test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase()); $test->addTestCase(new Doctrine_QueryTestCase()); +$test->addTestCase(new Doctrine_RawSql_TestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase()); //$test->addTestCase(new Doctrine_Cache_SqliteTestCase());