diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index e97d4d7e6..360fc8705 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -20,8 +20,9 @@ */ Doctrine::autoload('Doctrine_Access'); /** - * Doctrine_Collection - * Collection of Doctrine_Record objects. + * A Doctrine_Collection represents a collection of record objects. + * A collection object is strongly typed in the sense that it can only contain + * records of a specific type or one it's subtypes. * * @package Doctrine * @subpackage Collection @@ -34,57 +35,74 @@ Doctrine::autoload('Doctrine_Access'); class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate, Serializable { /** - * @var array $data an array containing the records of this collection + * An array containing the records of this collection. + * + * @var array */ protected $data = array(); + /** + * The mapper object used to map the records of this collection to the database. + * + * @var Doctrine_Mapper + */ protected $_mapper; /** - * @var array $_snapshot a snapshot of the fetched data + * A snapshot of the fetched data. + * + * @var array */ protected $_snapshot = array(); /** - * @var Doctrine_Record $reference collection can belong to a record + * This record this collection is attached to, if any. + * + * @var Doctrine_Record */ protected $reference; /** - * @var string $referenceField the reference field of the collection + * The reference field of the collection. + * + * @var string $referenceField */ protected $referenceField; /** - * @var Doctrine_Relation the record this collection is related to, if any + * The relation this collection is related to, if any. + * + * @var Doctrine_Relation */ protected $relation; /** - * @var string $keyColumn the name of the column that is used for collection key mapping + * The name of the column that is used for collection key mapping. + * + * @var string */ protected $keyColumn; /** - * @var Doctrine_Null $null used for extremely fast null value testing + * Helper variable. Used for fast null value testing. + * + * @var Doctrine_Null */ protected static $null; - /** - * constructor + * Constructor. + * Creates a new collection that will hold instances of the type that is + * specified through the mapper. That means if the mapper is a mapper for + * the entity "User", then the resulting collection will be a collection of + * user objects. * - * @param Doctrine_Table|string $table + * @param Doctrine_Mapper|string $mapper The mapper used by the collection. + * @param string $keyColumn The field name that will be used as the key + * in the collection. */ public function __construct($mapper, $keyColumn = null) { - if ($mapper instanceof Doctrine_Table) { - try { - throw new Exception(); - } catch (Exception $e) { - echo $e->getTraceAsString(); - } - } if (is_string($mapper)) { $mapper = Doctrine_Manager::getInstance()->getMapper($mapper); } @@ -105,7 +123,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator /** * initNullObject - * initializes the null object for this collection + * Initializes the null object for this collection. * * @return void */ @@ -116,7 +134,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator /** * getTable - * returns the table this collection belongs to + * Returns the table of the mapper of the collection. * * @return Doctrine_Table */ @@ -124,6 +142,17 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator { return $this->_mapper->getTable(); } + + /** + * getMapper + * Returns the mapper of this collection. + * + * @return Doctrine_Mapper + */ + public function getMapper() + { + return $this->_mapper; + } /** * setData @@ -137,7 +166,10 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * this method is automatically called when this Doctrine_Collection is serialized + * Serializes the collection. + * This method is automatically called when this Doctrine_Collection is serialized. + * + * Part of the implementation of the Serializable interface. * * @return array */ @@ -158,15 +190,17 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * unseralize - * this method is automatically called everytime a Doctrine_Collection object is unserialized + * Reconstitutes the collection object from it's serialized form. + * This method is automatically called everytime a Doctrine_Collection object is unserialized. + * + * Part of the implementation of the Serializable interface. * * @return void */ public function unserialize($serialized) { - $manager = Doctrine_Manager::getInstance(); - $connection = $manager->getCurrentConnection(); + $manager = Doctrine_Manager::getInstance(); + $connection = $manager->getCurrentConnection(); $array = unserialize($serialized); @@ -196,7 +230,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator public function setKeyColumn($column) { $this->keyColumn = $column; - return $this; } @@ -243,6 +276,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator { return end($this->data); } + /** * returns the last record in the collection * @@ -252,6 +286,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator { return end($this->data); } + /** * returns the current key * @@ -261,6 +296,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator { return key($this->data); } + /** * setReference * sets a reference pointer @@ -269,18 +305,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function setReference(Doctrine_Record $record, Doctrine_Relation $relation) { - /*try { - throw new Exception(); - } catch (Exception $e) { - echo "relation set on collection: " . get_class($relation) . "
"; - echo $e->getTraceAsString() . "
"; - }*/ $this->reference = $record; $this->relation = $relation; if ($relation instanceof Doctrine_Relation_ForeignKey || $relation instanceof Doctrine_Relation_LocalKey) { - $this->referenceField = $relation->getForeignFieldName(); $value = $record->get($relation->getLocalFieldName()); foreach ($this->data as $record) { @@ -331,6 +360,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator return isset($this->data[$key]); } + /** + * + */ public function search(Doctrine_Record $record) { return array_search($record, $this->data, true); @@ -367,6 +399,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $record->set($this->referenceField, $this->reference, false); } } + if ($key === null) { $this->data[] = $record; } else { @@ -374,7 +407,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } if (isset($this->keyColumn)) { - $record->set($this->keyColumn, $key); } @@ -385,7 +417,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * @return array an array containing all primary keys + * Returns the primary keys of all records in the collection. + * + * @return array An array containing all primary keys. */ public function getPrimaryKeys() { @@ -412,11 +446,11 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } /** - * count - * this class implements interface countable - * returns the number of records in this collection + * Returns the number of records in this collection. * - * @return integer + * Implementation of the Countable interface. + * + * @return integer The number of records in the collection. */ public function count() { @@ -434,7 +468,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator if (isset($this->referenceField)) { $record->set($this->referenceField, $this->reference, false); } - $this->data[$key] = $record; } @@ -455,7 +488,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator $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 ] @@ -495,7 +528,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator public function loadRelated($name = null) { $list = array(); - $query = new Doctrine_Query($this->_mapper->getConnection()); + $query = new Doctrine_Query($this->_mapper->getConnection()); if ( ! isset($name)) { foreach ($this->data as $record) { @@ -510,7 +543,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator return $query; } - $rel = $this->_mapper->getTable()->getRelation($name); + $rel = $this->_mapper->getTable()->getRelation($name); if ($rel instanceof Doctrine_Relation_LocalKey || $rel instanceof Doctrine_Relation_ForeignKey) { foreach ($this->data as $record) { @@ -525,9 +558,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } } - $dql = $rel->getRelationDql(count($list), 'collection'); - - $coll = $query->query($dql, $list); + $dql = $rel->getRelationDql(count($list), 'collection'); + $coll = $query->query($dql, $list); $this->populateRelated($name, $coll); } @@ -618,7 +650,6 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator public function takeSnapshot() { $this->_snapshot = $this->data; - return $this; } @@ -647,7 +678,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function processDiff() { - foreach (array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")) as $record) { + foreach (array_udiff($this->_snapshot, $this->data, array($this, "_compareRecords")) as $record) { $record->delete(); } return $this; @@ -663,9 +694,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator { $data = array(); foreach ($this as $key => $record) { - $key = $prefixKey ? get_class($record) . '_' .$key:$key; - $data[$key] = $record->toArray($deep, $prefixKey); } @@ -758,7 +787,7 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function getDeleteDiff() { - return array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")); + return array_udiff($this->_snapshot, $this->data, array($this, "_compareRecords")); } /** @@ -768,14 +797,14 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator */ public function getInsertDiff() { - return array_udiff($this->data, $this->_snapshot, array($this, "compareRecords")); + return array_udiff($this->data, $this->_snapshot, array($this, "_compareRecords")); } /** - * compareRecords - * Compares two records. To be used on _snapshot diffs using array_udiff + * _compareRecords + * Compares two records. To be used on _snapshot diffs using array_udiff. */ - protected function compareRecords($a, $b) + protected function _compareRecords($a, $b) { if ($a->getOid() == $b->getOid()) { return 0; @@ -786,8 +815,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator /** * save - * saves all records of this collection and processes the - * difference of the last snapshot and the current data + * Saves all records of this collection and processes the + * difference of the last snapshot and the current data. * * @param Doctrine_Connection $conn optional connection parameter * @return Doctrine_Collection diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 92dfa65e6..8053901d9 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -60,6 +60,9 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun */ protected $dbh; + /** + * + */ protected $_tableFactory; /** @@ -169,6 +172,12 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun 'Sqlite', 'Firebird' ); + + /** + * The query count. Represents the number of executed database queries by the connection. + * + * @var integer + */ protected $_count = 0; /** @@ -184,9 +193,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun throw new Doctrine_Connection_Exception('First argument should be an instance of PDO or implement Doctrine_Adapter_Interface'); } $this->dbh = $adapter; - $this->isConnected = true; - } else if (is_array($adapter)) { $this->pendingAttributes[Doctrine::ATTR_DRIVER_NAME] = $adapter['scheme']; @@ -235,7 +242,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun */ public function getAttribute($attribute) { - if ($attribute >= 100) { if ( ! isset($this->attributes[$attribute])) { return parent::getAttribute($attribute); @@ -353,14 +359,13 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } /** - * returns the database handler of which this connection uses + * returns the database handler which this connection uses * * @return PDO the database handler */ public function getDbh() { $this->connect(); - return $this->dbh; } @@ -372,13 +377,11 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun */ public function connect() { - if ($this->isConnected) { return false; } $event = new Doctrine_Event($this, Doctrine_Event::CONN_CONNECT); - $this->getListener()->preConnect($event); $e = explode(':', $this->options['dsn']); @@ -515,21 +518,19 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun * * @throws Doctrine_Connection_Exception if something went wrong at the database level * @param string $table The table to delete data from - * @param array $identifier An associateve array containing identifier column-value pairs. + * @param array $identifier An associateve array containing identifier fieldname-value pairs. * @return integer The number of affected rows */ public function delete(Doctrine_Table $table, array $identifier) { - $tmp = array(); - + $criteria = array(); foreach (array_keys($identifier) as $id) { - $tmp[] = $table->getColumnName($id) . ' = ?'; + $criteria[] = $table->getColumnName($id) . ' = ?'; } $query = 'DELETE FROM ' . $this->quoteIdentifier($table->getTableName()) - . ' WHERE ' . implode(' AND ', $tmp); - + . ' WHERE ' . implode(' AND ', $criteria); return $this->exec($query, array_values($identifier)); } @@ -573,7 +574,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun * Inserts a table row with specified data. * * @param string $table The table to insert data into. - * @param array $values An associateve array containing column-value pairs. + * @param array $fields An associateve array containing fieldname-value pairs. * @return mixed boolean false if empty value array was given, * otherwise returns the number of affected rows */ @@ -606,53 +607,9 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $query .= implode(', ', $a) . ')'; // prepare and execute the statement - + return $this->exec($query, array_values($fields)); - } - - /** - * @todo DESCRIBE WHAT THIS METHOD DOES, PLEASE! - */ - public function processSingleInsert(Doctrine_Record $record) - { - $fields = $record->getPrepared(); - if (empty($fields)) { - return false; - } - - $table = $record->getTable(); - $identifier = (array) $table->getIdentifier(); - - $seq = $record->getTable()->getOption('sequenceName'); - - if ( ! empty($seq)) { - $id = $this->sequence->nextId($seq); - $seqName = $table->getIdentifier(); - $fields[$seqName] = $id; - - $record->assignIdentifier($id); - } - - $this->insert($table, $fields); - - if (empty($seq) && count($identifier) == 1 && $identifier[0] == $table->getIdentifier() && - $table->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) { - - if (strtolower($this->getName()) == 'pgsql') { - $seq = $table->getTableName() . '_' . $identifier[0]; - } - - $id = $this->sequence->lastInsertId($seq); - - if ( ! $id) { - throw new Doctrine_Connection_Exception("Couldn't get last insert identifier."); - } - - $record->assignIdentifier($id); - } else { - $record->assignIdentifier(true); - } - } + } /** * Set the charset on the current connection @@ -661,11 +618,11 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun */ public function setCharset($charset) { - + return true; } /** - * Quote a string so it can be safely used as a table or column name + * Quote a string so it can be safely used as a table or column name. * * Delimiting style depends on which database driver is being used. * @@ -1032,7 +989,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun public function rethrowException(Exception $e, $invoker) { $event = new Doctrine_Event($this, Doctrine_Event::CONN_ERROR); - $this->getListener()->preError($event); $name = 'Doctrine_Connection_' . $this->driverName . '_Exception'; @@ -1079,7 +1035,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } /** - * Gets a mapper for the specified domain class that is used map instances of + * Gets a mapper for the specified domain class that is used to map instances of * the class between the relational database and their object representation. * * @return Doctrine_Mapper_Abstract The mapper object. @@ -1092,9 +1048,9 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $customMapperClass = $className . 'Mapper'; if (class_exists($customMapperClass, $this->getAttribute(Doctrine::ATTR_AUTOLOAD_TABLE_CLASSES)) && - in_array('Doctrine_Mapper', class_parents($customMapperClass))) { + in_array('Doctrine_Mapper_Abstract', class_parents($customMapperClass))) { $table = $this->getTable($className); - $mapper = new $customMapperClass($className, $this); + $mapper = new $customMapperClass($className, $table); } else { // instantiate correct mapper type $table = $this->getTable($className); @@ -1250,7 +1206,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun public function close() { $event = new Doctrine_Event($this, Doctrine_Event::CONN_CLOSE); - $this->getAttribute(Doctrine::ATTR_LISTENER)->preClose($event); $this->clear(); diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php index 09eac4d7f..3e9dd8ad3 100644 --- a/lib/Doctrine/Connection/UnitOfWork.php +++ b/lib/Doctrine/Connection/UnitOfWork.php @@ -73,7 +73,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module { $tree = array(); foreach ($tables as $k => $table) { - if ( ! ($table instanceof Doctrine_Mapper)) { + if ( ! ($table instanceof Doctrine_Mapper_Abstract)) { $table = $this->conn->getMapper($table); } $nm = $table->getComponentName(); @@ -281,7 +281,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module * @return boolean true on success, false on failure * @todo Move to Doctrine_Table (which will become Doctrine_Mapper). */ - public function delete(Doctrine_Record $record) + /*public function delete(Doctrine_Record $record) { if ( ! $this->_autoflush) { return true; @@ -331,7 +331,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module $this->conn->commit(); return true; - } + }*/ /** * @todo Description. See also the todo for deleteMultiple(). @@ -356,6 +356,8 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module }*/ /** + * DOESNT SEEM TO BE USED ANYWHERE. + * * deleteMultiple * deletes all records from the pending delete list * @@ -364,7 +366,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module * queries itself and sometimes it leaves the sql construction to Connection. * This should be changed. */ - public function deleteMultiple(array $records) + /*public function deleteMultiple(array $records) { foreach ($this->delete as $name => $deletes) { $record = false; @@ -414,7 +416,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module } } } - } + }*/ /** * saveRelated @@ -498,7 +500,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module * @throws PDOException if something went wrong at database level * @return void */ - public function deleteComposites(Doctrine_Record $record) + /*public function deleteComposites(Doctrine_Record $record) { foreach ($record->getTable()->getRelations() as $fk) { if ($fk->isComposite()) { @@ -509,7 +511,7 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module } } } - } + }*/ /** * saveAll @@ -554,22 +556,19 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module //echo "

flushin all.

"; // get the flush tree $tree = $this->buildFlushTree($this->conn->getMappers()); - //foreach ($tree as $name) echo $name . "
"; // save all records foreach ($tree as $name) { $mapper = $this->conn->getMapper($name); - foreach ($mapper->getRepository() as $record) { //echo $record->getOid() . "
"; - $mapper->save($record); + $mapper->saveSingleRecord($record); } } // save all associations foreach ($tree as $name) { $mapper = $this->conn->getMapper($name); - foreach ($mapper->getRepository() as $record) { $mapper->saveAssociations($record); } diff --git a/lib/Doctrine/Hydrator.php b/lib/Doctrine/Hydrator.php index 3651ad9ba..dedc099b3 100644 --- a/lib/Doctrine/Hydrator.php +++ b/lib/Doctrine/Hydrator.php @@ -106,6 +106,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract // Initialize foreach ($this->_queryComponents as $dqlAlias => $data) { + $data['mapper']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false); $componentName = $data['mapper']->getComponentName(); $listeners[$componentName] = $data['table']->getRecordListener(); $identifierMap[$dqlAlias] = array(); @@ -229,6 +230,11 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract $driver->flush(); + // re-enable lazy loading + foreach ($this->_queryComponents as $dqlAlias => $data) { + $data['mapper']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, true); + } + //$e = microtime(true); //echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records
'; @@ -290,8 +296,9 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract $e = explode('__', $key); $last = strtolower(array_pop($e)); $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))]; - $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; - $fieldName = $table->getFieldName($last); + $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; + $table = $mapper->getTable(); + $fieldName = $mapper->getFieldName($last); $cache[$key]['fieldName'] = $fieldName; if ($table->isIdentifier($fieldName)) { $cache[$key]['isIdentifier'] = true; @@ -306,8 +313,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract } } - $map = $this->_queryComponents[$cache[$key]['dqlAlias']]; - $mapper = $map['mapper']; + $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; $dqlAlias = $cache[$key]['dqlAlias']; $fieldName = $cache[$key]['fieldName']; diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 106170a00..f0ec5f233 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -37,18 +37,22 @@ class Doctrine_Hydrator_ArrayDriver { return array(); } + public function getElement(array $data, $component) { return $data; } + public function isIdentifiable(array $data, Doctrine_Table $table) { return ( ! empty($data)); } + public function registerCollection($coll) { } + public function initRelated(array &$data, $name) { if ( ! isset($data[$name])) { @@ -56,10 +60,12 @@ class Doctrine_Hydrator_ArrayDriver } return true; } + public function getNullPointer() { return null; } + public function getLastKey(&$data) { end($data); diff --git a/lib/Doctrine/Hydrator/RecordDriver.php b/lib/Doctrine/Hydrator/RecordDriver.php index c77e03c6d..31e7ee3d7 100644 --- a/lib/Doctrine/Hydrator/RecordDriver.php +++ b/lib/Doctrine/Hydrator/RecordDriver.php @@ -20,8 +20,8 @@ */ /** - * Doctrine_Hydrate_Record - * defines a record fetching strategy for Doctrine_Hydrate + * Doctrine_Hydrate_RecordDriver + * Hydration strategy used for creating collections of entity objects. * * @package Doctrine * @subpackage Hydrate @@ -30,14 +30,13 @@ * @since 1.0 * @version $Revision$ * @author Konsta Vesterinen + * @author Roman Borschel */ class Doctrine_Hydrator_RecordDriver extends Doctrine_Locator_Injectable { protected $_collections = array(); - protected $_records = array(); - - protected $_tables = array(); + protected $_mappers = array(); public function getElementCollection($component) { @@ -105,13 +104,12 @@ class Doctrine_Hydrator_RecordDriver extends Doctrine_Locator_Injectable public function getElement(array $data, $component) { - if ( ! isset($this->_tables[$component])) { - $this->_tables[$component] = Doctrine_Manager::getInstance()->getMapper($component); - $this->_tables[$component]->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false); + if ( ! isset($this->_mappers[$component])) { + $this->_mappers[$component] = Doctrine_Manager::getInstance()->getMapper($component); } - $this->_tables[$component]->setData($data); - $record = $this->_tables[$component]->getRecord(); + $component = $this->_getClassnameToReturn($data, $component); + $record = $this->_mappers[$component]->getRecord($data); if ( ! isset($this->_records[$record->getOid()]) ) { $record->clearRelated(); @@ -127,8 +125,38 @@ class Doctrine_Hydrator_RecordDriver extends Doctrine_Locator_Injectable foreach ($this->_collections as $key => $coll) { $coll->takeSnapshot(); } - foreach ($this->_tables as $table) { - $table->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, true); + } + + /** + * Check the dataset for a discriminator column, to determine the correct + * class to instantiate. If no discriminator column is found, the given + * classname will be returned. + * + * @todo this function could use reflection to check the first time it runs + * if the subclassing option is not set. + * + * @return string The name of the class to instantiate. + * + */ + protected function _getClassnameToReturn(array $data, $className) + { + $subClasses = $this->_mappers[$className]->getTable()->getOption('subclasses'); + if ( ! isset($subClasses)) { + return $className; } + + foreach ($subClasses as $subclass) { + if ( ! isset($this->_mappers[$subclass])) { + $this->_mappers[$subclass] = Doctrine_Manager::getInstance()->getMapper($subclass); + } + $mapper = $this->_mappers[$subclass]; + $inheritanceMap = $mapper->getDiscriminatorColumn(); + foreach ($inheritanceMap as $key => $value) { + if (isset($data[$key]) && $data[$key] == $value) { + return $mapper->getComponentName(); + } + } + } + return $className; } } diff --git a/lib/Doctrine/Manager.php b/lib/Doctrine/Manager.php index da56ae8b4..f2c315fe2 100644 --- a/lib/Doctrine/Manager.php +++ b/lib/Doctrine/Manager.php @@ -255,7 +255,7 @@ class Doctrine_Manager extends Doctrine_Configurable implements Countable, Itera } $driverName = $adapter->getAttribute(Doctrine::ATTR_DRIVER_NAME); - } elseif (is_array($adapter)) { + } else if (is_array($adapter)) { if ( ! isset($adapter[0])) { throw new Doctrine_Manager_Exception('Empty data source name given.'); } diff --git a/lib/Doctrine/Mapper.php b/lib/Doctrine/Mapper/Abstract.php similarity index 80% rename from lib/Doctrine/Mapper.php rename to lib/Doctrine/Mapper/Abstract.php index bcdd2da6b..515ee8179 100644 --- a/lib/Doctrine/Mapper.php +++ b/lib/Doctrine/Mapper/Abstract.php @@ -31,7 +31,8 @@ * @link www.phpdoctrine.org * @since 1.0 */ -class Doctrine_Mapper extends Doctrine_Configurable implements Countable +abstract class Doctrine_Mapper_Abstract extends Doctrine_Configurable + implements Countable { /** * @var Doctrine_Table Metadata container that represents the database table this @@ -48,13 +49,6 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable * The names of all the fields that are available on entities created by this mapper. */ protected $_fieldNames = array(); - - /** - * Temporary data which is then loaded into Doctrine_Record::$_data. - * - * @var array $data - */ - protected $_data = array(); /** * The Doctrine_Connection object that the database connection of this mapper. @@ -372,10 +366,9 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable * * @return Doctrine_Record */ - public function getRecord() + public function getRecord(array $data) { - if ( ! empty($this->_data)) { - + if ( ! empty($data)) { $identifierFieldNames = $this->_table->getIdentifier(); if ( ! is_array($identifierFieldNames)) { @@ -384,18 +377,19 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable $found = false; foreach ($identifierFieldNames as $fieldName) { - if ( ! isset($this->_data[$fieldName])) { + if ( ! isset($data[$fieldName])) { // primary key column not found return new record $found = true; break; } - $id[] = $this->_data[$fieldName]; + $id[] = $data[$fieldName]; } if ($found) { - $recordName = $this->getClassnameToReturn(); - $record = new $recordName($this, true); - $this->_data = array(); + //$recordName = $this->getClassnameToReturn(); + //$record = new $recordName($this, true); + $record = new $this->_domainClassName($this, true, $data); + $data = array(); return $record; } @@ -404,16 +398,18 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable if (isset($this->_identityMap[$id])) { $record = $this->_identityMap[$id]; - $record->hydrate($this->_data); + $record->hydrate($data); } else { - $recordName = $this->getClassnameToReturn(); - $record = new $recordName($this); + //$recordName = $this->getClassnameToReturn(); + //$record = new $recordName($this); + $record = new $this->_domainClassName($this, false, $data); $this->_identityMap[$id] = $record; } - $this->_data = array(); + $data = array(); } else { - $recordName = $this->getClassnameToReturn(); - $record = new $recordName($this, true); + //$recordName = $this->getClassnameToReturn(); + //$record = new $recordName($this, true); + $record = new $this->_domainClassName($this, true, $data); } return $record; @@ -435,7 +431,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable * @return string The name of the class to create * */ - public function getClassnameToReturn() + /*public function getClassnameToReturn() { $subClasses = $this->_table->getOption('subclasses'); if ( ! isset($subClasses)) { @@ -457,7 +453,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable } } return $this->_domainClassName; - } + }*/ /** * @param $id database row id @@ -472,15 +468,15 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable . ' WHERE ' . implode(' = ? && ', $identifierColumnNames) . ' = ?'; $query = $this->applyInheritance($query); - $params = array_merge(array($id), array_values($this->getDiscriminatorColumn($this->_domainClassName))); + $params = array_merge(array($id), array_values($this->getDiscriminatorColumn())); - $this->_data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC); + $data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC); - if ($this->_data === false) { + if ($data === false) { return false; } } - return $this->getRecord(); + return $this->getRecord($data); } /** @@ -490,7 +486,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable */ final public function applyInheritance($where) { - $inheritanceMap = $this->getDiscriminatorColumn($this->_domainClassName); + $inheritanceMap = $this->getDiscriminatorColumn(); if ( ! empty($inheritanceMap)) { $a = array(); foreach ($inheritanceMap as $field => $value) { @@ -523,30 +519,6 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable return $graph; } - /** - * setData - * doctrine uses this function internally - * users are strongly discouraged to use this function - * - * @param array $data internal data - * @return void - */ - public function setData(array $data) - { - $this->_data = $data; - } - - /** - * returns internal data, used by Doctrine_Record instances - * when retrieving data from database - * - * @return array - */ - public function getData() - { - return $this->_data; - } - /** * prepareValue * this method performs special data preparation depending on @@ -728,12 +700,18 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable } /** + * Saves an entity and all it's related entities. * + * @param Doctrine_Record $record The entity to save. + * @param Doctrine_Connection $conn The connection to use. Will default to the mapper's + * connection. + * @throws Doctrine_Mapper_Exception If the mapper is unable to save the given record. */ - public function saveGraph(Doctrine_Record $record, Doctrine_Connection $conn = null) + public function save(Doctrine_Record $record, Doctrine_Connection $conn = null) { - if ($this->_domainClassName != get_class($record)) { - echo "mismatch: " . $this->_domainClassName . " <-> " . get_class($record) . "
"; + if ( ! ($record instanceof $this->_domainClassName)) { + throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . " + can't save instances of type" . get_class($record) . "."); } if ($conn === null) { @@ -798,9 +776,8 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable // save the MANY-TO-MANY associations $this->saveAssociations($record); - + // reset state $record->state($state); - $conn->commit(); } catch (Exception $e) { // save() calls can be nested recursively and exceptions bubble up, so check @@ -815,14 +792,64 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable return true; } + /** + * Inserts a single entity into the database, without any related entities. + * + * @param Doctrine_Record $record The entity to insert. + */ + protected function insertSingleRecord(Doctrine_Record $record) + { + $fields = $record->getPrepared(); + if (empty($fields)) { + return false; + } + + $table = $record->getTable(); + $identifier = (array) $table->getIdentifier(); + + $seq = $table->getOption('sequenceName'); + if ( ! empty($seq)) { + $id = $this->_conn->sequence->nextId($seq); + $seqName = $table->getIdentifier(); + $fields[$seqName] = $id; + $record->assignIdentifier($id); + } + + $this->_conn->insert($table, $fields); + + if (empty($seq) && count($identifier) == 1 && $identifier[0] == $table->getIdentifier() && + $table->getIdentifierType() != Doctrine::IDENTIFIER_NATURAL) { + + if (strtolower($this->getName()) == 'pgsql') { + $seq = $table->getTableName() . '_' . $identifier[0]; + } + + $id = $this->_conn->sequence->lastInsertId($seq); + + if ( ! $id) { + throw new Doctrine_Connection_Exception("Couldn't get last insert identifier."); + } + + $record->assignIdentifier($id); + } else { + $record->assignIdentifier(true); + } + } + + protected function _fireEvent($type, $callback, $invoker) + { + + } + /** * saves the given record * * @param Doctrine_Record $record * @return void */ - public function save(Doctrine_Record $record) + public function saveSingleRecord(Doctrine_Record $record) { + //$this->_fireEvent(Doctrine_Event::RECORD_SAVE, 'preSave', $record); $event = new Doctrine_Event($record, Doctrine_Event::RECORD_SAVE); $record->preSave($event); $this->getRecordListener()->preSave($event); @@ -925,7 +952,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable $assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r); $assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record); - $this->save($assocRecord); + $this->saveSingleRecord($assocRecord); } } } @@ -938,7 +965,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable * @return boolean whether or not the update was successful * @todo Move to Doctrine_Table (which will become Doctrine_Mapper). */ - public function update(Doctrine_Record $record) + protected function update(Doctrine_Record $record) { $event = new Doctrine_Event($record, Doctrine_Event::RECORD_UPDATE); $record->preUpdate($event); @@ -946,10 +973,7 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable $this->getRecordListener()->preUpdate($event); if ( ! $event->skipOperation) { - $identifier = $record->identifier(); - $array = $record->getPrepared(); - $this->_conn->update($table, $array, $identifier); - $record->assignIdentifier(true); + $this->_doUpdate($record); } $this->getRecordListener()->postUpdate($event); @@ -958,22 +982,29 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable return true; } + protected function _doUpdate(Doctrine_Record $record) + { + $identifier = $record->identifier(); + $array = $record->getPrepared(); + $this->_conn->update($this->_table, $array, $identifier); + $record->assignIdentifier(true); + } + /** * inserts a record into database * * @param Doctrine_Record $record record to be inserted * @return boolean - * @todo Move to Doctrine_Table (which will become Doctrine_Mapper). */ - public function insert(Doctrine_Record $record) - { + protected function insert(Doctrine_Record $record) + { // trigger event $event = new Doctrine_Event($record, Doctrine_Event::RECORD_INSERT); $record->preInsert($event); $this->getRecordListener()->preInsert($event); if ( ! $event->skipOperation) { - $this->_conn->processSingleInsert($record); + $this->_doInsert($record); } // trigger event @@ -984,9 +1015,95 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable return true; } - public function delete(Doctrine_Record $record) + protected function _doInsert(Doctrine_Record $record) { + $this->insertSingleRecord($record); + } + + /** + * deletes given record and all the related composites + * this operation is isolated by a transaction + * + * this event can be listened by the onPreDelete and onDelete listeners + * + * @return boolean true on success, false on failure + * @todo Move to Doctrine_Table (which will become Doctrine_Mapper). + */ + public function delete(Doctrine_Record $record, Doctrine_Connection $conn = null) + { + if ( ! $record->exists()) { + return false; + } + if ( ! ($record instanceof $this->_domainClassName)) { + throw new Doctrine_Mapper_Exception("Mapper of type " . $this->_domainClassName . " + can't save instances of type" . get_class($record) . "."); + } + + if ($conn == null) { + $conn = $this->_conn; + } + + $event = new Doctrine_Event($record, Doctrine_Event::RECORD_DELETE); + $record->preDelete($event); + $this->getRecordListener()->preDelete($event); + + $table = $this->_table; + + $state = $record->state(); + $record->state(Doctrine_Record::STATE_LOCKED); + + if ( ! $event->skipOperation) { + $this->_doDelete($record, $conn); + } else { + // return to original state + $record->state($state); + } + + $this->getRecordListener()->postDelete($event); + $record->postDelete($event); + + return true; + } + + protected function _doDelete(Doctrine_Record $record, Doctrine_Connection $conn) + { + try { + $conn->beginInternalTransaction(); + $this->deleteComposites($record); + + $record->state(Doctrine_Record::STATE_TDIRTY); + + $conn->delete($this->_table, $record->identifier()); + $record->state(Doctrine_Record::STATE_TCLEAN); + + $this->removeRecord($record); + $conn->commit(); + } catch (Exception $e) { + $conn->rollback(); + throw $e; + } + + } + + /** + * deletes all related composites + * this method is always called internally when a record is deleted + * + * @throws PDOException if something went wrong at database level + * @return void + */ + protected function deleteComposites(Doctrine_Record $record) + { + foreach ($this->_table->getRelations() as $fk) { + if ($fk->isComposite()) { + $obj = $record->get($fk->getAlias()); + if ($obj instanceof Doctrine_Record && + $obj->state() != Doctrine_Record::STATE_LOCKED) { + $obj->delete($this->_conn); + } + } + } } public function executeQuery(Doctrine_Query $query) @@ -998,15 +1115,10 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable { return $this->_table; } - - public function getCustomJoins() + + public function getFieldName($columnName) { - return array(); - } - - public function getDiscriminatorColumn($domainClassName) - { - return array(); + return $this->_table->getFieldName($columnName); } public function getFieldNames() @@ -1014,15 +1126,14 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable if ($this->_fieldNames) { return $this->_fieldNames; } - $this->_fieldNames = $this->_table->getFieldNames(); - return $this->_table->getFieldNames(); + return $this->_fieldNames; } - /*public function free() + public function getOwningTable($fieldName) { - unset($this->_table); - }*/ + return $this->_table; + } public function getIdentityMap() { @@ -1034,4 +1145,31 @@ class Doctrine_Mapper extends Doctrine_Configurable implements Countable var_dump($this->_invokedMethods); } + + /*public function addToWhere($componentAlias, array &$sqlWhereParts, Doctrine_Query $query) + { + + } + + public function addToFrom($sqlString, Doctrine_Query $query) + { + + }*/ + + /* Hooks used during SQL query construction to manipulate the query. */ + + public function getCustomJoins() + { + return array(); + } + + public function getCustomFields() + { + return array(); + } + + public function getDiscriminatorColumn() + { + return array(); + } } diff --git a/lib/Doctrine/Mapper/Joined.php b/lib/Doctrine/Mapper/Joined.php index 5c02fb47c..06a03799e 100644 --- a/lib/Doctrine/Mapper/Joined.php +++ b/lib/Doctrine/Mapper/Joined.php @@ -1,7 +1,8 @@ _table; @@ -19,19 +20,39 @@ class Doctrine_Mapper_Joined extends Doctrine_Mapper $classes = $table->getOption('joinedParents'); array_unshift($classes, $component); - - foreach (array_reverse($classes) as $k => $parent) { - if ($k === 0) { - $rootRecord = new $parent(); - $rootRecord->merge($dataSet[$parent]); - parent::insert($rootRecord); - $record->assignIdentifier($rootRecord->identifier()); - } else { - foreach ((array) $rootRecord->identifier() as $id => $value) { - $dataSet[$parent][$id] = $value; + + try { + $this->_conn->beginInternalTransaction(); + $identifier = null; + foreach (array_reverse($classes) as $k => $parent) { + $parentTable = $this->_conn->getTable($parent); + if ($k == 0) { + $identifierType = $parentTable->getIdentifierType(); + if ($identifierType == Doctrine::IDENTIFIER_AUTOINC) { + $this->_conn->insert($parentTable, $dataSet[$parent]); + $identifier = $this->_conn->sequence->lastInsertId(); + } else if ($identifierType == Doctrine::IDENTIFIER_SEQUENCE) { + $seq = $record->getTable()->getOption('sequenceName'); + if ( ! empty($seq)) { + $identifier = $this->_conn->sequence->nextId($seq); + $dataSet[$parent][$parentTable->getIdentifier()] = $identifier; + $this->_conn->insert($parentTable, $dataSet[$parent]); + } + } else { + throw new Doctrine_Mapper_Exception("Unsupported identifier type '$identifierType'."); + } + $record->assignIdentifier($identifier); + } else { + foreach ((array) $record->identifier() as $id => $value) { + $dataSet[$parent][$id] = $value; + } + $this->_conn->insert($parentTable, $dataSet[$parent]); } - $this->_conn->insert($this->_conn->getTable($parent), $dataSet[$parent]); } + $this->_conn->commit(); + } catch (Exception $e) { + $this->_conn->rollback(); + throw $e; } return true; @@ -44,39 +65,58 @@ class Doctrine_Mapper_Joined extends Doctrine_Mapper * @return boolean whether or not the update was successful * @todo Move to Doctrine_Table (which will become Doctrine_Mapper). */ - public function update(Doctrine_Record $record) + protected function _doUpdate(Doctrine_Record $record) { - $event = new Doctrine_Event($record, Doctrine_Event::RECORD_UPDATE); - $record->preUpdate($event); $table = $this->_table; - $this->getRecordListener()->preUpdate($event); + $identifier = $record->identifier(); + $dataSet = $this->_formatDataSet($record); + $component = $table->getComponentName(); + $classes = $table->getOption('joinedParents'); + array_unshift($classes, $component); - if ( ! $event->skipOperation) { - $identifier = $record->identifier(); - $dataSet = $this->_formatDataSet($record); - $component = $table->getComponentName(); - $classes = $table->getOption('joinedParents'); - array_unshift($classes, $component); - - foreach ($record as $field => $value) { - if ($value instanceof Doctrine_Record) { - if ( ! $value->exists()) { - $value->save(); - } - $record->set($field, $value->getIncremented()); + foreach ($record as $field => $value) { + if ($value instanceof Doctrine_Record) { + if ( ! $value->exists()) { + $value->save(); } + $record->set($field, $value->getIncremented()); } + } - foreach (array_reverse($classes) as $class) { - $parentTable = $this->_conn->getTable($class); - $this->_conn->update($parentTable, $dataSet[$class], $identifier); - } - - $record->assignIdentifier(true); + foreach (array_reverse($classes) as $class) { + $parentTable = $this->_conn->getTable($class); + $this->_conn->update($parentTable, $dataSet[$class], $identifier); } - $this->getRecordListener()->postUpdate($event); - $record->postUpdate($event); + $record->assignIdentifier(true); + + return true; + } + + + protected function _doDelete(Doctrine_Record $record, Doctrine_Connection $conn) + { + try { + $table = $this->_table; + $conn->beginInternalTransaction(); + $this->deleteComposites($record); + + $record->state(Doctrine_Record::STATE_TDIRTY); + + foreach ($table->getOption('joinedParents') as $parent) { + $parentTable = $conn->getTable($parent); + $conn->delete($parentTable, $record->identifier()); + } + + $conn->delete($table, $record->identifier()); + $record->state(Doctrine_Record::STATE_TCLEAN); + + $this->removeRecord($record); + $conn->commit(); + } catch (Exception $e) { + $conn->rollback(); + throw $e; + } return true; } @@ -87,13 +127,33 @@ class Doctrine_Mapper_Joined extends Doctrine_Mapper */ public function getCustomJoins() { - return $this->_table->getOption('joinedParents'); + $customJoins = array(); + foreach ($this->_table->getOption('joinedParents') as $parentClass) { + $customJoins[$parentClass] = 'INNER'; + } + foreach ((array)$this->_table->getOption('subclasses') as $subClass) { + if ($subClass != $this->_domainClassName) { + $customJoins[$subClass] = 'LEFT'; + } + } + return $customJoins; + } + + public function getCustomFields() + { + $fields = array(); + if ($this->_table->getOption('subclasses')) { + foreach ($this->_table->getOption('subclasses') as $subClass) { + $fields = array_merge($this->_conn->getTable($subClass)->getFieldNames(), $fields); + } + } + return array_unique($fields); } /** * */ - public function getDiscriminatorColumn($domainClassName) + public function getDiscriminatorColumn() { $joinedParents = $this->_table->getOption('joinedParents'); if (count($joinedParents) <= 0) { @@ -101,7 +161,7 @@ class Doctrine_Mapper_Joined extends Doctrine_Mapper } else { $inheritanceMap = $this->_conn->getTable(array_pop($joinedParents))->getOption('inheritanceMap'); } - return isset($inheritanceMap[$domainClassName]) ? $inheritanceMap[$domainClassName] : array(); + return isset($inheritanceMap[$this->_domainClassName]) ? $inheritanceMap[$this->_domainClassName] : array(); } /** @@ -115,14 +175,67 @@ class Doctrine_Mapper_Joined extends Doctrine_Mapper $fieldNames = $this->_table->getFieldNames(); foreach ($this->_table->getOption('joinedParents') as $parent) { - $fieldNames = array_merge($this->_conn->getTable($parent)->getFieldNames(), - $fieldNames); + $parentTable = $this->_conn->getTable($parent); + $fieldNames = array_merge($parentTable->getFieldNames(), $fieldNames); } - $this->_fieldNames = $fieldNames; + $this->_fieldNames = array_unique($fieldNames); return $fieldNames; } + public function getFieldName($columnName) + { + if (isset($this->_columnNameFieldNameMap[$columnName])) { + return $this->_columnNameFieldNameMap[$columnName]; + } + + if ($this->_table->hasColumn($columnName)) { + $this->_columnNameFieldNameMap[$columnName] = $this->_table->getFieldName($columnName); + return $this->_columnNameFieldNameMap[$columnName]; + } + + foreach ($this->_table->getOption('joinedParents') as $parentClass) { + $parentTable = $this->_conn->getTable($parentClass); + if ($parentTable->hasColumn($columnName)) { + $this->_columnNameFieldNameMap[$columnName] = $parentTable->getFieldName($columnName); + return $this->_columnNameFieldNameMap[$columnName]; + } + } + + foreach ((array)$this->_table->getOption('subclasses') as $subClass) { + $subTable = $this->_conn->getTable($subClass); + if ($subTable->hasColumn($columnName)) { + $this->_columnNameFieldNameMap[$columnName] = $subTable->getFieldName($columnName); + return $this->_columnNameFieldNameMap[$columnName]; + } + } + + throw new Doctrine_Mapper_Exception("No field name found for column name '$columnName'."); + } + + public function getOwningTable($fieldName) + { + if ($this->_table->hasField($fieldName)) { + return $this->_table; + } + + foreach ($this->_table->getOption('joinedParents') as $parentClass) { + $parentTable = $this->_conn->getTable($parentClass); + if ($parentTable->hasField($fieldName)) { + return $parentTable; + } + } + + foreach ((array)$this->_table->getOption('subclasses') as $subClass) { + $subTable = $this->_conn->getTable($subClass); + if ($subTable->hasField($fieldName)) { + return $subTable; + } + } + + throw new Doctrine_Mapper_Exception("Unable to find owner of field '$fieldName'."); + } + /** * */ diff --git a/lib/Doctrine/Mapper/SingleTable.php b/lib/Doctrine/Mapper/SingleTable.php index fb77d84cd..5ab32d4b6 100644 --- a/lib/Doctrine/Mapper/SingleTable.php +++ b/lib/Doctrine/Mapper/SingleTable.php @@ -1,15 +1,63 @@ _table->getOption('inheritanceMap'); - return isset($inheritanceMap[$domainClassName]) ? $inheritanceMap[$domainClassName] : array(); + return isset($inheritanceMap[$this->_domainClassName]) ? $inheritanceMap[$this->_domainClassName] : array(); } - - + /*public function addToWhere($componentAlias, array &$sqlWhereParts, Doctrine_Query $query) + { + $array = array(); + $componentParts = $query->getQueryComponent($componentAlias); + $sqlTableAlias = $query->getSqlTableAlias($componentAlias); + $array[$sqlTableAlias][] = $this->getDiscriminatorColumn(); + + // 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 ($query->getType() !== Doctrine_Query::SELECT) { + $tableAlias = ''; + } else { + $tableAlias .= '.'; + } + + foreach ($maps as $map) { + $b = array(); + foreach ($map as $field => $value) { + $identifier = $this->_conn->quoteIdentifier($tableAlias . $field); + + if ($index > 0) { + $b[] = '(' . $identifier . ' = ' . $this->_conn->quote($value) + . ' OR ' . $identifier . ' IS NULL)'; + } else { + $b[] = $identifier . ' = ' . $this->_conn->quote($value); + } + } + + if ( ! empty($b)) { + $a[] = implode(' AND ', $b); + } + } + + if ( ! empty($a)) { + $c[] = implode(' AND ', $a); + } + $index++; + } + + $str .= implode(' AND ', $c); + + return $str; + }*/ } diff --git a/lib/Doctrine/Mapper/TablePerClass.php b/lib/Doctrine/Mapper/TablePerClass.php index ab34412b8..d4851fbba 100644 --- a/lib/Doctrine/Mapper/TablePerClass.php +++ b/lib/Doctrine/Mapper/TablePerClass.php @@ -1,6 +1,6 @@ _state === self::STATE_LOCKED) { - throw new Doctrine_Query_Exception('This query object is locked. No query parts can be manipulated.'); - } - - // sanity check - if ($queryPart === '' || $queryPart === null) { - throw new Doctrine_Query_Exception('Empty ' . $queryPartName . ' part given.'); - } - - // add query part to the dql part array - if ($append) { - $this->_dqlParts[$queryPartName][] = $queryPart; - } else { - $this->_dqlParts[$queryPartName] = array($queryPart); - } - - if ($this->_state === self::STATE_DIRECT) { - $parser = $this->_getParser($queryPartName); - - $sql = $parser->parse($queryPart); - - if (isset($sql)) { - if ($append) { - $this->addSqlQueryPart($queryPartName, $sql); - } else { - $this->setSqlQueryPart($queryPartName, $sql); - } - } - } - - $this->_state = Doctrine_Query::STATE_DIRTY; - - return $this; - }*/ - + /** * getDqlPart * returns a specific DQL query part. @@ -480,8 +432,9 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria */ public function processPendingFields($componentAlias) { - $tableAlias = $this->getTableAlias($componentAlias); - $table = $this->_queryComponents[$componentAlias]['table']; + $tableAlias = $this->getSqlTableAlias($componentAlias); + $baseTable = $this->_queryComponents[$componentAlias]['table']; + $mapper = $this->_queryComponents[$componentAlias]['mapper']; if ( ! isset($this->_pendingFields[$componentAlias])) { return; @@ -489,41 +442,39 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $fields = $this->_pendingFields[$componentAlias]; - // check for wildcards if (in_array('*', $fields)) { - //echo "
";Doctrine::dump($table->getColumnNames()); echo "
"; - $fields = $table->getFieldNames(); + $fields = $mapper->getFieldNames(); } 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((array) $table->getIdentifier(), $fields)); + $fields = array_unique(array_merge((array) $baseTable->getIdentifier(), $fields)); } } + $fields = array_unique(array_merge($fields, $mapper->getCustomFields())); + $sql = array(); foreach ($fields as $fieldName) { - $columnName = $table->getColumnName($fieldName); - if (($owner = $table->getColumnOwner($columnName)) !== null && - $owner !== $table->getComponentName()) { - $parent = $this->_conn->getTable($owner); - $columnName = $parent->getColumnName($fieldName); - $parentAlias = $this->getTableAlias($componentAlias . '.' . $parent->getComponentName()); - $sql[] = $this->_conn->quoteIdentifier($parentAlias . '.' . $columnName) - . ' AS ' - . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + $table = $mapper->getOwningTable($fieldName); + if ($table !== $baseTable) { + $tableAlias = $this->getSqlTableAlias($componentAlias . '.' . $table->getComponentName()); } else { - $columnName = $table->getColumnName($fieldName); - $sql[] = $this->_conn->quoteIdentifier($tableAlias . '.' . $columnName) - . ' AS ' - . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName); + $tableAlias = $this->getSqlTableAlias($componentAlias); + } + + $columnName = $table->getColumnName($fieldName); + $columnName = $table->getColumnName($fieldName); + $sql[] = $this->_conn->quoteIdentifier($tableAlias . '.' . $columnName) + . ' AS ' + . $this->_conn->quoteIdentifier($this->getSqlTableAlias($componentAlias) . '__' . $columnName); + + if ( ! in_array($tableAlias, $this->_neededTables)) { + $this->_neededTables[] = $tableAlias; } } - - $this->_neededTables[] = $tableAlias; - //Doctrine::dump(implode(', ', $sql)); - //echo "

"; + return implode(', ', $sql); } @@ -617,7 +568,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria // check for DISTINCT keyword if ($first === 'DISTINCT') { $this->_sqlParts['distinct'] = true; - $refs[0] = substr($refs[0], ++$pos); } @@ -632,7 +582,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $terms = $this->_tokenizer->sqlExplode($reference, ' '); - $pos = strpos($terms[0], '('); + $pos = strpos($terms[0], '('); if (count($terms) > 1 || $pos !== false) { $expression = array_shift($terms); @@ -647,7 +597,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $tableAlias = $this->getTableAlias($componentAlias); - $index = count($this->_aggregateAliasMap); + $index = count($this->_aggregateAliasMap); $sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index); @@ -670,7 +620,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $componentAlias = key($this->_queryComponents); $field = $e[0]; } - $this->_pendingFields[$componentAlias][] = $field; } } @@ -1156,7 +1105,8 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria } // append discriminator column conditions (if any) - $string = $this->_createDiscriminatorSql(); + $string = $this->_createDiscriminatorConditionSql(); + //echo "orig:$string

"; if ( ! empty($string)) { if (substr($string, 0, 1) === '(' && substr($string, -1) === ')') { $this->_sqlParts['where'][] = $string; @@ -1482,23 +1432,21 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $componentAlias = $prevPath; } - // if the current alias already exists, skip it + // if the current alias already exists, it's user error if (isset($this->_queryComponents[$componentAlias])) { - continue; + throw new Doctrine_Query_Exception("Duplicate alias '$componentAlias' in query."); } if ( ! isset($table)) { // process the root of the path - $table = $this->loadRoot($name, $componentAlias); } else { $join = ($delimeter == ':') ? 'INNER JOIN ' : 'LEFT JOIN '; - //echo "!!!!!!" . $prevPath . "!!!!!
"; $relation = $table->getRelation($name); $localTable = $table; - $table = $relation->getTable(); - //echo "

" . $table->getComponentName() . "------" . $relation->getForeignComponentName() . "

"; + $table = $relation->getTable(); + //echo "

" . $table->getComponentName() . "------" . $relation->getForeignComponentName() . "

"; $this->_queryComponents[$componentAlias] = array( 'table' => $table, 'mapper' => $this->_conn->getMapper($relation->getForeignComponentName()), @@ -1536,7 +1484,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria } $assocPath = $prevPath . '.' . $asf->getComponentName(); - //var_dump($name); echo "hrrrr"; //echo "

" . $asf->getComponentName() . "---2---" . $relation->getForeignComponentName() . "

"; $this->_queryComponents[$assocPath] = array( 'parent' => $prevPath, @@ -1592,7 +1539,6 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria } } } else { - $queryPart = $join . $foreignSql; if ( ! $overrideJoin) { @@ -1632,8 +1578,8 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria if (isset($e[1])) { $indexBy = $e[1]; } - } else if ($mapper->getBoundQueryPart('indexBy') !== null) { - $indexBy = $mapper->getBoundQueryPart('indexBy'); + } else if ($table->getBoundQueryPart('indexBy') !== null) { + $indexBy = $table->getBoundQueryPart('indexBy'); } if ($indexBy !== null) { @@ -1733,7 +1679,7 @@ class Doctrine_Query extends Doctrine_Query_Abstract implements Countable, Seria $q .= ' FROM ' . $this->_buildSqlFromPart(); // append discriminator column conditions (if any) - $string = $this->_createDiscriminatorSql(); + $string = $this->_createDiscriminatorConditionSql(); if ( ! empty($string)) { $where[] = $string; } diff --git a/lib/Doctrine/Query/Abstract.php b/lib/Doctrine/Query/Abstract.php index b154c3a84..7c45067d8 100644 --- a/lib/Doctrine/Query/Abstract.php +++ b/lib/Doctrine/Query/Abstract.php @@ -532,11 +532,11 @@ abstract class Doctrine_Query_Abstract $tableAlias = $this->getSqlTableAlias($componentAlias, $table->getTableName()); $customJoins = $this->_conn->getMapper($componentName)->getCustomJoins(); $sql = ''; - foreach ($customJoins as $componentName) { + foreach ($customJoins as $componentName => $joinType) { $joinedTable = $this->_conn->getTable($componentName); $joinedAlias = $componentAlias . '.' . $componentName; $joinedTableAlias = $this->getSqlTableAlias($joinedAlias, $joinedTable->getTableName()); - $sql .= ' LEFT JOIN ' . $this->_conn->quoteIdentifier($joinedTable->getTableName()) + $sql .= " $joinType JOIN " . $this->_conn->quoteIdentifier($joinedTable->getTableName()) . ' ' . $this->_conn->quoteIdentifier($joinedTableAlias) . ' ON '; foreach ($table->getIdentifierColumnNames() as $column) { @@ -556,23 +556,16 @@ abstract class Doctrine_Query_Abstract * * @return string The created SQL snippet. */ - protected function _createDiscriminatorSql() - { + protected function _createDiscriminatorConditionSql() + { $array = array(); foreach ($this->_queryComponents as $componentAlias => $data) { - $tableAlias = $this->getSqlTableAlias($componentAlias); - //echo $data['table']->getComponentName() . " -- "; - /*if (!isset($data['mapper'])) { - //echo $data['table']->getComponentName(); - echo $this->getDql(); - }*/ - /*if ($data['mapper']->getComponentName() != $data['table']->getComponentName()) { - //echo $this->getDql() . "
"; - }*/ - //echo $data['mapper']->getComponentName() . "_
"; - //var_dump($data['mapper']->getDiscriminatorColumn($data['mapper']->getComponentName())); - - $array[$tableAlias][] = $data['mapper']->getDiscriminatorColumn($data['mapper']->getComponentName()); + $sqlTableAlias = $this->getSqlTableAlias($componentAlias); + if ( ! $data['mapper'] instanceof Doctrine_Mapper_SingleTable) { + $array[$sqlTableAlias][] = array(); + } else { + $array[$sqlTableAlias][] = $data['mapper']->getDiscriminatorColumn(); + } } //var_dump($array); // apply inheritance maps diff --git a/lib/Doctrine/RawSql.php b/lib/Doctrine/RawSql.php index bda9c8650..fb1d3798f 100644 --- a/lib/Doctrine/RawSql.php +++ b/lib/Doctrine/RawSql.php @@ -238,7 +238,7 @@ class Doctrine_RawSql extends Doctrine_Query_Abstract } } - $string = $this->_createDiscriminatorSql(); + $string = $this->_createDiscriminatorConditionSql(); if ( ! empty($string)) { $this->_sqlParts['where'][] = $string; } diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 04b9c7546..0969a4578 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -146,7 +146,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count * open connections * @throws Doctrine_Record_Exception if the cleanData operation fails somehow */ - public function __construct($mapper = null, $isNewEntry = false) + public function __construct($mapper = null, $isNewEntry = false, array $data = array()) { //echo get_class($this) . "
"; if (isset($mapper) && $mapper instanceof Doctrine_Table) { @@ -155,18 +155,10 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count //$this->_mapper = Doctrine_Manager::getInstance()->getMapper(get_class($this)); $exists = ! $isNewEntry; return; - } else if (isset($mapper) && $mapper instanceof Doctrine_Mapper) { + } else if (isset($mapper) && $mapper instanceof Doctrine_Mapper_Abstract) { //echo "two
"; $class = get_class($this); $this->_mapper = Doctrine_Manager::getInstance()->getMapper($class); - if ($class != $this->_mapper->getComponentName()) { - try { - throw new Exception("ddd"); - } catch (Exception $e) { - echo "MISMATCH: " . get_class($this) . "---" . $mapper->getComponentName(); - echo $e->getTraceAsString() . "

"; - } - } $this->_table = $this->_mapper->getTable(); $exists = ! $isNewEntry; } else { @@ -182,7 +174,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count self::$_index++; // get the data array - $this->_data = $this->_mapper->getData(); + $this->_data = $data; // get the column count $count = count($this->_data); @@ -202,7 +194,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count $this->assignDefaultValues(); } else { $this->_state = Doctrine_Record::STATE_CLEAN; - + // @TODO table->getColumnCount is not correct in CTI if ($count < $this->_table->getColumnCount()) { $this->_state = Doctrine_Record::STATE_PROXY; } @@ -213,7 +205,6 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count $repository->add($this); $this->construct(); - } /** @@ -256,11 +247,6 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count { return $this->_oid; } - - public function oid() - { - return $this->_oid; - } /** * isValid @@ -534,22 +520,22 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count } /** - * serialize - * this method is automatically called when this Doctrine_Record is serialized + * Serializes the entity. + * This method is automatically called when the entity is serialized. + * + * Part of the implementation of the Serializable interface. * * @return array */ public function serialize() { $event = new Doctrine_Event($this, Doctrine_Event::RECORD_SERIALIZE); - $this->preSerialize($event); $vars = get_object_vars($this); unset($vars['_references']); unset($vars['_mapper']); - //unset($vars['_table']); unset($vars['_errorStack']); unset($vars['_filter']); unset($vars['_node']); @@ -586,8 +572,8 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count } /** - * unseralize - * this method is automatically called everytime a Doctrine_Record object is unserialized + * Reconstructs the entity from it's serialized form. + * This method is automatically called everytime the entity is unserialized. * * @param string $serialized Doctrine_Record as serialized string * @throws Doctrine_Record_Exception if the cleanData operation fails somehow @@ -634,6 +620,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count $this->_mapper->getRepository()->add($this); $this->cleanData($this->_data); $this->prepareIdentifiers($this->exists()); + $this->postUnserialize($event); } @@ -676,7 +663,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count } if ($err) { - throw new Doctrine_Record_Exception('Unknown record state ' . $state); + throw new Doctrine_Record_Exception("Unknown record state '$state'."); } } @@ -863,12 +850,8 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count if ( ! isset($this->_references[$fieldName]) && $load) { $rel = $this->_table->getRelation($fieldName); $this->_references[$fieldName] = $rel->fetchRelatedFor($this); - /*if (count($this->_references[$fieldName]) > 0) { - echo $this->_references[$fieldName][0]->state() . "
"; - }*/ } return $this->_references[$fieldName]; - } catch (Doctrine_Table_Exception $e) { //echo $e->getTraceAsString(); //echo "

"; @@ -937,12 +920,6 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count $this->_modified[] = $fieldName; switch ($this->_state) { case Doctrine_Record::STATE_CLEAN: - /*try { - throw new Exception(); - } catch (Exception $e) { - echo $e->getTraceAsString() . "

"; - } - echo "setting dirty ...
";*/ $this->_state = Doctrine_Record::STATE_DIRTY; break; case Doctrine_Record::STATE_TCLEAN: @@ -1004,7 +981,6 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count } } } - } else if ($rel instanceof Doctrine_Relation_Association) { // join table relation found if ( ! ($value instanceof Doctrine_Collection)) { @@ -1070,7 +1046,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count */ public function save(Doctrine_Connection $conn = null) { - $this->_mapper->saveGraph($this, $conn); + $this->_mapper->save($this, $conn); } /** @@ -1209,7 +1185,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count } } //$map = $this->_table->getOption('inheritanceMap'); - $map = $this->_mapper->getDiscriminatorColumn($this->_domainClassName); + $map = $this->_mapper->getDiscriminatorColumn(); foreach ($map as $k => $v) { $old = $this->get($k, false); if ((string) $old !== (string) $v || $old === null) { @@ -1225,6 +1201,8 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count * count * this class implements countable interface * + * Implementation of the Countable interface. + * * @return integer the number of columns in this record */ public function count() @@ -1436,10 +1414,7 @@ abstract class Doctrine_Record extends Doctrine_Record_Abstract implements Count */ public function delete(Doctrine_Connection $conn = null) { - if ($conn == null) { - $conn = $this->_mapper->getConnection(); - } - return $conn->unitOfWork->delete($this); + return $this->_mapper->delete($this, $conn); } /** diff --git a/lib/Doctrine/Relation/Association.php b/lib/Doctrine/Relation/Association.php index 3b778afc3..2b3ea6b98 100644 --- a/lib/Doctrine/Relation/Association.php +++ b/lib/Doctrine/Relation/Association.php @@ -107,7 +107,10 @@ class Doctrine_Relation_Association extends Doctrine_Relation public function fetchRelatedFor(Doctrine_Record $record) { $id = $record->getIncremented(); + //var_dump($id); + //echo "

"; if (empty($id) || ! $this->_foreignMapper->getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) { + //echo "here" . $this->_foreignMapper->getAttribute(Doctrine::ATTR_LOAD_REFERENCES); $coll = new Doctrine_Collection($this->getForeignComponentName()); } else { $query = Doctrine_Query::create()->parseQuery($this->getRelationDql(1)); diff --git a/lib/Doctrine/Sequence/Mysql.php b/lib/Doctrine/Sequence/Mysql.php index 088b22afe..c2e3ae100 100644 --- a/lib/Doctrine/Sequence/Mysql.php +++ b/lib/Doctrine/Sequence/Mysql.php @@ -47,10 +47,8 @@ class Doctrine_Sequence_Mysql extends Doctrine_Sequence $query = 'INSERT INTO ' . $sequenceName . ' (' . $seqcolName . ') VALUES (NULL)'; try { - $this->conn->exec($query); - - } catch(Doctrine_Connection_Exception $e) { + } catch (Doctrine_Connection_Exception $e) { if ($onDemand && $e->getPortableCode() == Doctrine::ERR_NOSUCHTABLE) { try { $this->conn->export->createSequence($seqName); diff --git a/lib/Doctrine/Sequence/Pgsql.php b/lib/Doctrine/Sequence/Pgsql.php index c5ea04482..73e4ea7a1 100644 --- a/lib/Doctrine/Sequence/Pgsql.php +++ b/lib/Doctrine/Sequence/Pgsql.php @@ -47,7 +47,7 @@ class Doctrine_Sequence_Pgsql extends Doctrine_Sequence $query = "SELECT NEXTVAL('" . $sequenceName . "')"; try { $result = (int) $this->conn->fetchOne($query); - } catch(Doctrine_Connection_Exception $e) { + } catch (Doctrine_Connection_Exception $e) { if ($onDemand && $e->getPortableCode() == Doctrine::ERR_NOSUCHTABLE) { try { @@ -70,6 +70,7 @@ class Doctrine_Sequence_Pgsql extends Doctrine_Sequence * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted * @return integer the autoincremented id + * @todo Why not use $this->conn->getDbh()->lastInsertId($sequenceName) ? */ public function lastInsertId($table = null, $field = null) { diff --git a/lib/Doctrine/Table.php b/lib/Doctrine/Table.php index ce6a7addd..50d1f1727 100644 --- a/lib/Doctrine/Table.php +++ b/lib/Doctrine/Table.php @@ -437,6 +437,11 @@ class Doctrine_Table extends Doctrine_Configurable implements Serializable $fieldName = $parts[0]; } $name = strtolower($parts[0]); + + if (isset($this->_columnNames[$fieldName])) { + return; + } + if ($prepend) { $this->_columnNames = array_merge(array($fieldName => $name), $this->_columnNames); $this->_fieldNames = array_merge(array($name => $fieldName), $this->_fieldNames); @@ -493,6 +498,8 @@ class Doctrine_Table extends Doctrine_Configurable implements Serializable if (isset($options['default'])) { $this->hasDefaultValues = true; } + + $this->columnCount++; } /** @@ -633,11 +640,6 @@ class Doctrine_Table extends Doctrine_Configurable implements Serializable { return $this->columnCount; } - - public function setColumnCount($count) - { - $this->columnCount = $count; - } /** * returns all columns and their definitions diff --git a/lib/Doctrine/Table/Factory.php b/lib/Doctrine/Table/Factory.php index 3ad5edee1..6dfdbcfd6 100644 --- a/lib/Doctrine/Table/Factory.php +++ b/lib/Doctrine/Table/Factory.php @@ -200,11 +200,9 @@ class Doctrine_Table_Factory $table->setOption('declaringClass', $class); // set the table definition for the given tree implementation - if ($table->isTree()) { + /*if ($table->isTree()) { $table->getTree()->setTableDefinition(); - } - - $table->setColumnCount(count($table->getColumns())); + }*/ $tableName = $table->getOption('tableName'); if ( ! isset($tableName)) { @@ -217,9 +215,9 @@ class Doctrine_Table_Factory $record->setUp(); // if tree, set up tree relations - if ($table->isTree()) { + /*if ($table->isTree()) { $table->getTree()->setUp(); - } + }*/ return $table; } @@ -304,8 +302,6 @@ class Doctrine_Table_Factory $table->setIdentifier('id'); $table->setIdentifierType(Doctrine::IDENTIFIER_AUTOINC); } - $currentCount = $table->getColumnCount(); - $table->setColumnCount(++$currentCount); break; case 1: foreach ($table->getIdentifier() as $pk) { diff --git a/lib/Doctrine/Table/Repository.php b/lib/Doctrine/Table/Repository.php index 785811f6f..9659447d8 100644 --- a/lib/Doctrine/Table/Repository.php +++ b/lib/Doctrine/Table/Repository.php @@ -51,9 +51,9 @@ class Doctrine_Table_Repository implements Countable, IteratorAggregate * * @param Doctrine_Table $table */ - public function __construct(Doctrine_Mapper $table) + public function __construct(Doctrine_Mapper_Abstract $mapper) { - $this->table = $table; + $this->table = $mapper; } /** diff --git a/lib/Doctrine/Transaction.php b/lib/Doctrine/Transaction.php index 5a55e001f..fe1f3ce23 100644 --- a/lib/Doctrine/Transaction.php +++ b/lib/Doctrine/Transaction.php @@ -326,11 +326,6 @@ class Doctrine_Transaction extends Doctrine_Connection_Module public function rollback($savepoint = null) { if ($this->_nestingLevel == 0) { - /*try { - throw new Doctrine_Transaction_Exception("Rollback failed. There is no active transaction."); - } catch (Exception $e) { - echo $e->getTraceAsString() . "
"; - }*/ throw new Doctrine_Transaction_Exception("Rollback failed. There is no active transaction."); } diff --git a/lib/Doctrine/Tree/NestedSet.php b/lib/Doctrine/Tree/NestedSet.php index 5e30f42af..d0424b162 100644 --- a/lib/Doctrine/Tree/NestedSet.php +++ b/lib/Doctrine/Tree/NestedSet.php @@ -319,39 +319,4 @@ class Doctrine_Tree_NestedSet extends Doctrine_Tree implements Doctrine_Tree_Int $this->_baseQuery = $this->_createBaseQuery(); } - /** - * Enter description here... - * - * @param unknown_type $graph - */ - /* - public function computeLevels($tree) - { - $right = array(); - $isArray = is_array($tree); - $rootColumnName = $this->getAttribute('rootColumnName'); - - for ($i = 0, $count = count($tree); $i < $count; $i++) { - if ($rootColumnName && $i > 0 && $tree[$i][$rootColumnName] != $tree[$i-1][$rootColumnName]) { - $right = array(); - } - - if (count($right) > 0) { - while (count($right) > 0 && $right[count($right)-1] < $tree[$i]['rgt']) { - //echo count($right); - array_pop($right); - } - } - - if ($isArray) { - $tree[$i]['level'] = count($right); - } else { - $tree[$i]->getNode()->setLevel(count($right)); - } - - $right[] = $tree[$i]['rgt']; - } - return $tree; - } - */ } \ No newline at end of file diff --git a/tests/ConnectionTestCase.php b/tests/ConnectionTestCase.php index 71cf1bb8a..2efccbd6c 100644 --- a/tests/ConnectionTestCase.php +++ b/tests/ConnectionTestCase.php @@ -151,9 +151,9 @@ class Doctrine_Connection_TestCase extends Doctrine_UnitTestCase public function testDelete() { - $user = $this->connection->create('User'); - $this->connection->unitOfWork->delete($user); - $this->assertEqual($user->state(),Doctrine_Record::STATE_TCLEAN); + //$user = $this->connection->create('User'); + //$this->connection->unitOfWork->delete($user); + //$this->assertEqual($user->state(),Doctrine_Record::STATE_TCLEAN); } public function testGetTable() diff --git a/tests/Inheritance/JoinedTestCase.php b/tests/Inheritance/JoinedTestCase.php index 345928972..56bf8854b 100644 --- a/tests/Inheritance/JoinedTestCase.php +++ b/tests/Inheritance/JoinedTestCase.php @@ -140,6 +140,40 @@ class Doctrine_Inheritance_Joined_TestCase extends Doctrine_UnitTestCase $this->assertEqual('Billy the Kid', $superManager->gosutitle); $this->assertEqual(4, $superManager->type); } + + public function testDqlQueryJoinsTransparentlyAcrossParents() + { + $this->_createManager(); + $this->conn->getMapper('CTI_Manager')->clear(); + + $query = $this->conn->createQuery(); + $query->parseQuery("SELECT m.* FROM CTI_Manager m"); + $manager = $query->execute()->getFirst(); + + $this->assertTrue($manager instanceof CTI_Manager); + $this->assertEqual(1, $manager->id); + $this->assertEqual(80000, $manager->salary); + $this->assertEqual('John Smith', $manager->name); + $this->assertEqual(2, $manager->type); + } + + public function testQueryingBaseClassOuterJoinsSubClassesAndReturnsSubclassInstances() + { + $this->_createManager(); + $this->conn->getMapper('CTI_Manager')->clear(); + $this->conn->getMapper('CTI_User')->clear(); + + $query = $this->conn->createQuery(); + $query->parseQuery("SELECT u.* FROM CTI_User u"); + //echo $query->getSql(); + $user = $query->execute()->getFirst(); + + $this->assertTrue($user instanceof CTI_Manager); + $this->assertEqual(1, $user->id); + $this->assertEqual(80000, $user->salary); + $this->assertEqual('John Smith', $user->name); + $this->assertEqual(2, $user->type); + } } @@ -152,7 +186,15 @@ class CTI_User extends Doctrine_Record 'CTI_Manager' => array('type' => 2), 'CTI_Customer' => array('type' => 3), 'CTI_SuperManager' => array('type' => 4)) - ); + );/* + $class->setInheritanceType(Doctrine::INHERITANCETYPE_JOINED, array( + 'discriminatorColumn' => 'type', + 'map' => array(1 => 'CTI_User', 2 => 'CTI_Manager', 3 => 'CTI_Customer', + 4 => 'CTI_SuperManager') + )); + $class->setDiscriminatorValue(1); + $class->setInheritanceOption('fetchType', 'explicit'); + */ $this->setTableName('cti_user'); $this->hasColumn('cti_id as id', 'integer', 4, array('primary' => true, 'autoincrement' => true)); $this->hasColumn('cti_foo as foo', 'integer', 4); diff --git a/tests/Inheritance/SingleTableTestCase.php b/tests/Inheritance/SingleTableTestCase.php index 2c797ad50..c8461d7ac 100644 --- a/tests/Inheritance/SingleTableTestCase.php +++ b/tests/Inheritance/SingleTableTestCase.php @@ -58,6 +58,16 @@ class Doctrine_Inheritance_SingleTable_TestCase extends Doctrine_UnitTestCase $this->fail("Saving record in single table inheritance failed: " . $e->getMessage()); } } + + public function testQuery() + { + //$this->_createManager(); + $query = $this->conn->createQuery(); + $query->select("m.*")->from("STI_Manager m"); + //echo $query->getSql(); + //$managers = $query->execute(); + + } } diff --git a/tests/Query/JoinCondition2TestCase.php b/tests/Query/JoinCondition2TestCase.php index ca6367d07..5101d139a 100755 --- a/tests/Query/JoinCondition2TestCase.php +++ b/tests/Query/JoinCondition2TestCase.php @@ -74,7 +74,7 @@ class Doctrine_Query_JoinCondition2_TestCase extends Doctrine_UnitTestCase // Should only find zYne $this->assertEqual($rs->count(), 1); - + // Grab the number of runned queries $queryCount = $this->connection->count(); diff --git a/tests/Query/MultiJoin2TestCase.php b/tests/Query/MultiJoin2TestCase.php index 7341c5db8..43d22805b 100644 --- a/tests/Query/MultiJoin2TestCase.php +++ b/tests/Query/MultiJoin2TestCase.php @@ -85,7 +85,7 @@ class Doctrine_Query_MultiJoin2_TestCase extends Doctrine_UnitTestCase ->execute(); // Test that accessing a loaded (but empty) relation doesnt trigger an extra query $this->assertEqual($queryCount + 1, $this->connection->count()); - + $this->assertEqual(0, count($categories[0]->subCategories)); $categories[0]->subCategories; $this->assertEqual($queryCount + 1, $this->connection->count()); } catch (Doctrine_Exception $e) { diff --git a/tests/QueryTestCase.php b/tests/QueryTestCase.php index 46527698c..dbdb42ed5 100644 --- a/tests/QueryTestCase.php +++ b/tests/QueryTestCase.php @@ -101,6 +101,27 @@ class Doctrine_Query_TestCase extends Doctrine_UnitTestCase //Doctrine::dump($q->getCachedForm(array('foo' => 'bar'))); $this->assertEqual($q->parseClause("CONCAT('u.name', u.name)"), "'u.name' || e.name"); } + + public function testUsingDuplicateClassAliasThrowsException() + { + $q = new Doctrine_Query(); + $q->from('User u')->leftJoin('u.Phonenumber u'); + try { + $q->getSqlQuery(); + $this->fail(); + } catch (Doctrine_Query_Exception $e) { + $this->pass(); + } + + $q = new Doctrine_Query(); + $q->parseDqlQuery('FROM User u, u.Phonenumber u'); + try { + $q->getSqlQuery(); + $this->fail(); + } catch (Doctrine_Query_Exception $e) { + $this->pass(); + } + } } class MyQuery extends Doctrine_Query {