From 827755afd318c45fe6f4be3ed9e953214a326086 Mon Sep 17 00:00:00 2001 From: zYne Date: Wed, 16 May 2007 21:28:33 +0000 Subject: [PATCH] --- draft/new-core/Collection.php | 632 ------------ draft/new-core/Hydrate.php | 633 ------------ draft/new-core/Query.php | 1294 ------------------------ lib/Doctrine/Collection.php | 10 +- lib/Doctrine/Connection.php | 2 - lib/Doctrine/Connection/UnitOfWork.php | 29 +- lib/Doctrine/Query.php | 8 +- lib/Doctrine/Query/Check.php | 4 +- lib/Doctrine/Relation/Association.php | 4 + tests/HydrateTestCase.php | 8 - tests/RecordTestCase.php | 31 +- tests/UnitTestCase.php | 8 +- 12 files changed, 54 insertions(+), 2609 deletions(-) delete mode 100644 draft/new-core/Collection.php delete mode 100644 draft/new-core/Hydrate.php delete mode 100644 draft/new-core/Query.php diff --git a/draft/new-core/Collection.php b/draft/new-core/Collection.php deleted file mode 100644 index 61fe43de5..000000000 --- a/draft/new-core/Collection.php +++ /dev/null @@ -1,632 +0,0 @@ -. - */ -Doctrine::autoload('Doctrine_Access'); -/** - * Doctrine_Collection - * Collection of Doctrine_Record objects. - * - * @package Doctrine - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @category Object Relational Mapping - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision: 1207 $ - * @author Konsta Vesterinen - */ -class Doctrine_Collection2 extends Doctrine_Collection implements Countable, IteratorAggregate, Serializable -{ - /** - * @var array $data an array containing the records of this collection - */ - protected $data = array(); - /** - * @var Doctrine_Table $table each collection has only records of specified table - */ - protected $_table; - /** - * @var array $_snapshot a snapshot of the fetched data - */ - protected $_snapshot = array(); - /** - * @var Doctrine_Record $reference collection can belong to a record - */ - protected $reference; - /** - * @var string $referenceField the reference field of the collection - */ - protected $referenceField; - /** - * @var Doctrine_Relation the record this collection is related to, if any - */ - protected $relation; - /** - * @var string $keyColumn the name of the column that is used for collection key mapping - */ - protected $keyColumn; - /** - * @var Doctrine_Null $null used for extremely fast null value testing - */ - protected static $null; - - - /** - * constructor - * - * @param Doctrine_Table|string $table - */ - public function __construct($table) - { - if ( ! ($table instanceof Doctrine_Table)) { - $table = Doctrine_Manager::getInstance() - ->getTable($table); - } - $this->_table = $table; - - $name = $table->getAttribute(Doctrine::ATTR_COLL_KEY); - if ($name !== null) { - $this->keyColumn = $name; - } - } - /** - * initNullObject - * initializes the null object for this collection - * - * @return void - */ - public static function initNullObject(Doctrine_Null $null) - { - self::$null = $null; - } - /** - * getTable - * returns the table this collection belongs to - * - * @return Doctrine_Table - */ - public function getTable() - { - return $this->_table; - } - /** - * this method is automatically called when this Doctrine_Collection is serialized - * - * @return array - */ - public function serialize() - { - $vars = get_object_vars($this); - - unset($vars['reference']); - unset($vars['reference_field']); - unset($vars['relation']); - unset($vars['expandable']); - unset($vars['expanded']); - unset($vars['generator']); - - $vars['table'] = $vars['table']->getComponentName(); - - return serialize($vars); - } - /** - * unseralize - * this method is automatically called everytime a Doctrine_Collection object is unserialized - * - * @return void - */ - public function unserialize($serialized) - { - $manager = Doctrine_Manager::getInstance(); - $connection = $manager->getCurrentConnection(); - - $array = unserialize($serialized); - - foreach ($array as $name => $values) { - $this->$name = $values; - } - - $this->_table = $connection->getTable($this->_table); - - $this->expanded = array(); - $this->expandable = true; - - $name = $this->_table->getAttribute(Doctrine::ATTR_COLL_KEY); - if ($name !== null) { - $this->keyColumn = $name; - } - } - /** - * setKeyColumn - * sets the key column for this collection - * - * @param string $column - * @return Doctrine_Collection - */ - public function setKeyColumn($column) - { - $this->keyColumn = $column; - - return $this; - } - /** - * getKeyColumn - * returns the name of the key column - * - * @return string - */ - public function getKeyColumn() - { - return $this->column; - } - /** - * getData - * returns all the records as an array - * - * @return array - */ - public function getData() - { - return $this->data; - } - /** - * getFirst - * returns the first record in the collection - * - * @return mixed - */ - public function getFirst() - { - return reset($this->data); - } - /** - * getLast - * returns the last record in the collection - * - * @return mixed - */ - public function getLast() - { - return end($this->data); - } - /** - * setReference - * sets a reference pointer - * - * @return void - */ - public function setReference(Doctrine_Record $record, Doctrine_Relation $relation) - { - $this->reference = $record; - $this->relation = $relation; - - if ($relation instanceof Doctrine_Relation_ForeignKey - || $relation instanceof Doctrine_Relation_LocalKey - ) { - - $this->referenceField = $relation->getForeign(); - - $value = $record->get($relation->getLocal()); - - foreach ($this->getNormalIterator() as $record) { - if ($value !== null) { - $record->set($this->referenceField, $value, false); - } else { - $record->set($this->referenceField, $this->reference, false); - } - } - } elseif ($relation instanceof Doctrine_Relation_Association) { - - } - } - /** - * getReference - * - * @return mixed - */ - public function getReference() - { - return $this->reference; - } - /** - * remove - * removes a specified collection element - * - * @param mixed $key - * @return boolean - */ - public function remove($key) - { - if ( ! isset($this->data[$key])) { - $this->expand($key); - - throw new Doctrine_Collection_Exception('Unknown key ' . $key); - } - - $removed = $this->data[$key]; - - unset($this->data[$key]); - return $removed; - } - /** - * contains - * whether or not this collection contains a specified element - * - * @param mixed $key the key of the element - * @return boolean - */ - public function contains($key) - { - return isset($this->data[$key]); - } - /** - * get - * returns a record for given key - * - * There are two special cases: - * - * 1. if null is given as a key a new record is created and attached - * at the end of the collection - * - * 2. if given key does not exist, then a new record is create and attached - * to the given key - * - * Collection also maps referential information to newly created records - * - * @param mixed $key the key of the element - * @return Doctrine_Record return a specified record - */ - public function get($key) - { - if ($key === null || ! isset($this->data[$key])) { - $record = $this->_table->create(); - - if (isset($this->referenceField)) { - $record->set($this->referenceField, $this->reference, false); - } - - $this->data[] = $record; - - return $record; - } - - return $this->data[$key]; - } - - /** - * @return array an array containing all primary keys - */ - public function getPrimaryKeys() - { - $list = array(); - $name = $this->_table->getIdentifier(); - - foreach ($this->data as $record) { - if (is_array($record) && isset($record[$name])) { - $list[] = $record[$name]; - } else { - $list[] = $record->getIncremented(); - } - } - return $list; - } - /** - * returns all keys - * @return array - */ - public function getKeys() - { - return array_keys($this->data); - } - /** - * count - * this class implements interface countable - * returns the number of records in this collection - * - * @return integer - */ - public function count() - { - return count($this->data); - } - /** - * set - * @param integer $key - * @param Doctrine_Record $record - * @return void - */ - public function set($key, Doctrine_Record $record) - { - if (isset($this->referenceField)) { - $record->set($this->referenceField, $this->reference, false); - } - $this->data[$key] = $record; - } - /** - * adds a record to collection - * @param Doctrine_Record $record record to be added - * @param string $key optional key for the record - * @return boolean - */ - public function add(Doctrine_Record $record, $key = null) - { - if (isset($this->referenceField)) { - $record->set($this->referenceField, $this->reference, false); - } - /** - * for some weird reason in_array cannot be used here (php bug ?) - * - * if used it results in fatal error : [ nesting level too deep ] - */ - foreach ($this->data as $val) { - if ($val === $record) { - return false; - } - } - - if (isset($key)) { - if (isset($this->data[$key])) { - return false; - } - $this->data[$key] = $record; - return true; - } - - if (isset($this->keyColumn)) { - $value = $record->get($this->keyColumn); - if ($value === null) { - throw new Doctrine_Collection_Exception("Couldn't create collection index. Record field '".$this->keyColumn."' was null."); - } - $this->data[$value] = $record; - } else { - $this->data[] = $record; - } - return true; - } - /** - * loadRelated - * - * @param mixed $name - * @return boolean - */ - public function loadRelated($name = null) - { - $list = array(); - $query = new Doctrine_Query($this->_table->getConnection()); - - if ( ! isset($name)) { - foreach ($this->data as $record) { - $value = $record->getIncremented(); - if ($value !== null) { - $list[] = $value; - } - }; - $query->from($this->_table->getComponentName() . '(' . implode(", ",$this->_table->getPrimaryKeys()) . ')'); - $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')'); - - return $query; - } - - $rel = $this->_table->getRelation($name); - - if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) { - foreach ($this->data as $record) { - $list[] = $record[$rel->getLocal()]; - } - } else { - foreach ($this->data as $record) { - $value = $record->getIncremented(); - if ($value !== null) { - $list[] = $value; - } - } - } - - $dql = $rel->getRelationDql(count($list), 'collection'); - - $coll = $query->query($dql, $list); - - $this->populateRelated($name, $coll); - } - /** - * populateRelated - * - * @param string $name - * @param Doctrine_Collection $coll - * @return void - */ - public function populateRelated($name, Doctrine_Collection $coll) - { - $rel = $this->_table->getRelation($name); - $table = $rel->getTable(); - $foreign = $rel->getForeign(); - $local = $rel->getLocal(); - - if ($rel instanceof Doctrine_Relation_LocalKey) { - foreach ($this->data as $key => $record) { - foreach ($coll as $k => $related) { - if ($related[$foreign] == $record[$local]) { - $this->data[$key]->setRelated($name, $related); - } - } - } - } elseif ($rel instanceof Doctrine_Relation_ForeignKey) { - foreach ($this->data as $key => $record) { - if ( ! $record->exists()) { - continue; - } - $sub = new Doctrine_Collection($table); - - foreach ($coll as $k => $related) { - if ($related[$foreign] == $record[$local]) { - $sub->add($related); - $coll->remove($k); - } - } - - $this->data[$key]->setRelated($name, $sub); - } - } elseif ($rel instanceof Doctrine_Relation_Association) { - $identifier = $this->_table->getIdentifier(); - $asf = $rel->getAssociationFactory(); - $name = $table->getComponentName(); - - foreach ($this->data as $key => $record) { - if ( ! $record->exists()) { - continue; - } - $sub = new Doctrine_Collection($table); - foreach ($coll as $k => $related) { - if ($related->get($local) == $record[$identifier]) { - $sub->add($related->get($name)); - } - } - $this->data[$key]->setRelated($name, $sub); - - } - } - } - /** - * getNormalIterator - * returns normal iterator - an iterator that will not expand this collection - * - * @return Doctrine_Iterator_Normal - */ - public function getNormalIterator() - { - return new Doctrine_Collection_Iterator_Normal($this); - } - /** - * takeSnapshot - * takes a snapshot from this collection - * - * snapshots are used for diff processing, for example - * when a fetched collection has three elements, then two of those - * are being removed the diff would contain one element - * - * Doctrine_Collection::save() attaches the diff with the help of last - * snapshot. - * - * @return Doctrine_Collection - */ - public function takeSnapshot() - { - $this->_snapshot = $this->data; - - return $this; - } - /** - * getSnapshot - * returns the data of the last snapshot - * - * @return array returns the data in last snapshot - */ - public function getSnapshot() - { - return $this->_snapshot; - } - /** - * processDiff - * processes the difference of the last snapshot and the current data - * - * an example: - * Snapshot with the objects 1, 2 and 4 - * Current data with objects 2, 3 and 5 - * - * The process would: - * 1. remove object 4 - * 2. add objects 3 and 5 - * - * @return Doctrine_Collection - */ - public function processDiff() - { - foreach (array_diff($this->snapshot, $this->data) as $record) { - $record->delete(); - } - foreach (array_diff($this->data, $this->snapshot) as $record) { - $record->save(); - } - - return $this; - } - /** - * save - * saves all records of this collection - * - * @return Doctrine_Collection - */ - public function save(Doctrine_Connection $conn = null) - { - if ($conn == null) { - $conn = $this->_table->getConnection(); - } - $conn->beginTransaction(); - - foreach ($this as $key => $record) { - $record->save($conn); - } - - $conn->commit(); - - return $this; - } - /** - * delete - * single shot delete - * deletes all records from this collection - * and uses only one database query to perform this operation - * - * @return Doctrine_Collection - */ - public function delete(Doctrine_Connection $conn = null) - { - if ($conn == null) { - $conn = $this->_table->getConnection(); - } - - $conn->beginTransaction(); - - foreach ($this as $key => $record) { - $record->delete($conn); - } - - $conn->commit(); - - $this->data = array(); - - return $this; - } - /** - * getIterator - * @return object ArrayIterator - */ - public function getIterator() - { - $data = $this->data; - return new ArrayIterator($data); - } - /** - * returns a string representation of this object - */ - public function __toString() - { - return Doctrine_Lib::getCollectionAsString($this); - } -} diff --git a/draft/new-core/Hydrate.php b/draft/new-core/Hydrate.php deleted file mode 100644 index dd7dbaf10..000000000 --- a/draft/new-core/Hydrate.php +++ /dev/null @@ -1,633 +0,0 @@ -. - */ - -/** - * Doctrine_Hydrate is a base class for Doctrine_RawSql and Doctrine_Query. - * Its purpose is to populate object graphs. - * - * - * @package Doctrine - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @category Object Relational Mapping - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision: 1255 $ - * @author Konsta Vesterinen - */ -class Doctrine_Hydrate2 -{ - /** - * QUERY TYPE CONSTANTS - */ - - /** - * constant for SELECT queries - */ - const SELECT = 0; - /** - * constant for DELETE queries - */ - const DELETE = 1; - /** - * constant for UPDATE queries - */ - const UPDATE = 2; - /** - * constant for INSERT queries - */ - const INSERT = 3; - /** - * constant for CREATE queries - */ - const CREATE = 4; - - /** - * @var array $params query input parameters - */ - protected $params = array(); - /** - * @var Doctrine_Connection $conn Doctrine_Connection object - */ - protected $conn; - /** - * @var Doctrine_View $view Doctrine_View object - */ - protected $view; - /** - * @var array $_aliasMap two dimensional array containing the map for query aliases - * Main keys are component aliases - * - * table table object associated with given alias - * - * relation the relation object owned by the parent - * - * parent the alias of the parent - */ - protected $_aliasMap = array(); - - /** - * @var array $tableAliases - */ - protected $tableAliases = array(); - /** - * - */ - protected $pendingAggregates = array(); - /** - * - */ - protected $subqueryAggregates = array(); - /** - * @var array $aggregateMap an array containing all aggregate aliases, keys as dql aliases - * and values as sql aliases - */ - protected $aggregateMap = array(); - /** - * @var Doctrine_Hydrate_Alias $aliasHandler - */ - protected $aliasHandler; - /** - * @var array $parts SQL query string parts - */ - protected $parts = array( - 'select' => array(), - 'from' => array(), - 'set' => array(), - 'join' => array(), - 'where' => array(), - 'groupby' => array(), - 'having' => array(), - 'orderby' => array(), - 'limit' => false, - 'offset' => false, - ); - /** - * @var integer $type the query type - * - * @see Doctrine_Query::* constants - */ - protected $type = self::SELECT; - /** - * constructor - * - * @param Doctrine_Connection|null $connection - */ - public function __construct($connection = null) - { - if ( ! ($connection instanceof Doctrine_Connection)) { - $connection = Doctrine_Manager::getInstance()->getCurrentConnection(); - } - $this->conn = $connection; - $this->aliasHandler = new Doctrine_Hydrate_Alias(); - } - /** - * getTableAliases - * - * @return array - */ - public function getTableAliases() - { - return $this->tableAliases; - } - public function setTableAliases(array $aliases) - { - $this->tableAliases = $aliases; - } - public function getTableAlias($componentAlias) - { - return $this->aliasHandler->getShortAlias($componentAlias); - } - public function addQueryPart($name, $part) - { - if ( ! isset($this->parts[$name])) { - throw new Doctrine_Hydrate_Exception('Unknown query part ' . $name); - } - $this->parts[$name][] = $part; - } - public function getDeclaration($name) - { - if ( ! isset($this->_aliasMap[$name])) { - throw new Doctrine_Hydrate_Exception('Unknown component alias ' . $name); - } - - return $this->_aliasMap[$name]; - } - public function setQueryPart($name, $part) - { - if ( ! isset($this->parts[$name])) { - throw new Doctrine_Hydrate_Exception('Unknown query part ' . $name); - } - - if ($name !== 'limit' && $name !== 'offset') { - $this->parts[$name] = array($part); - } else { - $this->parts[$name] = $part; - } - } - /** - * copyAliases - * - * @return void - */ - public function copyAliases($query) - { - $this->aliasHandler = $query->aliasHandler; - - return $this; - } - /** - * createSubquery - * - * @return Doctrine_Hydrate - */ - public function createSubquery() - { - $class = get_class($this); - $obj = new $class(); - - // copy the aliases to the subquery - $obj->copyAliases($this); - - // this prevents the 'id' being selected, re ticket #307 - $obj->isSubquery(true); - - return $obj; - } - /** - * limitSubqueryUsed - * - * @return boolean - */ - public function isLimitSubqueryUsed() - { - return false; - } - public function getQueryPart($part) - { - if ( ! isset($this->parts[$part])) { - throw new Doctrine_Hydrate_Exception('Unknown query part ' . $part); - } - - return $this->parts[$part]; - } - /** - * remove - * - * @param $name - */ - public function remove($name) - { - if (isset($this->parts[$name])) { - if ($name == "limit" || $name == "offset") { - $this->parts[$name] = false; - } else { - $this->parts[$name] = array(); - } - } - return $this; - } - /** - * clear - * resets all the variables - * - * @return void - */ - protected function clear() - { - $this->tables = array(); - $this->parts = array( - 'select' => array(), - 'from' => array(), - 'set' => array(), - 'join' => array(), - 'where' => array(), - 'groupby' => array(), - 'having' => array(), - 'orderby' => array(), - 'limit' => false, - 'offset' => false, - ); - $this->inheritanceApplied = false; - $this->tableAliases = array(); - $this->aliasHandler->clear(); - } - /** - * getConnection - * - * @return Doctrine_Connection - */ - public function getConnection() - { - return $this->conn; - } - /** - * 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 - * returns the view associated with this query object (if any) - * - * @return Doctrine_View the view associated with this query object - */ - public function getView() - { - return $this->view; - } - /** - * getParams - * - * @return array - */ - public function getParams() - { - return $this->params; - } - /** - * setParams - * - * @param array $params - */ - public function setParams(array $params = array()) { - $this->params = $params; - } - /** - * _fetch - * - * @param array $params prepared statement parameters - * @param integer $fetchMode the fetchmode - * @see Doctrine::FETCH_* constants - */ - public function _fetch($params = array(), $fetchMode = Doctrine::FETCH_RECORD) - { - $params = $this->conn->convertBooleans(array_merge($this->params, $params)); - - if ( ! $this->view) { - $query = $this->getQuery($params); - } else { - $query = $this->view->getSelectSql(); - } - - if ($this->isLimitSubqueryUsed() && - $this->conn->getDBH()->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') { - - $params = array_merge($params, $params); - } - $stmt = $this->conn->execute($query, $params); - - return $this->parseData($stmt); - } - - public function setAliasMap($map) - { - $this->_aliasMap = $map; - } - public function getAliasMap() - { - return $this->_aliasMap; - } - /** - * mapAggregateValues - * map the aggregate values of given dataset row to a given record - * - * @param Doctrine_Record $record - * @param array $row - * @return Doctrine_Record - */ - public function mapAggregateValues($record, array $row, $alias) - { - $found = false; - // aggregate values have numeric keys - if (isset($row[0])) { - // map each aggregate value - foreach ($row as $index => $value) { - $agg = false; - - if (isset($this->pendingAggregates[$alias][$index])) { - $agg = $this->pendingAggregates[$alias][$index][3]; - } elseif (isset($this->subqueryAggregates[$alias][$index])) { - $agg = $this->subqueryAggregates[$alias][$index]; - } - $record->mapValue($agg, $value); - $found = true; - } - } - return $found; - } - /** - * execute - * executes the dql query and populates all collections - * - * @param string $params - * @return Doctrine_Collection the root collection - */ - public function execute($params = array(), $return = Doctrine::FETCH_RECORD) - { - $array = (array) $this->_fetch($params, $return = Doctrine::FETCH_RECORD); - - if (empty($this->_aliasMap)) { - throw new Doctrine_Hydrate_Exception("Couldn't execute query. Component alias map was empty."); - } - // initialize some variables used within the main loop - reset($this->_aliasMap); - $rootMap = current($this->_aliasMap); - $rootAlias = key($this->_aliasMap); - $coll = new Doctrine_Collection2($rootMap['table']); - $prev[$rootAlias] = $coll; - - $prevRow = array(); - /** - * iterate over the fetched data - * here $data is a two dimensional array - */ - foreach ($array as $data) { - /** - * remove duplicated data rows and map data into objects - */ - foreach ($data as $tableAlias => $row) { - // skip empty rows (not mappable) - if (empty($row)) { - continue; - } - $alias = $this->aliasHandler->getComponentAlias($tableAlias); - $map = $this->_aliasMap[$alias]; - - // initialize previous row array if not set - if ( ! isset($prevRow[$tableAlias])) { - $prevRow[$tableAlias] = array(); - } - - // don't map duplicate rows - if ($prevRow[$tableAlias] !== $row) { - $identifiable = $this->isIdentifiable($row, $map['table']->getIdentifier()); - - if ($identifiable) { - // set internal data - $map['table']->setData($row); - } - - // initialize a new record - $record = $map['table']->getRecord(); - - // map aggregate values (if any) - if($this->mapAggregateValues($record, $row, $alias)) { - $identifiable = true; - } - - - if ($alias == $rootAlias) { - // add record into root collection - - if ($identifiable) { - $coll->add($record); - unset($prevRow); - } - } else { - - $relation = $map['relation']; - $parentAlias = $map['parent']; - $parentMap = $this->_aliasMap[$parentAlias]; - $parent = $prev[$parentAlias]->getLast(); - - // check the type of the relation - if ($relation->isOneToOne()) { - if ( ! $identifiable) { - continue; - } - $prev[$alias] = $record; - } else { - // one-to-many relation or many-to-many relation - if ( ! $prev[$parentAlias]->getLast()->hasReference($relation->getAlias())) { - // initialize a new collection - $prev[$alias] = new Doctrine_Collection2($map['table']); - $prev[$alias]->setReference($parent, $relation); - } else { - // previous entry found from memory - $prev[$alias] = $prev[$parentAlias]->getLast()->get($relation->getAlias()); - } - // add record to the current collection - if ($identifiable) { - $prev[$alias]->add($record); - } - } - // initialize the relation from parent to the current collection/record - $parent->set($relation->getAlias(), $prev[$alias]); - } - - // following statement is needed to ensure that mappings - // are being done properly when the result set doesn't - // contain the rows in 'right order' - - if ($prev[$alias] !== $record) { - $prev[$alias] = $record; - } - } - $prevRow[$tableAlias] = $row; - } - } - return $coll; - } - /** - * isIdentifiable - * returns whether or not a given data row is identifiable (it contains - * all primary key fields specified in the second argument) - * - * @param array $row - * @param mixed $primaryKeys - * @return boolean - */ - public function isIdentifiable(array $row, $primaryKeys) - { - if (is_array($primaryKeys)) { - foreach ($primaryKeys as $id) { - if ($row[$id] == null) { - return false; - } - } - } else { - if ( ! isset($row[$primaryKeys])) { - return false; - } - } - return true; - } - /** - * getType - * - * returns the type of this query object - * by default the type is Doctrine_Hydrate::SELECT but if update() or delete() - * are being called the type is Doctrine_Hydrate::UPDATE and Doctrine_Hydrate::DELETE, - * respectively - * - * @see Doctrine_Hydrate::SELECT - * @see Doctrine_Hydrate::UPDATE - * @see Doctrine_Hydrate::DELETE - * - * @return integer return the query type - */ - public function getType() - { - return $this->type; - } - /** - * applyInheritance - * applies column aggregation inheritance to DQL / SQL query - * - * @return string - */ - public function applyInheritance() - { - // get the inheritance maps - $array = array(); - - foreach ($this->_aliasMap as $componentAlias => $data) { - $tableAlias = $this->getTableAlias($componentAlias); - $array[$tableAlias][] = $data['table']->inheritanceMap; - } - - // apply inheritance maps - $str = ''; - $c = array(); - - $index = 0; - foreach ($array as $tableAlias => $maps) { - $a = array(); - - // don't use table aliases if the query isn't a select query - if ($this->type !== Doctrine_Query::SELECT) { - $tableAlias = ''; - } else { - $tableAlias .= '.'; - } - - foreach ($maps as $map) { - $b = array(); - foreach ($map as $field => $value) { - if ($index > 0) { - $b[] = '(' . $tableAlias . $field . ' = ' . $value - . ' OR ' . $tableAlias . $field . ' IS NULL)'; - } else { - $b[] = $tableAlias . $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 statement object - * - * @param mixed $stmt - * @return array - */ - public function parseData($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); - - $field = strtolower(array_pop($e)); - $tableAlias = strtolower(implode('__', $e)); - - $data[$tableAlias][$field] = $value; - - unset($data[$key]); - } - $array[] = $data; - } - - $stmt->closeCursor(); - return $array; - } - /** - * @return string returns a string representation of this object - */ - public function __toString() - { - return Doctrine_Lib::formatSql($this->getQuery()); - } -} diff --git a/draft/new-core/Query.php b/draft/new-core/Query.php deleted file mode 100644 index 49535fe53..000000000 --- a/draft/new-core/Query.php +++ /dev/null @@ -1,1294 +0,0 @@ -. - */ -Doctrine::autoload('Doctrine_Hydrate'); -/** - * Doctrine_Query - * - * @package Doctrine - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @category Object Relational Mapping - * @link www.phpdoctrine.com - * @since 1.0 - * @version $Revision: 1296 $ - * @author Konsta Vesterinen - */ -class Doctrine_Query extends Doctrine_Hydrate2 implements Countable -{ - /** - * @param array $subqueryAliases the table aliases needed in some LIMIT subqueries - */ - private $subqueryAliases = array(); - /** - * @param boolean $needsSubquery - */ - private $needsSubquery = false; - /** - * @param boolean $limitSubqueryUsed - */ - private $limitSubqueryUsed = false; - - - protected $_status = array('needsSubquery' => true); - /** - * @param boolean $isSubquery whether or not this query object is a subquery of another - * query object - */ - private $isSubquery; - - private $isDistinct = false; - - private $neededTables = array(); - /** - * @var array $pendingFields - */ - private $pendingFields = array(); - /** - * @var array $pendingSubqueries SELECT part subqueries, these are called pending subqueries since - * they cannot be parsed directly (some queries might be correlated) - */ - private $pendingSubqueries = array(); - /** - * @var boolean $subqueriesProcessed Whether or not pending subqueries have already been processed. - * Consequent calls to getQuery would result badly constructed queries - * without this variable - * - * Since subqueries can be correlated, they can only be processed when - * the main query is fully constructed - */ - private $subqueriesProcessed = false; - /** - * @var array $_parsers an array of parser objects - */ - protected $_parsers = array(); - - - /** - * create - * returns a new Doctrine_Query object - * - * @return Doctrine_Query - */ - public static function create() - { - return new Doctrine_Query(); - } - /** - * isSubquery - * if $bool parameter is set this method sets the value of - * Doctrine_Query::$isSubquery. If this value is set to true - * the query object will not load the primary key fields of the selected - * components. - * - * If null is given as the first parameter this method retrieves the current - * value of Doctrine_Query::$isSubquery. - * - * @param boolean $bool whether or not this query acts as a subquery - * @return Doctrine_Query|bool - */ - public function isSubquery($bool = null) - { - if ($bool === null) { - return $this->isSubquery; - } - - $this->isSubquery = (bool) $bool; - return $this; - } - - /** - * getAggregateAlias - * - * @return string - */ - public function getAggregateAlias($dqlAlias) - { - if(isset($this->aggregateMap[$dqlAlias])) { - return $this->aggregateMap[$dqlAlias]; - } - - return null; - } - - public function isDistinct($distinct = null) - { - if(isset($distinct)) - $this->isDistinct = (bool) $distinct; - - return $this->isDistinct; - } - - /** - * getParser - * parser lazy-loader - * - * @throws Doctrine_Query_Exception if unknown parser name given - * @return Doctrine_Query_Part - */ - public function getParser($name) - { - if ( ! isset($this->_parsers[$name])) { - $class = 'Doctrine_Query_' . ucwords(strtolower($name)); - - Doctrine::autoload($class); - - if ( ! class_exists($class)) { - throw new Doctrine_Query_Exception('Unknown parser ' . $name); - } - - $this->_parsers[$name] = new $class($this); - } - - return $this->_parsers[$name]; - } - /** - * processPendingFields - * the fields in SELECT clause cannot be parsed until the components - * in FROM clause are parsed, hence this method is called everytime a - * specific component is being parsed. - * - * @throws Doctrine_Query_Exception if unknown component alias has been given - * @param string $componentAlias the alias of the component - * @return void - */ - public function processPendingFields($componentAlias) - { - $tableAlias = $this->getTableAlias($componentAlias); - $table = $this->_aliasMap[$componentAlias]['table']; - - if (isset($this->pendingFields[$componentAlias])) { - $fields = $this->pendingFields[$componentAlias]; - - // check for wildcards - if (in_array('*', $fields)) { - $fields = $table->getColumnNames(); - } else { - // only auto-add the primary key fields if this query object is not - // a subquery of another query object - if ( ! $this->isSubquery) { - $fields = array_unique(array_merge($table->getPrimaryKeys(), $fields)); - } - } - } - foreach ($fields as $name) { - $name = $table->getColumnName($name); - - $this->parts['select'][] = $tableAlias . '.' .$name . ' AS ' . $tableAlias . '__' . $name; - } - - $this->neededTables[] = $tableAlias; - - } - /** - * parseSelect - * parses the query select part and - * adds selected fields to pendingFields array - * - * @param string $dql - */ - public function parseSelect($dql) - { - $refs = Doctrine_Tokenizer::bracketExplode($dql, ','); - - foreach ($refs as $reference) { - if (strpos($reference, '(') !== false) { - if (substr($reference, 0, 1) === '(') { - // subselect found in SELECT part - $this->parseSubselect($reference); - } else { - $this->parseAggregateFunction2($reference); - } - } else { - - $e = explode('.', $reference); - if (count($e) > 2) { - $this->pendingFields[] = $reference; - } else { - $this->pendingFields[$e[0]][] = $e[1]; - } - } - } - } - /** - * parseSubselect - * - * parses the subquery found in DQL SELECT part and adds the - * parsed form into $pendingSubqueries stack - * - * @param string $reference - * @return void - */ - public function parseSubselect($reference) - { - $e = Doctrine_Tokenizer::bracketExplode($reference, ' '); - $alias = $e[1]; - - if (count($e) > 2) { - if (strtoupper($e[1]) !== 'AS') { - throw new Doctrine_Query_Exception('Syntax error near: ' . $reference); - } - $alias = $e[2]; - } - - $subquery = substr($e[0], 1, -1); - - $this->pendingSubqueries[] = array($subquery, $alias); - } - public function parseAggregateFunction2($func) - { - $e = Doctrine_Tokenizer::bracketExplode($func, ' '); - $func = $e[0]; - - $pos = strpos($func, '('); - $name = substr($func, 0, $pos); - - try { - $argStr = substr($func, ($pos + 1), -1); - $args = explode(',', $argStr); - - $func = call_user_func_array(array($this->conn->expression, $name), $args); - - if(substr($func, 0, 1) !== '(') { - $pos = strpos($func, '('); - $name = substr($func, 0, $pos); - } else { - $name = $func; - } - - $e2 = explode(' ', $args[0]); - - $distinct = ''; - if (count($e2) > 1) { - if (strtoupper($e2[0]) == 'DISTINCT') { - $distinct = 'DISTINCT '; - } - - $args[0] = $e2[1]; - } - - - - $parts = explode('.', $args[0]); - $owner = $parts[0]; - $alias = (isset($e[1])) ? $e[1] : $name; - - $e3 = explode('.', $alias); - - if (count($e3) > 1) { - $alias = $e3[1]; - $owner = $e3[0]; - } - - // a function without parameters eg. RANDOM() - if ($owner === '') { - $owner = 0; - } - - $this->pendingAggregates[$owner][] = array($name, $args, $distinct, $alias); - } catch(Doctrine_Expression_Exception $e) { - throw new Doctrine_Query_Exception('Unknown function ' . $func . '.'); - } - } - public function processPendingSubqueries() - { - if ($this->subqueriesProcessed === true) { - return false; - } - - foreach ($this->pendingSubqueries as $value) { - list($dql, $alias) = $value; - - $sql = $this->createSubquery()->parseQuery($dql, false)->getQuery(); - - - - reset($this->_aliasMap); - $componentAlias = key($this->_aliasMap); - $tableAlias = $this->getTableAlias($componentAlias); - - $sqlAlias = $tableAlias . '__' . count($this->aggregateMap); - - $this->parts['select'][] = '(' . $sql . ') AS ' . $sqlAlias; - - $this->aggregateMap[$alias] = $sqlAlias; - $this->subqueryAggregates[$componentAlias][] = $alias; - } - $this->subqueriesProcessed = true; - - return true; - } - public function processPendingAggregates($componentAlias) - { - $tableAlias = $this->getTableAlias($componentAlias); - - $map = reset($this->_aliasMap); - $root = $map['table']; - $table = $this->_aliasMap[$componentAlias]['table']; - - $aggregates = array(); - - if(isset($this->pendingAggregates[$componentAlias])) { - $aggregates = $this->pendingAggregates[$componentAlias]; - } - - if ($root === $table) { - if (isset($this->pendingAggregates[0])) { - $aggregates += $this->pendingAggregates[0]; - } - } - - foreach($aggregates as $parts) { - list($name, $args, $distinct, $alias) = $parts; - - $arglist = array(); - foreach($args as $arg) { - $e = explode('.', $arg); - - - if (is_numeric($arg)) { - $arglist[] = $arg; - } elseif (count($e) > 1) { - $map = $this->_aliasMap[$e[0]]; - $table = $map['table']; - - $e[1] = $table->getColumnName($e[1]); - - if ( ! $table->hasColumn($e[1])) { - throw new Doctrine_Query_Exception('Unknown column ' . $e[1]); - } - - $arglist[] = $tableAlias . '.' . $e[1]; - } else { - $arglist[] = $e[0]; - } - } - - $sqlAlias = $tableAlias . '__' . count($this->aggregateMap); - - if (substr($name, 0, 1) !== '(') { - $this->parts['select'][] = $name . '(' . $distinct . implode(', ', $arglist) . ') AS ' . $sqlAlias; - } else { - $this->parts['select'][] = $name . ' AS ' . $sqlAlias; - } - $this->aggregateMap[$alias] = $sqlAlias; - $this->neededTables[] = $tableAlias; - } - } - /** - * getQueryBase - * returns the base of the generated sql query - * On mysql driver special strategy has to be used for DELETE statements - * - * @return string the base of the generated sql query - */ - public function getQueryBase() - { - switch ($this->type) { - case self::DELETE: - $q = 'DELETE FROM '; - break; - case self::UPDATE: - $q = 'UPDATE '; - break; - case self::SELECT: - $distinct = ($this->isDistinct()) ? 'DISTINCT ' : ''; - - $q = 'SELECT ' . $distinct . implode(', ', $this->parts['select']) . ' FROM '; - break; - } - return $q; - } - /** - * buildFromPart - * - * @return string - */ - public function buildFromPart() - { - $q = ''; - foreach ($this->parts['from'] as $k => $part) { - if ($k === 0) { - $q .= $part; - continue; - } - // preserve LEFT JOINs only if needed - - if (substr($part, 0, 9) === 'LEFT JOIN') { - $e = explode(' ', $part); - - $aliases = array_merge($this->subqueryAliases, - array_keys($this->neededTables)); - - if( ! in_array($e[3], $aliases) && - ! in_array($e[2], $aliases) && - - ! empty($this->pendingFields)) { - continue; - } - - } - - $e = explode(' ON ', $part); - - // we can always be sure that the first join condition exists - $e2 = explode(' AND ', $e[1]); - - $part = $e[0] . ' ON ' . array_shift($e2); - - if ( ! empty($e2)) { - $parser = new Doctrine_Query_JoinCondition($this); - $part .= ' AND ' . $parser->_parse(implode(' AND ', $e2)); - } - - $q .= ' ' . $part; - } - return $q; - } - /** - * builds the sql query from the given parameters and applies things such as - * column aggregation inheritance and limit subqueries if needed - * - * @param array $params an array of prepared statement params (needed only in mysql driver - * when limit subquery algorithm is used) - * @return string the built sql query - */ - public function getQuery($params = array()) - { - if (empty($this->parts['select']) || empty($this->parts['from'])) { - return false; - } - - $needsSubQuery = false; - $subquery = ''; - $map = reset($this->_aliasMap); - $table = $map['table']; - $rootAlias = key($this->_aliasMap); - - if ( ! empty($this->parts['limit']) && $this->needsSubquery && $table->getAttribute(Doctrine::ATTR_QUERY_LIMIT) == Doctrine::LIMIT_RECORDS) { - $needsSubQuery = true; - $this->limitSubqueryUsed = true; - } - - // process all pending SELECT part subqueries - $this->processPendingSubqueries(); - - // build the basic query - - $str = ''; - if ($this->isDistinct()) { - $str = 'DISTINCT '; - } - - $q = $this->getQueryBase(); - $q .= $this->buildFromPart(); - - if ( ! empty($this->parts['set'])) { - $q .= ' SET ' . implode(', ', $this->parts['set']); - } - - $string = $this->applyInheritance(); - - if ( ! empty($string)) { - $this->parts['where'][] = '(' . $string . ')'; - } - - - $modifyLimit = true; - if ( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"])) { - - if ($needsSubQuery) { - $subquery = $this->getLimitSubquery(); - - - switch (strtolower($this->conn->getName())) { - case 'mysql': - // mysql doesn't support LIMIT in subqueries - $list = $this->conn->execute($subquery, $params)->fetchAll(PDO::FETCH_COLUMN); - $subquery = implode(', ', $list); - break; - case 'pgsql': - // pgsql needs special nested LIMIT subquery - $subquery = 'SELECT doctrine_subquery_alias.' . $table->getIdentifier(). ' FROM (' . $subquery . ') AS doctrine_subquery_alias'; - break; - } - - $field = $this->aliasHandler->getShortAlias($rootAlias) . '.' . $table->getIdentifier(); - - // only append the subquery if it actually contains something - if ($subquery !== '') { - array_unshift($this->parts['where'], $field. ' IN (' . $subquery . ')'); - } - - $modifyLimit = false; - } - } - - $q .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : ''; - $q .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : ''; - $q .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']): ''; - $q .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : ''; - - if ($modifyLimit) { - $q = $this->conn->modifyLimitQuery($q, $this->parts['limit'], $this->parts['offset']); - } - - // return to the previous state - if ( ! empty($string)) { - array_pop($this->parts['where']); - } - if ($needsSubQuery) { - array_shift($this->parts['where']); - } - return $q; - } - /** - * getLimitSubquery - * this is method is used by the record limit algorithm - * - * when fetching one-to-many, many-to-many associated data with LIMIT clause - * an additional subquery is needed for limiting the number of returned records instead - * of limiting the number of sql result set rows - * - * @return string the limit subquery - */ - public function getLimitSubquery() - { - $map = reset($this->_aliasMap); - $table = $map['table']; - $componentAlias = key($this->_aliasMap); - - // get short alias - $alias = $this->aliasHandler->getShortAlias($componentAlias); - $primaryKey = $alias . '.' . $table->getIdentifier(); - - // initialize the base of the subquery - $subquery = 'SELECT DISTINCT ' . $primaryKey; - - if ($this->conn->getDBH()->getAttribute(PDO::ATTR_DRIVER_NAME) == 'pgsql') { - // pgsql needs the order by fields to be preserved in select clause - - foreach ($this->parts['orderby'] as $part) { - $e = explode(' ', $part); - - // don't add primarykey column (its already in the select clause) - if ($e[0] !== $primaryKey) { - $subquery .= ', ' . $e[0]; - } - } - } - - $subquery .= ' FROM'; - - foreach ($this->parts['from'] as $part) { - // preserve LEFT JOINs only if needed - if (substr($part,0,9) === 'LEFT JOIN') { - $e = explode(' ', $part); - - if ( ! in_array($e[3], $this->subqueryAliases) && - ! in_array($e[2], $this->subqueryAliases)) { - continue; - } - } - - $subquery .= ' ' . $part; - } - - // all conditions must be preserved in subquery - $subquery .= ( ! empty($this->parts['where']))? ' WHERE ' . implode(' AND ', $this->parts['where']) : ''; - $subquery .= ( ! empty($this->parts['groupby']))? ' GROUP BY ' . implode(', ', $this->parts['groupby']) : ''; - $subquery .= ( ! empty($this->parts['having']))? ' HAVING ' . implode(' AND ', $this->parts['having']) : ''; - $subquery .= ( ! empty($this->parts['orderby']))? ' ORDER BY ' . implode(', ', $this->parts['orderby']) : ''; - - // add driver specific limit clause - $subquery = $this->conn->modifyLimitQuery($subquery, $this->parts['limit'], $this->parts['offset']); - - $parts = Doctrine_Tokenizer::quoteExplode($subquery, ' ', "'", "'"); - - foreach($parts as $k => $part) { - if(strpos($part, "'") !== false) { - continue; - } - - if($this->aliasHandler->hasAliasFor($part)) { - $parts[$k] = $this->aliasHandler->generateNewAlias($part); - } - - if(strpos($part, '.') !== false) { - $e = explode('.', $part); - - $trimmed = ltrim($e[0], '( '); - $pos = strpos($e[0], $trimmed); - - $e[0] = substr($e[0], 0, $pos) . $this->aliasHandler->generateNewAlias($trimmed); - $parts[$k] = implode('.', $e); - } - } - $subquery = implode(' ', $parts); - - return $subquery; - } - /** - * tokenizeQuery - * splits the given dql query into an array where keys - * represent different query part names and values are - * arrays splitted using sqlExplode method - * - * example: - * - * parameter: - * $query = "SELECT u.* FROM User u WHERE u.name LIKE ?" - * returns: - * array('select' => array('u.*'), - * 'from' => array('User', 'u'), - * 'where' => array('u.name', 'LIKE', '?')) - * - * @param string $query DQL query - * @throws Doctrine_Query_Exception if some generic parsing error occurs - * @return array an array containing the query string parts - */ - public function tokenizeQuery($query) - { - $e = Doctrine_Tokenizer::sqlExplode($query, ' '); - - foreach($e as $k=>$part) { - $part = trim($part); - switch(strtolower($part)) { - case 'delete': - case 'update': - case 'select': - case 'set': - case 'from': - case 'where': - case 'limit': - case 'offset': - case 'having': - $p = $part; - $parts[$part] = array(); - break; - case 'order': - case 'group': - $i = ($k + 1); - if(isset($e[$i]) && strtolower($e[$i]) === "by") { - $p = $part; - $parts[$part] = array(); - } else - $parts[$p][] = $part; - break; - case "by": - continue; - default: - if( ! isset($p)) - throw new Doctrine_Query_Exception("Couldn't parse query."); - - $parts[$p][] = $part; - } - } - return $parts; - } - /** - * DQL PARSER - * parses a DQL query - * first splits the query in parts and then uses individual - * parsers for each part - * - * @param string $query DQL query - * @param boolean $clear whether or not to clear the aliases - * @throws Doctrine_Query_Exception if some generic parsing error occurs - * @return Doctrine_Query - */ - public function parseQuery($query, $clear = true) - { - if ($clear) { - $this->clear(); - } - - $query = trim($query); - $query = str_replace("\n", ' ', $query); - $query = str_replace("\r", ' ', $query); - - $parts = $this->tokenizeQuery($query); - - foreach($parts as $k => $part) { - $part = implode(' ', $part); - switch(strtolower($k)) { - case 'create': - $this->type = self::CREATE; - break; - case 'insert': - $this->type = self::INSERT; - break; - case 'delete': - $this->type = self::DELETE; - break; - case 'select': - $this->type = self::SELECT; - $this->parseSelect($part); - break; - case 'update': - $this->type = self::UPDATE; - $k = 'FROM'; - - case 'from': - $class = 'Doctrine_Query_' . ucwords(strtolower($k)); - $parser = new $class($this); - $parser->parse($part); - break; - case 'set': - $class = 'Doctrine_Query_' . ucwords(strtolower($k)); - $parser = new $class($this); - $parser->parse($part); - break; - case 'group': - case 'order': - $k .= 'by'; - case 'where': - case 'having': - $class = 'Doctrine_Query_' . ucwords(strtolower($k)); - $parser = new $class($this); - - $name = strtolower($k); - $parser->parse($part); - break; - case 'limit': - $this->parts['limit'] = trim($part); - break; - case 'offset': - $this->parts['offset'] = trim($part); - break; - } - } - - return $this; - } - public function load($path, $loadFields = true) - { - // parse custom join conditions - $e = explode(' ON ', $path); - - $joinCondition = ''; - - if (count($e) > 1) { - $joinCondition = ' AND ' . $e[1]; - $path = $e[0]; - } - - $tmp = explode(' ', $path); - $originalAlias = (count($tmp) > 1) ? end($tmp) : null; - - $e = preg_split("/[.:]/", $tmp[0], -1); - - $fullPath = $tmp[0]; - $prevPath = ''; - $fullLength = strlen($fullPath); - - if (isset($this->_aliasMap[$e[0]])) { - $table = $this->_aliasMap[$e[0]]['table']; - - $prevPath = $parent = array_shift($e); - } - - foreach ($e as $key => $name) { - // get length of the previous path - $length = strlen($prevPath); - - // build the current component path - $prevPath = ($prevPath) ? $prevPath . '.' . $name : $name; - - $delimeter = substr($fullPath, $length, 1); - - // if an alias is not given use the current path as an alias identifier - if (strlen($prevPath) === $fullLength && isset($originalAlias)) { - $componentAlias = $originalAlias; - } else { - $componentAlias = $prevPath; - } - - if ( ! isset($table)) { - // process the root of the path - - $table = $this->loadRoot($name, $componentAlias); - } else { - $join = ($delimeter == ':') ? 'INNER JOIN ' : 'LEFT JOIN '; - - $relation = $table->getRelation($name); - - $this->_aliasMap[$componentAlias] = array('table' => $relation->getTable(), - 'parent' => $parent, - 'relation' => $relation); - if ( ! $relation->isOneToOne()) { - $this->needsSubquery = true; - } - - $localAlias = $this->getShortAlias($parent, $table->getTableName()); - $foreignAlias = $this->getShortAlias($componentAlias, $relation->getTable()->getTableName()); - $localSql = $this->conn->quoteIdentifier($table->getTableName()) . ' ' . $localAlias; - $foreignSql = $this->conn->quoteIdentifier($relation->getTable()->getTableName()) . ' ' . $foreignAlias; - - $map = $relation->getTable()->inheritanceMap; - - if ( ! $loadFields || ! empty($map) || $joinCondition) { - $this->subqueryAliases[] = $foreignAlias; - } - - if ($relation instanceof Doctrine_Relation_Association) { - $asf = $relation->getAssociationFactory(); - - $assocTableName = $asf->getTableName(); - - if( ! $loadFields || ! empty($map) || $joinCondition) { - $this->subqueryAliases[] = $assocTableName; - } - - $assocPath = $prevPath . '.' . $asf->getComponentName(); - - $assocAlias = $this->getShortAlias($assocPath, $asf->getTableName()); - - $queryPart = $join . $assocTableName . ' ' . $assocAlias . ' ON ' . $localAlias . '.' - . $table->getIdentifier() . ' = ' - . $assocAlias . '.' . $relation->getLocal(); - - if ($relation instanceof Doctrine_Relation_Association_Self) { - $queryPart .= ' OR ' . $localAlias . '.' . $table->getIdentifier() . ' = ' - . $assocAlias . '.' . $relation->getForeign(); - } - - $this->parts['from'][] = $queryPart; - - $queryPart = $join . $foreignSql . ' ON ' . $foreignAlias . '.' - . $relation->getTable()->getIdentifier() . ' = ' - . $assocAlias . '.' . $relation->getForeign() - . $joinCondition; - - if ($relation instanceof Doctrine_Relation_Association_Self) { - $queryPart .= ' OR ' . $foreignAlias . '.' . $table->getIdentifier() . ' = ' - . $assocAlias . '.' . $relation->getLocal(); - } - - } else { - - $queryPart = $join . $foreignSql - . ' ON ' . $localAlias . '.' - . $relation->getLocal() . ' = ' . $foreignAlias . '.' . $relation->getForeign() - . $joinCondition; - } - $this->parts['from'][] = $queryPart; - } - if ($loadFields) { - - $restoreState = false; - // load fields if necessary - if ($loadFields && empty($this->pendingFields) - && empty($this->pendingAggregates) - && empty($this->pendingSubqueries)) { - - $this->pendingFields[$componentAlias] = array('*'); - - $restoreState = true; - } - - if(isset($this->pendingFields[$componentAlias])) { - $this->processPendingFields($componentAlias); - } - - if(isset($this->pendingAggregates[$componentAlias]) || isset($this->pendingAggregates[0])) { - $this->processPendingAggregates($componentAlias); - } - - if ($restoreState) { - $this->pendingFields = array(); - $this->pendingAggregates = array(); - } - } - $parent = $prevPath; - } - return end($this->_aliasMap); - } - /** - * loadRoot - * - * @param string $name - * @param string $componentAlias - */ - public function loadRoot($name, $componentAlias) - { - // get the connection for the component - $this->conn = Doctrine_Manager::getInstance() - ->getConnectionForComponent($name); - - $table = $this->conn->getTable($name); - $tableName = $table->getTableName(); - - // get the short alias for this table - $tableAlias = $this->aliasHandler->getShortAlias($componentAlias, $tableName); - // quote table name - $queryPart = $this->conn->quoteIdentifier($tableName); - - if ($this->type === self::SELECT) { - $queryPart .= ' ' . $tableAlias; - } - - $this->parts['from'][] = $queryPart; - $this->tableAliases[$tableAlias] = $componentAlias; - $this->_aliasMap[$componentAlias] = array('table' => $table); - - return $table; - } - /** - * count - * fetches the count of the query - * - * This method executes the main query without all the - * selected fields, ORDER BY part, LIMIT part and OFFSET part. - * - * Example: - * Main query: - * SELECT u.*, p.phonenumber FROM User u - * LEFT JOIN u.Phonenumber p - * WHERE p.phonenumber = '123 123' LIMIT 10 - * - * The query this method executes: - * SELECT COUNT(DISTINCT u.id) FROM User u - * LEFT JOIN u.Phonenumber p - * WHERE p.phonenumber = '123 123' - * - * @param array $params an array of prepared statement parameters - * @return integer the count of this query - */ - public function count($params = array()) - { - // initialize temporary variables - $where = $this->parts['where']; - $having = $this->parts['having']; - $map = reset($this->_aliasMap); - $componentAlias = key($this->_aliasMap); - $table = $map['table']; - - // build the query base - $q = 'SELECT COUNT(DISTINCT ' . $this->aliasHandler->getShortAlias($table->getTableName()) - . '.' . $table->getIdentifier() - . ') FROM ' . $this->buildFromPart(); - - // append column aggregation inheritance (if needed) - $string = $this->applyInheritance(); - - if ( ! empty($string)) { - $where[] = $string; - } - // append conditions - $q .= ( ! empty($where)) ? ' WHERE ' . implode(' AND ', $where) : ''; - $q .= ( ! empty($having)) ? ' HAVING ' . implode(' AND ', $having): ''; - - if ( ! is_array($params)) { - $params = array($params); - } - // append parameters - $params = array_merge($this->params, $params); - - return (int) $this->getConnection()->fetchOne($q, $params); - } - /** - * isLimitSubqueryUsed - * whether or not limit subquery algorithm is used - * - * @return boolean - */ - public function isLimitSubqueryUsed() { - return $this->limitSubqueryUsed; - } - - /** - * query - * query the database with DQL (Doctrine Query Language) - * - * @param string $query DQL query - * @param array $params prepared statement parameters - * @see Doctrine::FETCH_* constants - * @return mixed - */ - public function query($query, $params = array()) - { - $this->parseQuery($query); - - return $this->execute($params); - } - /** - * getShortAlias - * some database such as Oracle need the identifier lengths to be < ~30 chars - * hence Doctrine creates as short identifier aliases as possible - * - * this method is used for the creation of short table aliases, its also - * smart enough to check if an alias already exists for given component (componentAlias) - * - * @param string $componentAlias the alias for the query component to search table alias for - * @param string $tableName the table name from which the table alias is being created - * @return string the generated / fetched short alias - */ - public function getShortAlias($componentAlias, $tableName) - { - return $this->aliasHandler->getShortAlias($componentAlias, $tableName); - } - /** - * addSelect - * adds fields to the SELECT part of the query - * - * @param string $select DQL SELECT part - * @return Doctrine_Query - */ - public function addSelect($select) - { - return $this->getParser('select')->parse($select, true); - } - /** - * addWhere - * adds conditions to the WHERE part of the query - * - * @param string $where DQL WHERE part - * @param mixed $params an array of parameters or a simple scalar - * @return Doctrine_Query - */ - public function addWhere($where, $params = array()) - { - if (is_array($params)) { - $this->params = array_merge($this->params, $params); - } else { - $this->params[] = $params; - } - return $this->getParser('where')->parse($where, true); - } - /** - * addGroupBy - * adds fields to the GROUP BY part of the query - * - * @param string $groupby DQL GROUP BY part - * @return Doctrine_Query - */ - public function addGroupBy($groupby) - { - return $this->getParser('groupby')->parse($groupby, true); - } - /** - * addHaving - * adds conditions to the HAVING part of the query - * - * @param string $having DQL HAVING part - * @param mixed $params an array of parameters or a simple scalar - * @return Doctrine_Query - */ - public function addHaving($having, $params = array()) - { - if (is_array($params)) { - $this->params = array_merge($this->params, $params); - } else { - $this->params[] = $params; - } - return $this->getParser('having')->parse($having, true); - } - /** - * addOrderBy - * adds fields to the ORDER BY part of the query - * - * @param string $orderby DQL ORDER BY part - * @return Doctrine_Query - */ - public function addOrderBy($orderby) - { - return $this->getParser('orderby')->parse($orderby, true); - } - /** - * select - * sets the SELECT part of the query - * - * @param string $select DQL SELECT part - * @return Doctrine_Query - */ - public function select($select) - { - return $this->getParser('select')->parse($select); - } - /** - * distinct - * Makes the query SELECT DISTINCT. - * - * @param bool $flag Whether or not the SELECT is DISTINCT (default true). - * @return Doctrine_Query - */ - public function distinct($flag = true) - { - $this->_parts['distinct'] = (bool) $flag; - - return $this; - } - - /** - * forUpdate - * Makes the query SELECT FOR UPDATE. - * - * @param bool $flag Whether or not the SELECT is FOR UPDATE (default true). - * @return Doctrine_Query - */ - public function forUpdate($flag = true) - { - $this->_parts[self::FOR_UPDATE] = (bool) $flag; - - return $this; - } - /** - * delete - * sets the query type to DELETE - * - * @return Doctrine_Query - */ - public function delete() - { - $this->type = self::DELETE; - - return $this; - } - /** - * update - * sets the UPDATE part of the query - * - * @param string $update DQL UPDATE part - * @return Doctrine_Query - */ - public function update($update) - { - $this->type = self::UPDATE; - - return $this->getParser('from')->parse($update); - } - /** - * set - * sets the SET part of the query - * - * @param string $update DQL UPDATE part - * @return Doctrine_Query - */ - public function set($key, $value) - { - return $this->getParser('set')->parse($key . ' = ' . $value); - } - /** - * from - * sets the FROM part of the query - * - * @param string $from DQL FROM part - * @return Doctrine_Query - */ - public function from($from) - { - return $this->getParser('from')->parse($from); - } - /** - * innerJoin - * appends an INNER JOIN to the FROM part of the query - * - * @param string $join DQL INNER JOIN - * @return Doctrine_Query - */ - public function innerJoin($join) - { - return $this->getParser('from')->parse('INNER JOIN ' . $join); - } - /** - * leftJoin - * appends a LEFT JOIN to the FROM part of the query - * - * @param string $join DQL LEFT JOIN - * @return Doctrine_Query - */ - public function leftJoin($join) - { - return $this->getParser('from')->parse('LEFT JOIN ' . $join); - } - /** - * groupBy - * sets the GROUP BY part of the query - * - * @param string $groupby DQL GROUP BY part - * @return Doctrine_Query - */ - public function groupBy($groupby) - { - return $this->getParser('groupby')->parse($groupby); - } - /** - * where - * sets the WHERE part of the query - * - * @param string $join DQL WHERE part - * @param mixed $params an array of parameters or a simple scalar - * @return Doctrine_Query - */ - public function where($where, $params = array()) - { - $this->params = (array) $params; - - return $this->getParser('where')->parse($where); - } - /** - * having - * sets the HAVING part of the query - * - * @param string $having DQL HAVING part - * @param mixed $params an array of parameters or a simple scalar - * @return Doctrine_Query - */ - public function having($having, $params) - { - $this->params = (array) $params; - - return $this->getParser('having')->parse($having); - } - /** - * orderBy - * sets the ORDER BY part of the query - * - * @param string $orderby DQL ORDER BY part - * @return Doctrine_Query - */ - public function orderBy($orderby) - { - return $this->getParser('orderby')->parse($orderby); - } - /** - * limit - * sets the DQL query limit - * - * @param integer $limit limit to be used for limiting the query results - * @return Doctrine_Query - */ - public function limit($limit) - { - return $this->getParser('limit')->parse($limit); - } - /** - * offset - * sets the DQL query offset - * - * @param integer $offset offset to be used for paginating the query - * @return Doctrine_Query - */ - public function offset($offset) - { - return $this->getParser('offset')->parse($offset); - } -} - diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index fd830eabf..d8f93be00 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -555,12 +555,20 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function processDiff() { - foreach (array_diff($this->_snapshot, $this->data) as $record) { + foreach (array_diff($this->_snapshot, $this->data) as $record) { $record->delete(); } return $this; } + public function getDeleteDiff() + { + return array_diff($this->_snapshot, $this->data); + } + public function getInsertDiff() + { + return array_diff($this->data, $this->_snapshot); + } /** * save * saves all records of this collection and processes the diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 4bb341690..9ae939e9f 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -785,8 +785,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } $tableObject->getRelations(); - - //$this->getTable('RelationTestChild')->getRelation('Children'); } $next = count($this->tables); } diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php index fc1b40298..83fa73f2a 100644 --- a/lib/Doctrine/Connection/UnitOfWork.php +++ b/lib/Doctrine/Connection/UnitOfWork.php @@ -30,7 +30,7 @@ Doctrine::autoload('Doctrine_Connection_Module'); * @version $Revision$ * @author Konsta Vesterinen */ -class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implements IteratorAggregate, Countable +class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module { /** * buildFlushTree @@ -163,6 +163,20 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implemen } } elseif ($fk instanceof Doctrine_Relation_Association) { + $assocTable = $fk->getAssociationTable(); + foreach ($v->getDeleteDiff() as $r) { + $query = 'DELETE FROM ' . $assocTable->getTableName() + . ' WHERE ' . $fk->getForeign() . ' = ?' + . ' AND ' . $fk->getLocal() . ' = ?'; + $this->query($r->getIncremented(), $record->getIncremented()); + } + foreach ($v->getInsertDiff as $r) { + $assocRecord = $assocTable->create(); + $assocRecord->set($fk->getForeign(), $r); + $assocRecord->set($fk->getLocal(), $record); + $assocRecord->save($this->conn); + } + $v->save($this->conn); } } @@ -186,8 +200,10 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implemen public function saveAssociations(Doctrine_Record $record) { foreach ($record->getTable()->getRelations() as $rel) { - $table = $rel->getTable(); - $alias = $rel->getAlias(); + $table = $rel->getTable(); + $alias = $rel->getAlias(); + + } } /** @@ -206,7 +222,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implemen $obj = $record->get($fk->getAlias()); $obj->delete($this->conn); break; - }; + } } } /** @@ -347,9 +363,4 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module implemen return true; } - public function getIterator() - { } - - public function count() - { } } diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index e6573608a..a2826d906 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -30,7 +30,7 @@ Doctrine::autoload('Doctrine_Hydrate'); * @version $Revision$ * @author Konsta Vesterinen */ -class Doctrine_Query extends Doctrine_Hydrate implements Countable +class Doctrine_Query extends Doctrine_Hydrate implements Countable { /** * @param array $subqueryAliases the table aliases needed in some LIMIT subqueries @@ -205,7 +205,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable */ public function parseSelect($dql) { - $refs = Doctrine_Query::bracketExplode($dql, ','); + $refs = Doctrine_Tokenizer::bracketExplode($dql, ','); foreach ($refs as $reference) { if (strpos($reference, '(') !== false) { @@ -237,7 +237,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable */ public function parseSubselect($reference) { - $e = Doctrine_Query::bracketExplode($reference, ' '); + $e = Doctrine_Tokenizer::bracketExplode($reference, ' '); $alias = $e[1]; if (count($e) > 2) { @@ -253,7 +253,7 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable } public function parseAggregateFunction2($func) { - $e = Doctrine_Query::bracketExplode($func, ' '); + $e = Doctrine_Tokenizer::bracketExplode($func, ' '); $func = $e[0]; $pos = strpos($func, '('); diff --git a/lib/Doctrine/Query/Check.php b/lib/Doctrine/Query/Check.php index babb35f82..546154f36 100644 --- a/lib/Doctrine/Query/Check.php +++ b/lib/Doctrine/Query/Check.php @@ -83,7 +83,7 @@ class Doctrine_Query_Check */ public function parseClause($dql) { - $parts = Doctrine_Query::sqlExplode($dql, ' AND '); + $parts = Doctrine_Tokenizer::sqlExplode($dql, ' AND '); if (count($parts) > 1) { $ret = array(); @@ -93,7 +93,7 @@ class Doctrine_Query_Check $r = implode(' AND ', $ret); } else { - $parts = Doctrine_Query::quoteExplode($dql, ' OR '); + $parts = Doctrine_Tokenizer::quoteExplode($dql, ' OR '); if (count($parts) > 1) { $ret = array(); foreach ($parts as $part) { diff --git a/lib/Doctrine/Relation/Association.php b/lib/Doctrine/Relation/Association.php index 9ce46a8ff..7215c843f 100644 --- a/lib/Doctrine/Relation/Association.php +++ b/lib/Doctrine/Relation/Association.php @@ -42,6 +42,10 @@ class Doctrine_Relation_Association extends Doctrine_Relation { return $this->definition['assocTable']; } + public function getAssociationTable() + { + return $this->definition['assocTable']; + } /** * processDiff * diff --git a/tests/HydrateTestCase.php b/tests/HydrateTestCase.php index e9293260e..58a4b35a2 100644 --- a/tests/HydrateTestCase.php +++ b/tests/HydrateTestCase.php @@ -19,12 +19,6 @@ * . */ - -require_once('../draft/new-core/Record.php'); -require_once('../draft/new-core/Hydrate.php'); -require_once('../draft/new-core/Query.php'); -require_once('../draft/new-core/Collection.php'); - /** * Doctrine_Hydrate_TestCase * @@ -36,8 +30,6 @@ require_once('../draft/new-core/Collection.php'); * @since 1.0 * @version $Revision$ */ - - class Doctrine_Hydrate_TestCase extends Doctrine_UnitTestCase { protected $testData1 = array( diff --git a/tests/RecordTestCase.php b/tests/RecordTestCase.php index 00da973f3..9b240262c 100644 --- a/tests/RecordTestCase.php +++ b/tests/RecordTestCase.php @@ -31,14 +31,14 @@ * @version $Revision$ */ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { - /** + public function prepareTables() { $this->tables[] = "enumTest"; $this->tables[] = "fieldNameTest"; $this->tables[] = "GzipTest"; parent::prepareTables(); } - */ + public function testIssetForPrimaryKey() { $this->assertTrue(isset($this->users[0]->id)); $this->assertTrue(isset($this->users[0]['id'])); @@ -50,7 +50,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { $this->assertFalse(isset($user['id'])); $this->assertFalse($user->contains('id')); } - /** + public function testUnknownColumn() { } @@ -299,14 +299,6 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { $this->assertEqual($user->name, null); } - - public function testDateTimeType() { - $date = new DateTest(); - - - } - - public function testSerialize() { $user = $this->connection->getTable("User")->find(4); $str = serialize($user); @@ -324,9 +316,6 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { $user->call('substr', 'name', 0, 1); $this->assertEqual($user->name, 'z'); } - - - public function testCompositePK() { $record = new EntityReference(); $this->assertEqual($record->getTable()->getIdentifier(), array("entity1","entity2")); @@ -711,8 +700,8 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { $this->assertEqual($new->loginname, 'jackd'); } - public function testReferences() { - + public function testReferences() + { $user = $this->objTable->find(5); $pf = $this->connection->getTable("Phonenumber"); @@ -946,13 +935,15 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { } - public function testCount() { + public function testCount() + { $user = $this->connection->getTable("User")->find(4); $this->assertTrue(is_integer($user->count())); } - public function testGetReference() { + public function testGetReference() + { $user = $this->connection->getTable("User")->find(4); $this->assertTrue($user->Email instanceof Doctrine_Record); @@ -961,10 +952,10 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase { $this->assertTrue($user->Phonenumber->count() == 1); } - public function testGetIterator() { + public function testGetIterator() + { $user = $this->connection->getTable("User")->find(4); $this->assertTrue($user->getIterator() instanceof ArrayIterator); } - */ } ?> diff --git a/tests/UnitTestCase.php b/tests/UnitTestCase.php index d99df14de..71a9e2d6b 100644 --- a/tests/UnitTestCase.php +++ b/tests/UnitTestCase.php @@ -228,10 +228,10 @@ class Doctrine_UnitTestCase extends UnitTestCase { } } public function setUp() { - if( ! $this->init) $this->init(); - - if(isset($this->objTable)) - $this->objTable->clear(); + if ( ! $this->init) { + $this->init(); + } + $this->objTable->clear(); $this->init = true; }