From ab65ad5b4d26052f2104c40bfc5453538aa3542c Mon Sep 17 00:00:00 2001 From: romanb Date: Tue, 6 May 2008 13:41:22 +0000 Subject: [PATCH] Refactorings. Started with new hydrator for 2.0. --- lib/Doctrine/ClassMetadata.php | 561 ++++---- lib/Doctrine/Collection.php | 10 +- lib/Doctrine/Connection.php | 1176 +++++++++-------- lib/Doctrine/Connection/UnitOfWork.php | 61 +- lib/Doctrine/EntityRepository.php | 225 ++++ lib/Doctrine/Hydrator.php | 41 +- lib/Doctrine/Hydrator/ArrayDriver.php | 2 - lib/Doctrine/Hydrator/RecordDriver.php | 62 +- lib/Doctrine/HydratorNew.php | 422 ++++++ lib/Doctrine/Mapper.php | 198 +-- lib/Doctrine/Mapper/JoinedStrategy.php | 3 +- lib/Doctrine/Query.php | 2 +- lib/Doctrine/Record.php | 173 +-- lib/Doctrine/Relation/Association.php | 13 +- lib/Doctrine/Relation/Association/Self.php | 8 +- lib/Doctrine/Relation/Nest.php | 30 +- lib/Doctrine/Relation/Parser.php | 6 - lib/Doctrine/Validator.php | 3 +- tests/Orm/Hydration/BasicHydrationTest.php | 296 ++++- tests/Orm/UnitOfWorkTestCase.php | 12 +- tests/models/cms/CmsPhonenumber.php | 9 + tests/models/cms/CmsUser.php | 3 + tests_old/AccessTestCase.php | 6 +- tests_old/ClassTableInheritanceTestCase.php | 6 +- tests_old/CustomPrimaryKeyTestCase.php | 2 +- tests_old/DataType/BooleanTestCase.php | 2 +- tests_old/DataType/EnumTestCase.php | 2 +- .../DoctrineTest/Doctrine_UnitTestCase.php | 2 +- tests_old/Inheritance/JoinedTestCase.php | 6 +- tests_old/Query/JoinCondition2TestCase.php | 6 +- tests_old/Query/MultiJoinTestCase.php | 8 +- tests_old/Query/ReferenceModelTestCase.php | 2 +- tests_old/Query/RegistryTestCase.php | 2 +- tests_old/RecordTestCase.php | 61 +- tests_old/Relation/NestTestCase.php | 2 +- tests_old/TableTestCase.php | 2 +- tests_old/Ticket/626DTestCase.php | 2 +- tests_old/ValidatorTestCase.php | 8 +- 38 files changed, 2143 insertions(+), 1292 deletions(-) create mode 100644 lib/Doctrine/EntityRepository.php create mode 100644 lib/Doctrine/HydratorNew.php create mode 100644 tests/models/cms/CmsPhonenumber.php diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php index 84b4c0c30..4db380493 100644 --- a/lib/Doctrine/ClassMetadata.php +++ b/lib/Doctrine/ClassMetadata.php @@ -1,4 +1,4 @@ - null, 'treeOptions' => null, 'queryParts' => array() - ); - + ); + /** * Inheritance options. */ protected $_inheritanceOptions = array( - // JOINED & TABLE_PER_CLASS options + // JOINED & TABLE_PER_CLASS options 'discriminatorColumn' => null, 'discriminatorMap' => array(), - // JOINED options + // JOINED options 'joinSubclasses' => true - ); - + ); + /** * Specific options that can be set for the database table the class is mapped to. * Some of them are dbms specific and they are only used if the table is generated * by Doctrine (NOT when using Migrations). - * + * * -- type table type (mysql example: INNODB) * * -- charset character set @@ -252,29 +251,29 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab 'collate' => null, 'indexes' => array(), 'checks' => array() - ); - + ); + /** * @var array $_invokedMethods method invoker cache */ protected $_invokedMethods = array(); - - + + /** * Constructs a new ClassMetadata instance. * * @param string $entityName Name of the entity class the metadata info is used for. */ public function __construct($entityName, Doctrine_Connection $conn) - { + { $this->_entityName = $entityName; $this->_rootEntityName = $entityName; $this->_conn = $conn; $this->_parser = new Doctrine_Relation_Parser($this); $this->_filters[] = new Doctrine_Record_Filter_Standard(); - $this->setConfigurableParent($this->_conn); + $this->setConfigurableParent($this->_conn); } - + /** * */ @@ -282,7 +281,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_conn; } - + /** * getComponentName * @@ -292,7 +291,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_entityName; } - + /** * Gets the name of the root class of the entity hierarchy. If the entity described * by the ClassMetadata is not participating in a hierarchy, this is the same as the @@ -304,7 +303,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_rootEntityName; } - + /** * @deprecated */ @@ -312,19 +311,22 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->getClassName(); } - + /** * Checks whether a field is part of the identifier/primary key field(s). * * @param string $fieldName The field name - * @return boolean TRUE if the field is part of the table identifier/primary key field(s), + * @return boolean TRUE if the field is part of the table identifier/primary key field(s), * FALSE otherwise. */ public function isIdentifier($fieldName) { - return in_array($fieldName, $this->getIdentifier()); + if ($this->_identifierType != Doctrine::IDENTIFIER_COMPOSITE) { + return $fieldName === $this->_identifier[0]; + } + return in_array($fieldName, $this->_identifier); } - + /** * addIndex * @@ -350,7 +352,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return false; } - + /** * setOption * sets an option and returns this object in order to @@ -365,21 +367,21 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab public function setOption($name, $value) { /*switch ($name) { - case 'tableName': - case 'index': - case 'sequenceName': - case 'type': - case 'charset': - case 'collation': - case 'collate': - return $this->setTableOption($name, $value); - case 'enumMap': - $this->_enumMap = $value; - return; - }*/ + case 'tableName': + case 'index': + case 'sequenceName': + case 'type': + case 'charset': + case 'collation': + case 'collate': + return $this->setTableOption($name, $value); + case 'enumMap': + $this->_enumMap = $value; + return; + }*/ $this->_options[$name] = $value; } - + /** * Sets a table option. */ @@ -388,9 +390,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ( ! array_key_exists($name, $this->_tableOptions)) { throw new Doctrine_ClassMetadata_Exception("Unknown table option: '$name'."); } - $this->_tableOptions[$name] = $value; + $this->_tableOptions[$name] = $value; } - + /** * Gets a table option. */ @@ -399,16 +401,16 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ( ! array_key_exists($name, $this->_tableOptions)) { throw new Doctrine_ClassMetadata_Exception("Unknown table option: '$name'."); } - + return $this->_tableOptions[$name]; } - + public function getBehaviorForMethod($method) { return (isset($this->_invokedMethods[$method])) ? - $this->_invokedMethods[$method] : false; + $this->_invokedMethods[$method] : false; } - + public function addBehaviorMethod($method, $behavior) { $this->_invokedMethods[$method] = $class; @@ -430,7 +432,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab } return null; } - + /** * getOptions * returns all options of this table and the associated values @@ -441,7 +443,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_options; } - + /** * getTableOptions * returns all table options. @@ -452,7 +454,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_tableOptions; } - + /** * getColumnName * @@ -466,9 +468,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab public function getColumnName($fieldName) { return isset($this->_columnNames[$fieldName]) ? - $this->_columnNames[$fieldName] : $fieldName; + $this->_columnNames[$fieldName] : $fieldName; } - + /** * @deprecated */ @@ -476,17 +478,17 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->getColumnMapping($columnName); } - + public function getColumnMapping($columnName) { return isset($this->_mappedColumns[$columnName]) ? - $this->_mappedColumns[$columnName] : false; + $this->_mappedColumns[$columnName] : false; } - + /** * getFieldName - * - * returns the field name for a column name + * + * returns the field name for a column name * if no field name can be found the column name is returned. * * @param string $columnName column name @@ -495,9 +497,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab public function getFieldName($columnName) { return isset($this->_fieldNames[$columnName]) ? - $this->_fieldNames[$columnName] : $columnName; + $this->_fieldNames[$columnName] : $columnName; } - + /** * @deprecated */ @@ -507,12 +509,12 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $this->setColumn($name, $options['type'], $options['length'], $options); } } - + /** * Maps a column of the class' database table to a field of the entity. * * @param string $name The name of the column to map. Syntax: columnName [as propertyName]. - * The property name is optional. If not used the column will be + * The property name is optional. If not used the column will be * mapped to a property with the same name. * @param string $type The type of the column. * @param integer $length The length of the column. @@ -524,6 +526,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function mapColumn($name, $type, $length = null, $options = array(), $prepend = false) { + // converts 0 => 'primary' to 'primary' => true etc. foreach ($options as $k => $option) { if (is_numeric($k)) { if ( ! empty($option) && $option !== false) { @@ -532,7 +535,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab unset($options[$k]); } } - + // extract column name & field name $parts = explode(' as ', $name); if (count($parts) > 1) { @@ -541,11 +544,11 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $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); @@ -553,56 +556,68 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $this->_columnNames[$fieldName] = $name; $this->_fieldNames[$name] = $fieldName; } - - if ($length == null) { - switch ($type) { - case 'string': - case 'clob': - case 'float': - case 'integer': - case 'array': - case 'object': - case 'blob': - case 'gzip': - // use php int max - $length = 2147483647; - break; - case 'boolean': - $length = 1; - case 'date': - // YYYY-MM-DD ISO 8601 - $length = 10; - case 'time': - // HH:NN:SS+00:00 ISO 8601 - $length = 14; - case 'timestamp': - // YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601 - $length = 25; - break; - } - } + // Inspect & fill $options + + if ($length == null) { + $length = $this->_getDefaultLength($type); + } + $options['type'] = $type; $options['length'] = $length; + if ( ! $this->_hasDefaultValues && isset($options['default'])) { + $this->_hasDefaultValues = true; + } + if ( ! empty($options['primary'])) { + if ( ! in_array($fieldName, $this->_identifier)) { + $this->_identifier[] = $fieldName; + } + /*if (isset($options['autoincrement']) && $options['autoincrement'] === true) { + + }*/ + } + /* + if ( ! isset($options['immutable'])) { + $options['immutable'] = false; + }*/ + if ($prepend) { $this->_mappedColumns = array_merge(array($name => $options), $this->_mappedColumns); } else { $this->_mappedColumns[$name] = $options; } - if ( ! empty($options['primary'])) { - if ( ! in_array($fieldName, $this->_identifier)) { - $this->_identifier[] = $fieldName; - } - } - if (isset($options['default'])) { - $this->_hasDefaultValues = true; - } - $this->_columnCount++; } - + + private function _getDefaultLength($type) + { + switch ($type) { + case 'string': + case 'clob': + case 'float': + case 'integer': + case 'array': + case 'object': + case 'blob': + case 'gzip': + // use php int max + return 2147483647; + case 'boolean': + return 1; + case 'date': + // YYYY-MM-DD ISO 8601 + return 10; + case 'time': + // HH:NN:SS+00:00 ISO 8601 + return 14; + case 'timestamp': + // YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601 + return 25; + } + } + /** * setColumn * @@ -620,7 +635,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->mapColumn($name, $type, $length, $options, $prepend); } - + /** * Gets the names of all validators that are applied on a field. * @@ -631,9 +646,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $columnName = $this->getColumnName($fieldName); return isset($this->_mappedColumns[$columnName]['validators']) ? - $this->_mappedColumns[$columnName]['validators'] : array(); + $this->_mappedColumns[$columnName]['validators'] : array(); } - + /** * Checks whether the class mapped class has a default value on any field. * @@ -663,7 +678,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return null; } } - + /** * Gets the identifier (primary key) field(s) of the mapped class. * @@ -674,7 +689,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_identifier; } - + /** * Gets the identifier (primary key) field(s) of the mapped class. * @@ -684,7 +699,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_identifier; } - + public function setIdentifier(array $identifier) { $this->_identifier = $identifier; @@ -694,14 +709,14 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab * Gets the type of the identifier (primary key) used by the mapped class. The type * can be either Doctrine::IDENTIFIER_NATURAL, Doctrine::IDENTIFIER_AUTOINCREMENT, * Doctrine::IDENTIFIER_SEQUENCE or Doctrine::IDENTIFIER_COMPOSITE. - * + * * @return integer */ public function getIdentifierType() { return $this->_identifierType; } - + /** * Sets the identifier type used by the mapped class. */ @@ -719,12 +734,12 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_mappedColumns[$columnName]); } - + public function hasMappedColumn($columnName) { return isset($this->_mappedColumns[$columnName]); } - + /** * hasField * @return boolean @@ -733,7 +748,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_columnNames[$fieldName]); } - + /** * @param string $fieldName * @return array @@ -756,24 +771,24 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab * @return mixed */ public function enumValue($fieldName, $index) - { + { if ($index instanceof Doctrine_Null) { return $index; } - + if (isset($this->_enumValues[$fieldName][$index])) { return $this->_enumValues[$fieldName][$index]; } - + $columnName = $this->getColumnName($fieldName); if ( ! $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM) && - isset($this->_mappedColumns[$columnName]['values'][$index])) { + isset($this->_mappedColumns[$columnName]['values'][$index])) { $enumValue = $this->_mappedColumns[$columnName]['values'][$index]; } else { $enumValue = $index; } $this->_enumValues[$fieldName][$index] = $enumValue; - + return $enumValue; } @@ -791,10 +806,10 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ($index === false || ! $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) { return $index; } - + return $value; } - + /** * getColumnCount * @@ -805,7 +820,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_columnCount; } - + /** * getMappedColumnCount * @@ -815,9 +830,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_columnCount; } - + /** - * + * * @return string The name of the accessor (getter) method or NULL if the field does * not have a custom accessor. */ @@ -825,11 +840,11 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $columnName = $this->getColumnName($fieldName); return isset($this->_mappedColumns[$columnName]['accessor']) ? - $this->_mappedColumns[$columnName]['accessor'] : null; + $this->_mappedColumns[$columnName]['accessor'] : null; } - + /** - * + * * @return string The name of the mutator (setter) method or NULL if the field does * not have a custom mutator. */ @@ -837,7 +852,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $columnName = $this->getColumnName($fieldName); return isset($this->_mappedColumns[$columnName]['mutator']) ? - $this->_mappedColumns[$columnName]['mutator'] : null; + $this->_mappedColumns[$columnName]['mutator'] : null; } /** @@ -850,7 +865,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_mappedColumns; } - + /** * Gets all mapped columns and their mapping definitions. * @@ -869,7 +884,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function removeColumn($fieldName) { - $columnName = array_search($fieldName, $this->_fieldNames); + $columnName = array_search($fieldName, $this->_fieldNames); unset($this->_fieldNames[$columnName]); @@ -878,7 +893,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return true; } $this->_columnCount--; - + return false; } @@ -892,15 +907,15 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ($fieldNames === null) { return array_keys($this->_mappedColumns); } else { - $columnNames = array(); - foreach ($fieldNames as $fieldName) { - $columnNames[] = $this->getColumnName($fieldName); - } - - return $columnNames; + $columnNames = array(); + foreach ($fieldNames as $fieldName) { + $columnNames[] = $this->getColumnName($fieldName); + } + + return $columnNames; } } - + /** * returns an array with all the identifier column names. * @@ -910,7 +925,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->getColumnNames((array) $this->getIdentifier()); } - + /** * returns an array containing all the field names. * @@ -930,14 +945,14 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab public function getDefinitionOf($fieldName) { $columnName = $this->getColumnName($fieldName); - + return $this->getColumnDefinition($columnName); } - + public function getMappingForField($fieldName) { $columnName = $this->getColumnName($fieldName); - + return $this->getColumnDefinition($columnName); } @@ -951,12 +966,12 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->getTypeOfColumn($this->getColumnName($fieldName)); } - + public function getTypeOfField($fieldName) { return $this->getTypeOfColumn($this->getColumnName($fieldName)); } - + /** * getTypeOfColumn * @@ -966,7 +981,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_mappedColumns[$columnName]) ? $this->_mappedColumns[$columnName]['type'] : false; } - + /** * Gets the (maximum) length of a field. */ @@ -974,7 +989,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_mappedColumns[$this->getColumnName($fieldName)]['length']; } - + /** * getTableName * @@ -984,12 +999,12 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->getTableOption('tableName'); } - + public function getInheritedFields() { - + } - + /** * Adds a named query. * @@ -999,15 +1014,15 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function addNamedQuery($name, $query) { - + } - + public function bindRelation($args, $type) { return $this->bind($args, $type); } - - /** + + /** * DESCRIBE WHAT THIS METHOD DOES, PLEASE! * * @todo Name proposal: addRelation @@ -1030,7 +1045,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $options = array_merge($args[1], $options); $this->_parser->bind($args[0], $options); } - + /** * hasRelation * @@ -1051,7 +1066,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_parser->getRelation($alias, $recursive); } - + public function getRelationParser() { return $this->_parser; @@ -1067,7 +1082,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_parser->getRelations(); } - + /** * getBehaviors * returns all behaviors attached to the class. @@ -1079,7 +1094,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_behaviors; } - + /** * Gets the inheritance type used by the class. * @@ -1089,7 +1104,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_inheritanceType; } - + /** * Sets the subclasses of the class. * All entity classes that participate in a hierarchy and have subclasses @@ -1099,9 +1114,9 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function setSubclasses(array $subclasses) { - $this->_subClasses = $subclasses; + $this->_subClasses = $subclasses; } - + /** * Gets the names of all subclasses. * @@ -1111,7 +1126,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_subClasses; } - + /** * Checks whether the class has any persistent subclasses. * @@ -1121,7 +1136,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return ! $this->_subClasses; } - + /** * Gets the names of all parent classes. * @@ -1131,7 +1146,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_parentClasses; } - + /** * Sets the parent class names. */ @@ -1142,7 +1157,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $this->_rootEntityName = array_pop($classNames); } } - + /** * Checks whether the class has any persistent parent classes. * @@ -1152,7 +1167,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return ! $this->_parentClasses; } - + /** * Sets the inheritance type used by the class and it's subclasses. * @@ -1163,26 +1178,26 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ($parentClassNames = $this->getParentClasses()) { if ($this->_conn->getClassMetadata($parentClassNames[0])->getInheritanceType() != $type) { throw new Doctrine_ClassMetadata_Exception("All classes in an inheritance hierarchy" - . " must share the same inheritance mapping type. Mixing is not allowed."); + . " must share the same inheritance mapping type. Mixing is not allowed."); } } - + if ($type == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) { $this->_checkRequiredDiscriminatorOptions($options); } else if ($type == Doctrine::INHERITANCE_TYPE_JOINED) { $this->_checkRequiredDiscriminatorOptions($options); } else if ($type == Doctrine::INHERITANCE_TYPE_TABLE_PER_CLASS) { ; - } else { + } else { throw new Doctrine_ClassMetadata_Exception("Invalid inheritance type '$type'."); } - + $this->_inheritanceType = $type; foreach ($options as $name => $value) { $this->setInheritanceOption($name, $value); } } - + /** * Checks if the 2 options 'discriminatorColumn' and 'discriminatorMap' are present. * If either of them is missing an exception is thrown. @@ -1195,13 +1210,13 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { if ( ! isset($options['discriminatorColumn'])) { throw new Doctrine_ClassMetadata_Exception("Missing option 'discriminatorColumn'." - . " Inheritance types JOINED and SINGLE_TABLE require this option."); + . " Inheritance types JOINED and SINGLE_TABLE require this option."); } else if ( ! isset($options['discriminatorMap'])) { throw new Doctrine_ClassMetadata_Exception("Missing option 'discriminatorMap'." - . " Inheritance types JOINED and SINGLE_TABLE require this option."); + . " Inheritance types JOINED and SINGLE_TABLE require this option."); } } - + /** * Gets an inheritance option. * @@ -1211,10 +1226,10 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ( ! array_key_exists($name, $this->_inheritanceOptions)) { throw new Doctrine_ClassMetadata_Exception("Unknown inheritance option: '$name'."); } - + return $this->_inheritanceOptions[$name]; } - + /** * Gets all inheritance options. */ @@ -1222,7 +1237,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_inheritanceOptions; } - + /** * Sets an inheritance option. */ @@ -1231,25 +1246,25 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ( ! array_key_exists($name, $this->_inheritanceOptions)) { throw new Doctrine_ClassMetadata_Exception("Unknown inheritance option: '$name'."); } - + switch ($name) { case 'discriminatorColumn': if ($value !== null && ! is_string($value)) { throw new Doctrine_ClassMetadata_Exception("Invalid value '$value' for option" - . " 'discriminatorColumn'."); + . " 'discriminatorColumn'."); } break; case 'discriminatorMap': if ( ! is_array($value)) { throw new Doctrine_ClassMetadata_Exception("Value for option 'discriminatorMap'" - . " must be an array."); + . " must be an array."); } break; } - + $this->_inheritanceOptions[$name] = $value; } - + /** * export * exports this class to the database based on its mapping. @@ -1276,7 +1291,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $columns = array(); $primary = array(); $allColumns = $this->getColumns(); - + // If the class is part of a Single Table Inheritance hierarchy, collect the fields // of all classes in the hierarchy. if ($this->_inheritanceType == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) { @@ -1337,11 +1352,11 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $primary[] = $name; } } - + // Collect foreign keys from the relations $options['foreignKeys'] = array(); if ($parseForeignKeys && $this->getAttribute(Doctrine::ATTR_EXPORT) - & Doctrine::EXPORT_CONSTRAINTS) { + & Doctrine::EXPORT_CONSTRAINTS) { $constraints = array(); $emptyIntegrity = array('onUpdate' => null, 'onDelete' => null); foreach ($this->getRelations() as $name => $relation) { @@ -1380,16 +1395,16 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab } $options['primary'] = $primary; - + return array('tableName' => $this->getTableOption('tableName'), 'columns' => $columns, 'options' => array_merge($options, $this->getTableOptions())); } - + /** * getTemplate * - * @param string $template + * @param string $template * @return void * @todo Unify under 'Behaviors'. */ @@ -1401,7 +1416,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return $this->_behaviors[$behaviorName]; } - + /** * @todo Unify under 'Behaviors'. */ @@ -1409,7 +1424,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_behaviors[$behaviorName]); } - + /** * @todo Unify under 'Behaviors'. */ @@ -1419,7 +1434,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return $this; } - + /** * @todo Unify under 'Behaviors'. */ @@ -1427,7 +1442,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_generators; } - + /** * @todo Unify under 'Behaviors'. */ @@ -1439,7 +1454,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return $this->_generators[$plugin]; } - + /** * @todo Unify under 'Behaviors'. */ @@ -1447,20 +1462,20 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_generators[$generator]); } - + /** * @todo Unify under 'Behaviors'. */ public function addGenerator(Doctrine_Record_Generator $generator, $name = null) { - if ($name === null) { + if ($name === null) { $this->_generators[] = $generator; } else { $this->_generators[$name] = $generator; } return $this; } - + /** * loadBehavior * @@ -1471,16 +1486,16 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $this->actAs($behavior, $options); } - + /** * @todo Unify under 'Behaviors'. */ public function loadGenerator(Doctrine_Record_Generator $generator) { - $generator->initialize($this->_table); + $generator->initialize($this->_table); $this->addGenerator($generator, get_class($generator)); } - + /** * unshiftFilter * @@ -1492,10 +1507,10 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $filter->setTable($this); array_unshift($this->_filters, $filter); - + return $this; } - + /** * getTree * @@ -1510,13 +1525,13 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab if ( ! $this->_tree) { $options = $this->getOption('treeOptions') ? $this->getOption('treeOptions') : array(); $this->_tree = Doctrine_Tree::factory($this, - $this->getOption('treeImpl'), $options); + $this->getOption('treeImpl'), $options); } return $this->_tree; } return false; } - + /** * isTree * @@ -1529,7 +1544,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return ( ! is_null($this->getOption('treeImpl'))) ? true : false; } - + /** * getFilters * @@ -1540,7 +1555,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return $this->_filters; } - + /** * Checks whether a persistent field is inherited from a superclass. * @@ -1550,7 +1565,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return isset($this->_mappedColumns[$this->getColumnName($fieldName)]['inherited']); } - + /** * bindQueryParts * binds query parts to given component @@ -1560,7 +1575,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function bindQueryParts(array $queryParts) { - $this->_options['queryParts'] = $queryParts; + $this->_options['queryParts'] = $queryParts; return $this; } @@ -1574,14 +1589,14 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab */ public function bindQueryPart($queryPart, $value) { - $this->_options['queryParts'][$queryPart] = $value; + $this->_options['queryParts'][$queryPart] = $value; return $this; } - + /** * getBoundQueryPart * - * @param string $queryPart + * @param string $queryPart * @return string $queryPart */ public function getBoundQueryPart($queryPart) @@ -1601,7 +1616,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { $this->setTableOption('tableName', $this->_conn->formatter->getTableName($tableName)); } - + /** * Serializes the metadata. * @@ -1616,7 +1631,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab //return serialize($contents); return ""; } - + /** * Reconstructs the metadata class from it's serialized representation. * @@ -1628,37 +1643,37 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return true; } - + /** * @todo Implementation. */ public function oneToOne($targetEntity, $definition) { - + } - + /** * @todo Implementation. */ public function oneToMany($targetEntity, $definition) { - + } - + /** * @todo Implementation. */ public function manyToOne($targetEntity, $definition) { - + } - + /** * @todo Implementation. */ public function manyToMany($targetEntity, $definition) { - + } /** @@ -1697,7 +1712,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return $this; } - + /** * check * adds a check constraint @@ -1716,10 +1731,10 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab } else { $this->_addCheckConstraint($constraint, $name); } - + return $this; } - + protected function _addCheckConstraint($definition, $name) { if (is_string($name)) { @@ -1728,53 +1743,75 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab $this->_tableOptions['checks'][] = $definition; } } - + /** * Registers a custom mapper for the entity class. * * @param string $mapperClassName The class name of the custom mapper. + * @deprecated */ public function setCustomMapperClass($mapperClassName) { if ( ! is_subclass_of($mapperClassName, 'Doctrine_Mapper')) { throw new Doctrine_ClassMetadata_Exception("The custom mapper must be a subclass" - . " of Doctrine_Mapper."); + . " of Doctrine_Mapper."); } - $this->_customMapperClassName = $mapperClassName; + $this->_customRepositoryClassName = $mapperClassName; } + /** + * Registers a custom mapper for the entity class. + * + * @param string $mapperClassName The class name of the custom mapper. + * @deprecated + */ + public function setCustomRepositoryClass($repositoryClassName) + { + if ( ! is_subclass_of($repositoryClassName, 'Doctrine_EntityRepository')) { + throw new Doctrine_ClassMetadata_Exception("The custom repository must be a subclass" + . " of Doctrine_EntityRepository."); + } + $this->_customRepositoryClassName = $repositoryClassName; + } + /** * Gets the name of the custom mapper class used for the entity class. * * @return string|null The name of the custom mapper class or NULL if the entity * class does not have a custom mapper class. + * @deprecated */ public function getCustomMapperClass() { - return $this->_customMapperClassName; + return $this->_customRepositoryClassName; } + public function getCustomRepositoryClass() + { + return $this->_customRepositoryClassName; + } + /** * @todo Thoughts & Implementation. */ - public function setType($type) + public function setEntityType($type) { //Doctrine::CLASSTYPE_ENTITY //Doctrine::CLASSTYPE_MAPPED_SUPERCLASS //Doctrine::CLASSTYPE_TRANSIENT } - + /** - * + * * @todo Implementation. Replaces the bindComponent() methods on the old Doctrine_Manager. * Binding an Entity to a specific EntityManager in 2.0 is the same as binding * it to a Connection in 1.0. */ public function bindToEntityManager($emName) { - + } - + /** * @todo Implementation. Immutable entities can not be updated or deleted once * they are created. This means the entity can only be modified as long as it's @@ -1784,12 +1821,12 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab { return false; } - + public function isDiscriminatorColumn($columnName) { return $columnName === $this->_inheritanceOptions['discriminatorColumn']; } - + /** * hasOne * binds One-to-One aggregate relation @@ -1821,7 +1858,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return $this; } - + public function hasAttribute($key) { switch ($key) { @@ -1837,7 +1874,7 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab } } - + /** * */ diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php index c1c9ec22e..d83bce568 100644 --- a/lib/Doctrine/Collection.php +++ b/lib/Doctrine/Collection.php @@ -449,7 +449,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } } else { // @todo does not take composite keys into account - $list[] = $record->getIncremented(); + $ids = $record->identifier(); + $list[] = count($ids) > 0 ? array_pop($ids) : null; } } @@ -564,7 +565,9 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator if ( ! isset($name)) { foreach ($this->data as $record) { - $value = $record->getIncremented(); + // FIXME: composite key support + $ids = $record->identifier(); + $value = count($ids) > 0 ? array_pop($ids) : null; if ($value !== null) { $list[] = $value; } @@ -583,7 +586,8 @@ class Doctrine_Collection extends Doctrine_Access implements Countable, Iterator } } else { foreach ($this->data as $record) { - $value = $record->getIncremented(); + $ids = $record->identifier(); + $value = count($ids) > 0 ? array_pop($ids) : null; if ($value !== null) { $list[] = $value; } diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index 2cabb24ab..cacfa6136 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Configurable'); + /** * Doctrine_Connection * @@ -56,16 +56,94 @@ Doctrine::autoload('Doctrine_Configurable'); * @todo Split up into Doctrine::DBAL::Connection & Doctrine::ORM::EntityManager. * Doctrine::DBAL::Connection must have no dependencies on ORM components since * it sits one layer below. + * Right now, this is the unification of these two classes. */ abstract class Doctrine_Connection extends Doctrine_Configurable implements Countable, IteratorAggregate { + /* + * ----------- Connection attributes --------------- + */ /** * The PDO database handle. * * @var PDO */ protected $dbh; - + + /** + * $_name + * + * Name of the connection + * + * @var string $_name + */ + protected $_name; + + /** + * The name of this connection driver. + * + * @var string $driverName + */ + protected $driverName; + + /** + * Whether or not a connection has been established. + * + * @var boolean $isConnected + */ + protected $isConnected = false; + + /** + * An array containing all features this driver supports, keys representing feature + * names and values as one of the following (true, false, 'emulated'). + * + * @var array $supported + */ + protected $supported = array(); + + /** + * @var array $properties an array of connection properties + */ + protected $properties = array( + 'sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false), + array('start' => '/*', 'end' => '*/', 'escape' => false)), + 'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'), + 'string_quoting' => array('start' => "'", 'end' => "'", 'escape' => false, + 'escape_pattern' => false), + 'wildcards' => array('%', '_'), + 'varchar_max_length' => 255, + ); + + /** + * @var array $serverInfo + */ + protected $serverInfo = array(); + + /** + * + */ + protected $options = array(); + + /** + * List of all available drivers. + * + * @var array $availableDrivers + */ + private static $availableDrivers = array( + 'Mysql', 'Pgsql', 'Oracle', 'Informix', 'Mssql', 'Sqlite', 'Firebird' + ); + + /** + * The query count. Represents the number of executed database queries by the connection. + * + * @var integer + */ + protected $_count = 0; + + + /* + * ----------- EntityManager attributes --------------- + */ /** * The metadata factory is used to retrieve the metadata of entity classes. * @@ -81,38 +159,18 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun * @todo package:orm */ protected $_mappers = array(); - + /** - * $_name + * The EntityRepository instances. * - * Name of the connection - * - * @var string $_name + * @var array */ - protected $_name; + private $_repositories = array(); - /** - * The name of this connection driver. - * - * @var string $driverName + + /* + * ----------- Mixed attributes (need to split up) --------------- */ - protected $driverName; - - /** - * Whether or not a connection has been established. - * - * @var boolean $isConnected - */ - protected $isConnected = false; - - /** - * An array containing all features this driver supports, keys representing feature - * names and values as one of the following (true, false, 'emulated'). - * - * @var array $supported - */ - protected $supported = array(); - /** * @var array $pendingAttributes An array of pending attributes. When setting attributes * no connection is needed. When connected all the pending @@ -158,50 +216,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun 'formatter' => false, 'util' => false, ); - - /** - * @var array $properties an array of connection properties - */ - protected $properties = array('sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false), - array('start' => '/*', 'end' => '*/', 'escape' => false)), - 'identifier_quoting' => array('start' => '"', 'end' => '"','escape' => '"'), - 'string_quoting' => array('start' => "'", - 'end' => "'", - 'escape' => false, - 'escape_pattern' => false), - 'wildcards' => array('%', '_'), - 'varchar_max_length' => 255, - ); - - /** - * @var array $serverInfo - */ - protected $serverInfo = array(); - - /** - * - */ - protected $options = array(); - - /** - * @var array $availableDrivers an array containing all available drivers - */ - private static $availableDrivers = array( - 'Mysql', - 'Pgsql', - 'Oracle', - 'Informix', - 'Mssql', - 'Sqlite', - 'Firebird' - ); - - /** - * The query count. Represents the number of executed database queries by the connection. - * - * @var integer - */ - protected $_count = 0; + /** * the constructor @@ -239,7 +254,11 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this); } - + + + /* + * ----------- Connection methods --------------- + */ /** * getOption * @@ -267,39 +286,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->options[$option] = $value; } - - /** - * getAttribute - * retrieves a database connection attribute - * - * @param integer $attribute - * @return mixed - */ - public function getAttribute($attribute) - { - if ($attribute >= 100) { - if ( ! isset($this->_attributes[$attribute])) { - return parent::getAttribute($attribute); - } - return $this->_attributes[$attribute]; - } - - if ($this->isConnected) { - try { - return $this->dbh->getAttribute($attribute); - } catch (Exception $e) { - throw new Doctrine_Connection_Exception('Attribute ' . $attribute . ' not found.'); - } - } else { - if ( ! isset($this->pendingAttributes[$attribute])) { - $this->connect(); - $this->getAttribute($attribute); - } - - return $this->pendingAttributes[$attribute]; - } - } - + /** * returns an array of available PDO drivers */ @@ -307,32 +294,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return PDO::getAvailableDrivers(); } - - /** - * setAttribute - * sets an attribute - * - * @todo why check for >= 100? has this any special meaning when creating - * attributes? - * - * @param integer $attribute - * @param mixed $value - * @return boolean - */ - public function setAttribute($attribute, $value) - { - if ($attribute >= 100) { - parent::setAttribute($attribute, $value); - } else { - if ($this->isConnected) { - $this->dbh->setAttribute($attribute, $value); - } else { - $this->pendingAttributes[$attribute] = $value; - } - } - return $this; - } - + /** * getName * returns the name of this driver @@ -343,7 +305,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->_name; } - + /** * setName * @@ -368,56 +330,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->driverName; } - - /** - * __get - * lazy loads given module and returns it - * - * @see Doctrine_DataDict - * @see Doctrine_Expression - * @see Doctrine_Export - * @see Doctrine_Transaction - * @see Doctrine_Connection::$modules all availible modules - * @param string $name the name of the module to get - * @throws Doctrine_Connection_Exception if trying to get an unknown module - * @return Doctrine_Connection_Module connection module - */ - public function __get($name) - { - if (isset($this->properties[$name])) { - return $this->properties[$name]; - } - - if ( ! isset($this->modules[$name])) { - throw new Doctrine_Connection_Exception('Unknown module / property ' . $name); - } - if ($this->modules[$name] === false) { - switch ($name) { - case 'unitOfWork': - $this->modules[$name] = new Doctrine_Connection_UnitOfWork($this); - break; - case 'formatter': - $this->modules[$name] = new Doctrine_Formatter($this); - break; - default: - $class = 'Doctrine_' . ucwords($name) . '_' . $this->getDriverName(); - $this->modules[$name] = new $class($this); - } - } - - return $this->modules[$name]; - } - - /** - * returns the manager that created this connection - * - * @return Doctrine_Manager - */ - public function getManager() - { - return $this->getParent(); - } - + /** * returns the database handler which this connection uses * @@ -429,7 +342,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun return $this->dbh; } - + /** * Establishes the connection with the database. * @@ -510,7 +423,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun && ($this->supported[$feature] === 'emulated' || $this->supported[$feature])); } - + /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same @@ -849,32 +762,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->execute($statement, $params)->fetchAll(Doctrine::FETCH_BOTH); } - - /** - * query - * queries the database using Doctrine Query Language - * returns a collection of Doctrine_Record objects - * - * - * $users = $conn->query('SELECT u.* FROM User u'); - * - * $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone')); - * - * - * @param string $query DQL query - * @param array $params query parameters - * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD - * @see Doctrine_Query - * @return Doctrine_Collection Collection of Doctrine_Record objects - * @todo package:orm - */ - public function query($query, array $params = array(), $hydrationMode = null) - { - $parser = new Doctrine_Query($this); - - return $parser->query($query, $params, $hydrationMode); - } - + /** * prepare * @@ -903,37 +791,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $this->rethrowException($e, $this); } - - /** - * query - * queries the database using Doctrine Query Language and returns - * the first record found - * - * - * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1)); - * - * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?', - * array('someone', 'password') - * ); - * - * - * @param string $query DQL query - * @param array $params query parameters - * @see Doctrine_Query - * @return Doctrine_Record|false Doctrine_Record object on success, - * boolean false on failure - */ - public function queryOne($query, array $params = array()) - { - $parser = new Doctrine_Query($this); - - $coll = $parser->query($query, $params); - if ( ! $coll->contains(0)) { - return false; - } - return $coll[0]; - } - + /** * queries the database with limit and offset * added to the query and returns a Doctrine_Connection_Statement object @@ -977,7 +835,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun try { if ( ! empty($params)) { - //echo $query . "
"; $stmt = $this->prepare($query); $stmt->execute($params); return $stmt; @@ -987,14 +844,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $this->getAttribute(Doctrine::ATTR_LISTENER)->preQuery($event); if ( ! $event->skipOperation) { - //try { - $stmt = $this->dbh->query($query); - /*} catch (Exception $e) { - if (strstr($e->getMessage(), 'no such column')) { - echo $query . "

"; - } - }*/ - + $stmt = $this->dbh->query($query); $this->_count++; } $this->getAttribute(Doctrine::ATTR_LISTENER)->postQuery($event); @@ -1021,6 +871,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun if ( ! empty($params)) { $stmt = $this->prepare($query); $stmt->execute($params); + return $stmt->rowCount(); } else { $event = new Doctrine_Event($this, Doctrine_Event::CONN_EXEC, $query, $params); @@ -1052,7 +903,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $this->getListener()->preError($event); $name = 'Doctrine_Connection_' . $this->driverName . '_Exception'; - + $exc = new $name($e->getMessage(), (int) $e->getCode()); if ( ! is_array($e->errorInfo)) { $e->errorInfo = array(null, null, null, null); @@ -1065,151 +916,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun $this->getListener()->postError($event); } - - /** - * hasTable - * whether or not this connection has table $name initialized - * - * @param mixed $name - * @return boolean - * @deprecated - * @todo package:orm - */ - public function hasTable($name) - { - return isset($this->tables[$name]); - } - - /** - * Returns the metadata for a class. - * - * @return Doctrine_Metadata - * @deprecated Use getClassMetadata() - * @todo package:orm - */ - public function getMetadata($className) - { - return $this->getClassMetadata($className); - } - - /** - * Returns the metadata for a class. - * - * @return Doctrine_Metadata - * @todo package:orm - */ - public function getClassMetadata($className) - { - if ( ! $this->_metadataFactory) { - $this->_metadataFactory = new Doctrine_ClassMetadata_Factory($this, - new Doctrine_ClassMetadata_CodeDriver()); - } - - return $this->_metadataFactory->getMetadataFor($className); - } - - /** - * Sets the driver that is used to obtain metadata informations about entity - * classes. - * - * @param $driver The driver to use. - * @todo package:orm - */ - public function setClassMetadataDriver($driver) - { - $this->_metadataFactory->setDriver($driver); - } - /** - * 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. - * - * @param string $entityClassName The name of the entity class. - * @return Doctrine_Mapper The mapper object. - * @todo package:orm - */ - /*public function getMapper($entityName) - { - if (isset($this->_mappers[$entityName])) { - return $this->_mappers[$entityName]; - } - - $metadata = $this->getClassMetadata($entityName); - $customMapperClassName = $metadata->getCustomMapperClass(); - if ($customMapperClassName !== null) { - $mapper = new $customMapperClassName($entityName, $metadata); - } else { - // instantiate correct mapper type - $inheritanceType = $metadata->getInheritanceType(); - if ($inheritanceType == Doctrine::INHERITANCE_TYPE_JOINED) { - $mapper = new Doctrine_Mapper_Joined($entityName, $metadata); - } else if ($inheritanceType == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) { - $mapper = new Doctrine_Mapper_SingleTable($entityName, $metadata); - } else if ($inheritanceType == Doctrine::INHERITANCE_TYPE_TABLE_PER_CLASS) { - $mapper = new Doctrine_Mapper_TablePerClass($entityName, $metadata); - } else { - throw new Doctrine_Connection_Exception("Unknown inheritance type '$inheritanceType'. Can't create mapper."); - } - } - - $this->_mappers[$entityName] = $mapper; - - return $mapper; - }*/ - - /** - * 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. - * - * @param string $entityClassName The name of the entity class. - * @return Doctrine_Mapper The mapper object. - * @todo package:orm - */ - public function getMapper($entityName) - { - if (isset($this->_mappers[$entityName])) { - return $this->_mappers[$entityName]; - } - - $metadata = $this->getClassMetadata($entityName); - $customMapperClassName = $metadata->getCustomMapperClass(); - if ($customMapperClassName !== null) { - $mapper = new $customMapperClassName($entityName, $metadata); - } else { - $mapper = new Doctrine_Mapper($entityName, $metadata); - } - $this->_mappers[$entityName] = $mapper; - - return $mapper; - } - - /** - * Gets all mappers that are currently maintained by the connection. - * - * @todo package:orm - */ - public function getMappers() - { - return $this->_mappers; - } - - /** - * returns an iterator that iterates through all - * initialized table objects - * - * - * foreach ($conn as $index => $table) { - * print $table; // get a string representation of each table object - * } - * - * - * @return ArrayIterator SPL ArrayIterator object - */ - public function getIterator() - { - return new ArrayIterator($this->_mappers); - } - /** * Returns the number of queries executed by the connection. * @@ -1220,85 +927,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->_count; } - - /** - * create - * creates a record - * - * create creates a record - * @param string $name component name - * @return Doctrine_Record Doctrine_Record object - * @todo Any strong reasons why this should not be removed? - * @todo package:orm - */ - public function create($name) - { - return $this->getMapper($name)->create(); - } - - /** - * Creates a new Doctrine_Query object that operates on this connection. - * - * @return Doctrine_Query - * @todo package:orm - */ - public function createQuery($dql = "") - { - $query = new Doctrine_Query($this); - if ( ! empty($dql)) { - $query->parseQuery($dql); - } - - return $query; - } - - /** - * flush - * saves all the records from all tables - * this operation is isolated using a transaction - * - * @throws PDOException if something went wrong at database level - * @return void - * @todo package:orm - */ - public function flush() - { - $this->beginInternalTransaction(); - $this->unitOfWork->flush(); - $this->commit(); - } - - /** - * clear - * clears all repositories - * - * @return void - * @todo package:orm - */ - public function clear() - { - $this->unitOfWork->detachAll(); - foreach ($this->_mappers as $mapper) { - $mapper->clear(); // clear identity map of each mapper - } - } - - /** - * evictTables - * evicts all tables - * - * @return void - * @todo package:orm - * @deprecated - */ - public function evictTables() - { - $this->clear(); - $this->tables = array(); - $this->_mappers = array(); - $this->exported = array(); - } - + /** * Closes the connection. * @@ -1326,18 +955,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->transaction->getTransactionLevel(); } - - /** - * Returns the current internal transaction nesting level. - * - * @return integer The nesting level. A value of 0 means theres no active transaction. - * @todo package:orm??? - */ - public function getInternalTransactionLevel() - { - return $this->transaction->getInternalTransactionLevel(); - } - + /** * errorCode * Fetch the SQLSTATE associated with the last operation on the database handle @@ -1363,48 +981,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun return $this->dbh->errorInfo(); } - - /** - * getCacheDriver - * - * @return Doctrine_Cache_Interface - * @deprecated Use getResultCacheDriver() - */ - public function getCacheDriver() - { - return $this->getResultCacheDriver(); - } - - /** - * getResultCacheDriver - * - * @return Doctrine_Cache_Interface - * @todo package:orm - */ - public function getResultCacheDriver() - { - if ( ! $this->getAttribute(Doctrine::ATTR_RESULT_CACHE)) { - throw new Doctrine_Exception('Result Cache driver not initialized.'); - } - - return $this->getAttribute(Doctrine::ATTR_RESULT_CACHE); - } - - /** - * getQueryCacheDriver - * - * @return Doctrine_Cache_Interface - * @todo package:orm - */ - public function getQueryCacheDriver() - { - if ( ! $this->getAttribute(Doctrine::ATTR_QUERY_CACHE)) { - throw new Doctrine_Exception('Query Cache driver not initialized.'); - } - - return $this->getAttribute(Doctrine::ATTR_QUERY_CACHE); - } - + /** * lastInsertId * @@ -1439,20 +1016,7 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun { return $this->transaction->beginTransaction($savepoint); } - - /** - * Initiates a transaction. - * - * This method must only be used by Doctrine itself to initiate transactions. - * Userland-code must use {@link beginTransaction()}. - * - * @todo package:orm??? - */ - public function beginInternalTransaction($savepoint = null) - { - return $this->transaction->beginInternalTransaction($savepoint); - } - + /** * commit * Commit the database changes done during a transaction that is in @@ -1564,47 +1128,491 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun return Doctrine_Lib::getConnectionAsString($this); } - public function hasAttribute($key) + + /* + * ----------- EntityManager methods --------------- + */ + + /** + * query + * queries the database using Doctrine Query Language + * returns a collection of Doctrine_Record objects + * + * + * $users = $conn->query('SELECT u.* FROM User u'); + * + * $users = $conn->query('SELECT u.* FROM User u WHERE u.name LIKE ?', array('someone')); + * + * + * @param string $query DQL query + * @param array $params query parameters + * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD + * @see Doctrine_Query + * @return Doctrine_Collection Collection of Doctrine_Record objects + * @todo package:orm + */ + public function query($query, array $params = array(), $hydrationMode = null) { - switch ($key) { - case Doctrine::ATTR_COLL_KEY: - case Doctrine::ATTR_LISTENER: - case Doctrine::ATTR_RECORD_LISTENER: - case Doctrine::ATTR_QUOTE_IDENTIFIER: - case Doctrine::ATTR_SEQCOL_NAME: - case Doctrine::ATTR_FIELD_CASE: - case Doctrine::ATTR_IDXNAME_FORMAT: - case Doctrine::ATTR_SEQNAME_FORMAT: - case Doctrine::ATTR_DBNAME_FORMAT: - case Doctrine::ATTR_TBLCLASS_FORMAT: - case Doctrine::ATTR_TBLNAME_FORMAT: - case Doctrine::ATTR_EXPORT: - case Doctrine::ATTR_DECIMAL_PLACES: - case Doctrine::ATTR_PORTABILITY: - case Doctrine::ATTR_VALIDATE: - case Doctrine::ATTR_QUERY_LIMIT: - case Doctrine::ATTR_DEFAULT_TABLE_TYPE: - case Doctrine::ATTR_DEF_TEXT_LENGTH: - case Doctrine::ATTR_DEF_VARCHAR_LENGTH: - case Doctrine::ATTR_DEF_TABLESPACE: - case Doctrine::ATTR_EMULATE_DATABASE: - case Doctrine::ATTR_USE_NATIVE_ENUM: - case Doctrine::ATTR_CREATE_TABLES: - case Doctrine::ATTR_COLL_LIMIT: - case Doctrine::ATTR_CACHE: // deprecated - case Doctrine::ATTR_RESULT_CACHE: - case Doctrine::ATTR_CACHE_LIFESPAN: // deprecated - case Doctrine::ATTR_RESULT_CACHE_LIFESPAN: - case Doctrine::ATTR_LOAD_REFERENCES: - case Doctrine::ATTR_THROW_EXCEPTIONS: - case Doctrine::ATTR_QUERY_CACHE: - case Doctrine::ATTR_QUERY_CACHE_LIFESPAN: - case Doctrine::ATTR_MODEL_LOADING: - case Doctrine::ATTR_METADATA_CACHE: - case Doctrine::ATTR_METADATA_CACHE_LIFESPAN: - return true; - default: - return false; + $parser = new Doctrine_Query($this); + + return $parser->query($query, $params, $hydrationMode); + } + + /** + * query + * queries the database using Doctrine Query Language and returns + * the first record found + * + * + * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.id = ?', array(1)); + * + * $user = $conn->queryOne('SELECT u.* FROM User u WHERE u.name LIKE ? AND u.password = ?', + * array('someone', 'password') + * ); + * + * + * @param string $query DQL query + * @param array $params query parameters + * @see Doctrine_Query + * @return Doctrine_Record|false Doctrine_Record object on success, + * boolean false on failure + */ + public function queryOne($query, array $params = array()) + { + $parser = new Doctrine_Query($this); + + $coll = $parser->query($query, $params); + if ( ! $coll->contains(0)) { + return false; + } + return $coll[0]; + } + + /** + * hasTable + * whether or not this connection has table $name initialized + * + * @param mixed $name + * @return boolean + * @deprecated + * @todo package:orm + */ + public function hasTable($name) + { + return isset($this->tables[$name]); + } + + /** + * Returns the metadata for a class. + * + * @return Doctrine_Metadata + * @deprecated Use getClassMetadata() + * @todo package:orm + */ + public function getMetadata($className) + { + return $this->getClassMetadata($className); + } + + /** + * Returns the metadata for a class. + * + * @return Doctrine_Metadata + * @todo package:orm + */ + public function getClassMetadata($className) + { + if ( ! $this->_metadataFactory) { + $this->_metadataFactory = new Doctrine_ClassMetadata_Factory($this, + new Doctrine_ClassMetadata_CodeDriver()); + } + + return $this->_metadataFactory->getMetadataFor($className); + } + + /** + * Sets the driver that is used to obtain metadata informations about entity + * classes. + * + * @param $driver The driver to use. + * @todo package:orm + */ + public function setClassMetadataDriver($driver) + { + $this->_metadataFactory->setDriver($driver); + } + + /** + * 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. + * + * @param string $entityClassName The name of the entity class. + * @return Doctrine_Mapper The mapper object. + * @todo package:orm + */ + public function getMapper($entityName) + { + if (isset($this->_mappers[$entityName])) { + return $this->_mappers[$entityName]; + } + + $metadata = $this->getClassMetadata($entityName); + $customMapperClassName = $metadata->getCustomMapperClass(); + if ($customMapperClassName !== null) { + $mapper = new $customMapperClassName($entityName, $metadata); + } else { + $mapper = new Doctrine_Mapper($entityName, $metadata); + } + $this->_mappers[$entityName] = $mapper; + + return $mapper; + } + + /** + * Gets all mappers that are currently maintained by the connection. + * + * @todo package:orm + */ + public function getMappers() + { + return $this->_mappers; + } + + /** + * returns an iterator that iterates through all + * initialized table objects + * + * + * foreach ($conn as $index => $table) { + * print $table; // get a string representation of each table object + * } + * + * + * @return ArrayIterator SPL ArrayIterator object + */ + public function getIterator() + { + return new ArrayIterator($this->_mappers); + } + + /** + * create + * creates a record + * + * create creates a record + * @param string $name component name + * @return Doctrine_Record Doctrine_Record object + * @todo Any strong reasons why this should not be removed? + * @todo package:orm + */ + public function create($name) + { + return $this->getMapper($name)->create(); + } + + /** + * Creates a new Doctrine_Query object that operates on this connection. + * + * @return Doctrine_Query + * @todo package:orm + */ + public function createQuery($dql = "") + { + $query = new Doctrine_Query($this); + if ( ! empty($dql)) { + $query->parseQuery($dql); + } + + return $query; + } + + /** + * flush + * saves all the records from all tables + * this operation is isolated using a transaction + * + * @throws PDOException if something went wrong at database level + * @return void + * @todo package:orm + */ + public function flush() + { + $this->beginInternalTransaction(); + $this->unitOfWork->flush(); + $this->commit(); + } + + /** + * clear + * clears all repositories + * + * @return void + * @todo package:orm + */ + public function clear($entityName = null) + { + if ($entityName === null) { + $this->unitOfWork->detachAll(); + foreach ($this->_mappers as $mapper) { + $mapper->clear(); // clear identity map of each mapper + } + } else { + $this->getMapper($entityName)->clear(); } } + + /** + * evictTables + * evicts all tables + * + * @return void + * @todo package:orm + * @deprecated + */ + public function evictTables() + { + $this->clear(); + $this->tables = array(); + $this->_mappers = array(); + $this->exported = array(); + } + + public function save(Doctrine_Record $entity, $conn = null) + { + $this->getMapper($entity->getClassName())->save($entity, $conn); + } + + public function remove(Doctrine_Record $entity, $conn = null) + { + $this->getMapper($entity->getClassName())->delete($entity, $conn); + } + + public function createEntity($entityName, array $data = array()) + { + return $this->getMapper($entityName)->create($data); + } + + public function detach(Doctrine_Record $entity) + { + $this->getMapper($entity->getClassName())->detach($entity); + } + + public function removeRecord(Doctrine_Record $entity) + { + $this->getMapper($entity->getClassName())->removeRecord($entity); + } + + public function manage(Doctrine_Record $entity) + { + $this->getMapper($entity->getClassName())->manage($entity); + } + + public function executeNamedQuery($name, $params = array(), $hydrationMode = Doctrine::HYDRATE_RECORD) + { + return Doctrine_Manager::getInstance() + ->createNamedQuery($name) + ->execute($params, $hydrationMode); + } + + + + /** + * Returns the current internal transaction nesting level. + * + * @return integer The nesting level. A value of 0 means theres no active transaction. + * @todo package:orm??? + */ + public function getInternalTransactionLevel() + { + return $this->transaction->getInternalTransactionLevel(); + } + + /** + * getCacheDriver + * + * @return Doctrine_Cache_Interface + * @deprecated Use getResultCacheDriver() + */ + public function getCacheDriver() + { + return $this->getResultCacheDriver(); + } + + /** + * getResultCacheDriver + * + * @return Doctrine_Cache_Interface + * @todo package:orm + */ + public function getResultCacheDriver() + { + if ( ! $this->getAttribute(Doctrine::ATTR_RESULT_CACHE)) { + throw new Doctrine_Exception('Result Cache driver not initialized.'); + } + + return $this->getAttribute(Doctrine::ATTR_RESULT_CACHE); + } + + /** + * getQueryCacheDriver + * + * @return Doctrine_Cache_Interface + * @todo package:orm + */ + public function getQueryCacheDriver() + { + if ( ! $this->getAttribute(Doctrine::ATTR_QUERY_CACHE)) { + throw new Doctrine_Exception('Query Cache driver not initialized.'); + } + + return $this->getAttribute(Doctrine::ATTR_QUERY_CACHE); + } + + /** + * Initiates a transaction. + * + * This method must only be used by Doctrine itself to initiate transactions. + * Userland-code must use {@link beginTransaction()}. + * + * @todo package:orm??? + */ + public function beginInternalTransaction($savepoint = null) + { + return $this->transaction->beginInternalTransaction($savepoint); + } + + /** + * Enter description here... + * + * @todo To EntityManager + */ + public function getRepository($entityName) + { + if (isset($this->_repositories[$entityName])) { + return $this->_repositories[$entityName]; + } + + $metadata = $this->getClassMetadata($entityName); + $customRepositoryClassName = $metadata->getCustomRepositoryClass(); + if ($customRepositoryClassName !== null) { + $repository = new $customRepositoryClassName($entityName, $metadata); + } else { + $repository = new Doctrine_EntityRepository($entityName, $metadata); + } + $this->_repositories[$entityName] = $repository; + + return $repository; + } + + + /* + * ----------- Mixed methods (need to figure out where they go) --------------- + */ + + /** + * getAttribute + * retrieves a database connection attribute + * + * @param integer $attribute + * @return mixed + */ + public function getAttribute($attribute) + { + if ($attribute >= 100) { + if ( ! isset($this->_attributes[$attribute])) { + return parent::getAttribute($attribute); + } + return $this->_attributes[$attribute]; + } + + if ($this->isConnected) { + try { + return $this->dbh->getAttribute($attribute); + } catch (Exception $e) { + throw new Doctrine_Connection_Exception('Attribute ' . $attribute . ' not found.'); + } + } else { + if ( ! isset($this->pendingAttributes[$attribute])) { + $this->connect(); + $this->getAttribute($attribute); + } + + return $this->pendingAttributes[$attribute]; + } + } + + /** + * setAttribute + * sets an attribute + * + * @todo why check for >= 100? has this any special meaning when creating + * attributes? + * + * @param integer $attribute + * @param mixed $value + * @return boolean + */ + public function setAttribute($attribute, $value) + { + if ($attribute >= 100) { + parent::setAttribute($attribute, $value); + } else { + if ($this->isConnected) { + $this->dbh->setAttribute($attribute, $value); + } else { + $this->pendingAttributes[$attribute] = $value; + } + } + return $this; + } + + /** + * __get + * lazy loads given module and returns it + * + * @see Doctrine_DataDict + * @see Doctrine_Expression + * @see Doctrine_Export + * @see Doctrine_Transaction + * @see Doctrine_Connection::$modules all availible modules + * @param string $name the name of the module to get + * @throws Doctrine_Connection_Exception if trying to get an unknown module + * @return Doctrine_Connection_Module connection module + */ + public function __get($name) + { + if (isset($this->properties[$name])) { + return $this->properties[$name]; + } + + if ( ! isset($this->modules[$name])) { + throw new Doctrine_Connection_Exception('Unknown module / property ' . $name); + } + if ($this->modules[$name] === false) { + switch ($name) { + case 'unitOfWork': + $this->modules[$name] = new Doctrine_Connection_UnitOfWork($this); + break; + case 'formatter': + $this->modules[$name] = new Doctrine_Formatter($this); + break; + default: + $class = 'Doctrine_' . ucwords($name) . '_' . $this->getDriverName(); + $this->modules[$name] = new $class($this); + } + } + + return $this->modules[$name]; + } + + /** + * returns the manager that created this connection + * + * @return Doctrine_Manager + */ + public function getManager() + { + return $this->getParent(); + } + } diff --git a/lib/Doctrine/Connection/UnitOfWork.php b/lib/Doctrine/Connection/UnitOfWork.php index 7c8f09438..082bac886 100644 --- a/lib/Doctrine/Connection/UnitOfWork.php +++ b/lib/Doctrine/Connection/UnitOfWork.php @@ -20,7 +20,23 @@ */ /** - * Doctrine_Connection_UnitOfWork + * The UnitOfWork is responsible for writing out changes to the database at + * the correct time and in the correct order. + * + * Some terminology: + * + * New entity: From the point of view of the unitOfWork is an entity that + * already has an identity but is not yet persisted into the database. This + * is usually the case for all newly saved entities that use a SEQUENCE id + * generator. Entities with an IDENTITY id generator get persisted as soon + * as they're saved in order to obtain the identifier. Therefore entities that + * use an IDENTITY id generator never appear in the list of new entities of the UoW. + * + * Dirty entity: ... + * + * Removed entity: ... + * + * Clean entity: ... * * @package Doctrine * @subpackage Connection @@ -105,12 +121,19 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module */ public function registerNew(Doctrine_Record $entity) { - if (isset($this->_dirtyEntities[$entity->getOid()])) { - throw new Doctrine_Connection_Exception("Dirty object can't be registered as new."); - } else if (isset($this->_removedEntities[$entity->getOid()])) { - throw new Doctrine_Connection_Exception("Removed object can't be registered as new."); + if ( ! $entity->identifier()) { + throw new Doctrine_Connection_Exception("Entity without identity " + . "can't be registered as new."); } - $this->_newEntities[$entity->getOid()] = $entity; + $oid = $entity->getOid(); + if (isset($this->_dirtyEntities[$oid])) { + throw new Doctrine_Connection_Exception("Dirty object can't be registered as new."); + } else if (isset($this->_removedEntities[$oid])) { + throw new Doctrine_Connection_Exception("Removed object can't be registered as new."); + } else if (isset($this->_newEntities[$oid])) { + throw new Doctrine_Connection_Exception("Object already registered as new. Can't register twice."); + } + $this->_newEntities[$oid] = $entity; } public function isRegisteredNew(Doctrine_Record $entity) @@ -131,12 +154,17 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module */ public function registerDirty(Doctrine_Record $entity) { + if ( ! $entity->identifier()) { + throw new Doctrine_Connection_Exception("Entity without identity " + . "can't be registered as dirty."); + } + $oid = $entity->getOid(); if (isset($this->_removedEntities[$entity->getOid()])) { throw new Doctrine_Connection_Exception("Removed object can't be registered as dirty."); - } else if (isset($this->_newEntities[$entity->getOid()])) { - throw new Doctrine_Connection_Exception(""); } - $this->_dirtyEntities[$entity->getOid()] = $entity; + if ( ! isset($this->_dirtyEntities[$oid], $this->_newEntities[$oid])) { + $this->_dirtyEntities[$entity->getOid()] = $entity; + } } public function isRegisteredDirty(Doctrine_Record $entity) @@ -149,8 +177,21 @@ class Doctrine_Connection_UnitOfWork extends Doctrine_Connection_Module */ public function registerRemoved(Doctrine_Record $entity) { + if ($entity->isTransient()) { + return; + } $this->unregisterIdentity($entity); - $this->_removedEntities[$entity->getOid()] = $entity; + $oid = $entity->getOid(); + if (isset($this->_newEntities[$oid])) { + unset($this->_newEntities[$oid]); + return; + } + if (isset($this->_dirtyEntities[$oid])) { + unset($this->_dirtyEntities[$oid]); + } + if ( ! isset($this->_removedEntities[$oid])) { + $this->_removedEntities[$oid] = $entity; + } } public function isRegisteredRemoved(Doctrine_Record $entity) diff --git a/lib/Doctrine/EntityRepository.php b/lib/Doctrine/EntityRepository.php new file mode 100644 index 000000000..d4e66ebe8 --- /dev/null +++ b/lib/Doctrine/EntityRepository.php @@ -0,0 +1,225 @@ +. + */ + +/** + * Base class for all custom user-defined repositories. + * Provides basic finder methods, common to all repositories. + * + * @package Doctrine + * @subpackage EntityRepository + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.org + * @since 2.0 + * @version $Revision$ + * @author Roman Borschel + * @todo package:orm + */ +class Doctrine_EntityRepository +{ + protected $_entityName; + protected $_conn; + protected $_classMetadata; + + public function __construct($entityName, Doctrine_ClassMetadata $classMetadata) + { + $this->_entityName = $entityName; + $this->_conn = $classMetadata->getConnection(); + $this->_classMetadata = $classMetadata; + } + + /** + * createQuery + * creates a new Doctrine_Query object and adds the component name + * of this table as the query 'from' part + * + * @param string Optional alias name for component aliasing. + * + * @return Doctrine_Query + */ + protected function _createQuery($alias = '') + { + if ( ! empty($alias)) { + $alias = ' ' . trim($alias); + } + return Doctrine_Query::create($this->_conn)->from($this->_entityName . $alias); + } + + /** + * clear + * clears the first level cache (identityMap) + * + * @return void + * @todo what about a more descriptive name? clearIdentityMap? + */ + public function clear() + { + $this->_conn->unitOfWork->clearIdentitiesForEntity($this->_classMetadata->getRootClassName()); + } + + /** + * Finds an entity by its primary key. + * + * @param $id database row id + * @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD + * @return mixed Array or Doctrine_Record or false if no result + * @todo Remove. Move to EntityRepository. + */ + public function find($id, $hydrationMode = null) + { + if (is_null($id)) { + return false; + } + + if (is_array($id) && count($id) > 1) { + // it's a composite key. keys = field names, values = values. + $values = array_values($id); + $keys = array_keys($id); + } else { + $values = is_array($id) ? array_values($id) : array($id); + $keys = $this->_classMetadata->getIdentifier(); + } + + return $this->_createQuery() + ->where(implode(' = ? AND ', $keys) . ' = ?') + ->fetchOne($values, $hydrationMode); + } + + /** + * Finds all entities of the mapper's class. + * Use with care. + * + * @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD + * @return Doctrine_Collection + */ + public function findAll($hydrationMode = null) + { + return $this->_createQuery()->execute(array(), $hydrationMode); + } + + /** + * findBy + * + * @param string $column + * @param string $value + * @param string $hydrationMode + * @return void + */ + protected function findBy($fieldName, $value, $hydrationMode = null) + { + return $this->_createQuery()->where($fieldName . ' = ?')->execute(array($value), $hydrationMode); + } + + /** + * findOneBy + * + * @param string $column + * @param string $value + * @param string $hydrationMode + * @return void + */ + protected function findOneBy($fieldName, $value, $hydrationMode = null) + { + $results = $this->_createQuery()->where($fieldName . ' = ?')->limit(1)->execute( + array($value), $hydrationMode); + return $hydrationMode === Doctrine::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst(); + } + + /** + * findBySql + * finds records with given SQL where clause + * returns a collection of records + * + * @param string $dql DQL after WHERE clause + * @param array $params query parameters + * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD + * @return Doctrine_Collection + * + * @todo This actually takes DQL, not SQL, but it requires column names + * instead of field names. This should be fixed to use raw SQL instead. + */ + public function findBySql($dql, array $params = array(), $hydrationMode = null) + { + return $this->_createQuery()->where($dql)->execute($params, $hydrationMode); + } + + /** + * findByDql + * finds records with given DQL where clause + * returns a collection of records + * + * @param string $dql DQL after WHERE clause + * @param array $params query parameters + * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD + * @return Doctrine_Collection + */ + public function findByDql($dql, array $params = array(), $hydrationMode = null) + { + $query = new Doctrine_Query($this->_conn); + $component = $this->getComponentName(); + $dql = 'FROM ' . $component . ' WHERE ' . $dql; + + return $query->query($dql, $params, $hydrationMode); + } + + /** + * Adds support for magic finders. + * findByColumnName, findByRelationAlias + * findById, findByContactId, etc. + * + * @return void + * @throws Doctrine_Mapper_Exception If the method called is an invalid find* method + * or no find* method at all and therefore an invalid + * method call. + */ + public function __call($method, $arguments) + { + if (substr($method, 0, 6) == 'findBy') { + $by = substr($method, 6, strlen($method)); + $method = 'findBy'; + } else if (substr($method, 0, 9) == 'findOneBy') { + $by = substr($method, 9, strlen($method)); + $method = 'findOneBy'; + } else { + throw new Doctrine_Mapper_Exception("Undefined method '$method'."); + } + + if (isset($by)) { + if ( ! isset($arguments[0])) { + throw new Doctrine_Mapper_Exception('You must specify the value to findBy.'); + } + + $fieldName = Doctrine::tableize($by); + $hydrationMode = isset($arguments[1]) ? $arguments[1]:null; + + if ($this->_classMetadata->hasField($fieldName)) { + return $this->$method($fieldName, $arguments[0], $hydrationMode); + } else if ($this->_classMetadata->hasRelation($by)) { + $relation = $this->_classMetadata->getRelation($by); + if ($relation['type'] === Doctrine_Relation::MANY) { + throw new Doctrine_Mapper_Exception('Cannot findBy many relationship.'); + } + return $this->$method($relation['local'], $arguments[0], $hydrationMode); + } else { + throw new Doctrine_Mapper_Exception('Cannot find by: ' . $by . '. Invalid field or relationship alias.'); + } + } + } +} \ No newline at end of file diff --git a/lib/Doctrine/Hydrator.php b/lib/Doctrine/Hydrator.php index 331237a08..f424f51d0 100644 --- a/lib/Doctrine/Hydrator.php +++ b/lib/Doctrine/Hydrator.php @@ -52,13 +52,14 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract * 'table' => Table object, * 'parent' => Parent DQL alias (if any), * 'relation' => Relation object (if any), - * 'map' => Custom index to use as the key in the result (if any) + * 'map' => Custom index to use as the key in the result (if any), + * 'agg' => List of aggregate values (sql alias => dql alias) * ) * ) * @return mixed The created object/array graph. */ public function hydrateResultSet($stmt, $tableAliases, $hydrationMode = null) - { + { if ($hydrationMode === null) { $hydrationMode = $this->_hydrationMode; } @@ -82,7 +83,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract // Used variables during hydration reset($this->_queryComponents); $rootAlias = key($this->_queryComponents); - $rootComponentName = $this->_queryComponents[$rootAlias]['mapper']->getComponentName(); + $rootComponentName = $this->_queryComponents[$rootAlias]['table']->getComponentName(); // if only one component is involved we can make our lives easier $isSimpleQuery = count($this->_queryComponents) <= 1; // Holds hydration listeners that get called during hydration @@ -110,7 +111,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract foreach ($this->_queryComponents as $dqlAlias => $component) { // disable lazy-loading of related elements during hydration $component['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false); - $componentName = $component['mapper']->getComponentName(); + $componentName = $component['table']->getClassName(); $listeners[$componentName] = $component['table']->getRecordListener(); $identifierMap[$dqlAlias] = array(); $prev[$dqlAlias] = array(); @@ -127,9 +128,8 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract // // hydrate the data of the root entity from the current row // - $table = $this->_queryComponents[$rootAlias]['table']; - $mapper = $this->_queryComponents[$rootAlias]['mapper']; - $componentName = $mapper->getComponentName(); + $class = $this->_queryComponents[$rootAlias]['table']; + $componentName = $class->getComponentName(); // just event stuff $event->set('data', $rowData[$rootAlias]); @@ -175,9 +175,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract foreach ($rowData as $dqlAlias => $data) { $index = false; $map = $this->_queryComponents[$dqlAlias]; - $table = $map['table']; - $mapper = $map['mapper']; - $componentName = $mapper->getComponentName(); + $componentName = $map['table']->getComponentName(); // just event stuff $event->set('data', $data); @@ -186,7 +184,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract $parent = $map['parent']; $relation = $map['relation']; - $relationAlias = $map['relation']->getAlias(); + $relationAlias = $relation->getAlias(); $path = $parent . '.' . $dqlAlias; @@ -195,16 +193,19 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract } // check the type of the relation - if ( ! $relation->isOneToOne() && $driver->initRelated($prev[$parent], $relationAlias)) { + if ( ! $relation->isOneToOne()) { $oneToOne = false; // append element if (isset($nonemptyComponents[$dqlAlias])) { + $driver->initRelated($prev[$parent], $relationAlias); if ( ! isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) { $element = $driver->getElement($data, $componentName); + // just event stuff $event->set('data', $element); $listeners[$componentName]->postHydrate($event); //-- + if ($field = $this->_getCustomIndexField($dqlAlias)) { // TODO: we should check this earlier. Fields used in INDEXBY // must be unique. Then this can be removed here. @@ -223,7 +224,7 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract $index = $identifierMap[$path][$id[$parent]][$id[$dqlAlias]]; } // register collection for later snapshots - $driver->registerCollection($prev[$parent][$relationAlias]); + //$driver->registerCollection($prev[$parent][$relationAlias]); } } else { // 1-1 relation @@ -315,11 +316,17 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract if ( ! isset($cache[$key])) { // cache general information like the column name <-> field name mapping $e = explode('__', $key); - $columnName = strtolower(array_pop($e)); + $columnName = strtolower(array_pop($e)); $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))]; $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; $classMetadata = $mapper->getClassMetadata(); - $fieldName = $mapper->getFieldName($columnName); + // check whether it's an aggregate value or a regular field + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) { + $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName]; + } else { + $fieldName = $mapper->getFieldName($columnName); + } + $cache[$key]['fieldName'] = $fieldName; // cache identifier information @@ -343,10 +350,6 @@ class Doctrine_Hydrator extends Doctrine_Hydrator_Abstract $dqlAlias = $cache[$key]['dqlAlias']; $fieldName = $cache[$key]['fieldName']; - if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) { - $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName]; - } - if ($cache[$key]['isIdentifier']) { $id[$dqlAlias] .= '|' . $value; } diff --git a/lib/Doctrine/Hydrator/ArrayDriver.php b/lib/Doctrine/Hydrator/ArrayDriver.php index 52e9101df..8bdcc81dc 100644 --- a/lib/Doctrine/Hydrator/ArrayDriver.php +++ b/lib/Doctrine/Hydrator/ArrayDriver.php @@ -71,8 +71,6 @@ class Doctrine_Hydrator_ArrayDriver if ( ! isset($data[$name])) { $data[$name] = array(); } - - return true; } /** diff --git a/lib/Doctrine/Hydrator/RecordDriver.php b/lib/Doctrine/Hydrator/RecordDriver.php index 7c70203be..fe6094cf2 100644 --- a/lib/Doctrine/Hydrator/RecordDriver.php +++ b/lib/Doctrine/Hydrator/RecordDriver.php @@ -20,7 +20,7 @@ */ /** - * Doctrine_Hydrate_RecordDriver + * Doctrine_Hydrator_RecordDriver * Hydration strategy used for creating graphs of entity objects. * * @package Doctrine @@ -34,8 +34,13 @@ */ class Doctrine_Hydrator_RecordDriver { + /** Collections initialized by the driver */ protected $_collections = array(); + /** Mappers */ protected $_mappers = array(); + /** Memory for initialized relations */ + private $_initializedRelations = array(); + /** Null object */ private $_nullObject; public function __construct() @@ -53,54 +58,32 @@ class Doctrine_Hydrator_RecordDriver public function getLastKey($coll) { - $coll->end(); - - return $coll->key(); + // check needed because of mixed results + if (is_array($coll)) { + end($coll); + return key($coll); + } else { + $coll->end(); + return $coll->key(); + } } - public function initRelated($record, $name) + public function initRelated(Doctrine_Record $record, $name) { - return true; - /* - if ( ! is_array($record)) { - $record[$name]; - return true; + if ( ! isset($this->_initializedRelations[$record->getOid()][$name])) { + $relation = $record->getClassMetadata()->getRelation($name); + $relatedClass = $relation->getTable(); + $coll = $this->getElementCollection($relatedClass->getClassName()); + $coll->setReference($record, $relation); + $record[$name] = $coll; + $this->_initializedRelations[$record->getOid()][$name] = true; } - return false; - */ } public function registerCollection(Doctrine_Collection $coll) { $this->_collections[] = $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 Doctrine_Table $table - * @return boolean - */ - /*public function isIdentifiable(array $row, Doctrine_Table $table) - { - $primaryKeys = $table->getIdentifierColumnNames(); - - if (is_array($primaryKeys)) { - foreach ($primaryKeys as $id) { - if ( ! isset($row[$id])) { - return false; - } - } - } else { - if ( ! isset($row[$primaryKeys])) { - return false; - } - } - return true; - }*/ public function getNullPointer() { @@ -127,6 +110,7 @@ class Doctrine_Hydrator_RecordDriver } $this->_collections = array(); $this->_mappers = array(); + $this->_initializedRelations = array(); } /** diff --git a/lib/Doctrine/HydratorNew.php b/lib/Doctrine/HydratorNew.php new file mode 100644 index 000000000..b730ab38d --- /dev/null +++ b/lib/Doctrine/HydratorNew.php @@ -0,0 +1,422 @@ +. + */ + +/** + * The hydrator has the tedious task to construct object or array graphs out of + * a database result set. + * + * @package Doctrine + * @subpackage Hydrator + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.phpdoctrine.org + * @since 1.0 + * @version $Revision: 3192 $ + * @author Konsta Vesterinen + * @author Roman Borschel + */ +class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract +{ + /** + * hydrateResultSet + * parses the data returned by statement object + * + * This is method defines the core of Doctrine's object population algorithm. + * + * The key idea is the loop over the rowset only once doing all the needed operations + * within this massive loop. + * + * @todo: Detailed documentation. Refactor (too long & nesting level). + * + * @param mixed $stmt + * @param array $tableAliases Array that maps table aliases (SQL alias => DQL alias) + * @param array $aliasMap Array that maps DQL aliases to their components + * (DQL alias => array( + * 'table' => Table object, + * 'parent' => Parent DQL alias (if any), + * 'relation' => Relation object (if any), + * 'map' => Custom index to use as the key in the result (if any), + * 'agg' => List of aggregate value names (sql alias => dql alias) + * ) + * ) + * @return mixed The created object/array graph. + */ + public function hydrateResultSet($stmt, $tableAliases, $hydrationMode = null) + { + if ($hydrationMode === null) { + $hydrationMode = $this->_hydrationMode; + } + + if ($hydrationMode == Doctrine::HYDRATE_NONE) { + return $stmt->fetchAll(PDO::FETCH_NUM); + } + + $this->_tableAliases = $tableAliases; + + if ($hydrationMode == Doctrine::HYDRATE_ARRAY) { + $driver = new Doctrine_Hydrator_ArrayDriver(); + } else { + $driver = new Doctrine_Hydrator_RecordDriver(); + } + + $event = new Doctrine_Event(null, Doctrine_Event::HYDRATE, null); + + //$s = microtime(true); + + // Used variables during hydration + reset($this->_queryComponents); + $rootAlias = key($this->_queryComponents); + $rootComponentName = $this->_queryComponents[$rootAlias]['table']->getClassName(); + // if only one class is involved we can make our lives easier + $isSimpleQuery = count($this->_queryComponents) <= 1; + // Holds hydration listeners that get called during hydration + $listeners = array(); + // Lookup map to quickly discover/lookup existing records in the result + // It's the identifier "memory" + $identifierMap = array(); + // Holds for each class a pointer to the last previously seen element in the result set + $resultPointers = array(); + // holds the values of the identifier/primary key fields of components, + // separated by a pipe '|' and grouped by component alias (r, u, i, ... whatever) + // the $idTemplate is a prepared template. $id is set to a fresh template when + // starting to process a row. + $id = array(); + $idTemplate = array(); + + // Holds the resulting hydrated data structure + if ($this->_isResultMixed) { + $result = array(); + } else { + $result = $driver->getElementCollection($rootComponentName); + } + + if ($stmt === false || $stmt === 0) { + return $result; + } + + // Initialize + foreach ($this->_queryComponents as $dqlAlias => $component) { + // disable lazy-loading of related elements during hydration + $component['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, false); + $componentName = $component['table']->getClassName(); + $listeners[$componentName] = $component['table']->getRecordListener(); + $identifierMap[$dqlAlias] = array(); + $resultPointers[$dqlAlias] = array(); + $idTemplate[$dqlAlias] = ''; + } + + // Process result set + $cache = array(); + while ($data = $stmt->fetch(Doctrine::FETCH_ASSOC)) { + $id = $idTemplate; // initialize the id-memory + $nonemptyComponents = array(); + $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents); + + // + // hydrate the data of the root entity from the current row + // + $class = $this->_queryComponents[$rootAlias]['table']; + $componentName = $class->getComponentName(); + + // just event stuff + $event->set('data', $rowData[$rootAlias]); + $listeners[$componentName]->preHydrate($event); + //-- + + // Check for an existing element + $index = false; + if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) { + $element = $driver->getElement($rowData[$rootAlias], $componentName); + + // just event stuff + $event->set('data', $element); + $listeners[$componentName]->postHydrate($event); + //-- + + // do we need to index by a custom field? + if ($field = $this->_getCustomIndexField($rootAlias)) { + if (isset($result[$field])) { + throw new Doctrine_Hydrator_Exception("Couldn't hydrate. Found non-unique key mapping."); + } else if ( ! isset($element[$field])) { + throw new Doctrine_Hydrator_Exception("Couldn't hydrate. Found a non-existent key."); + } + if ($this->_isResultMixed) { + $result[] = array($element[$field] => $element); + } else { + $result[$element[$field]] = $element; + } + } else { + if ($this->_isResultMixed) { + $result[] = array($element); + } else { + $result[] = $element; + } + } + + $identifierMap[$rootAlias][$id[$rootAlias]] = $driver->getLastKey($result); + } else { + $index = $identifierMap[$rootAlias][$id[$rootAlias]]; + } + + $this->_setLastElement($resultPointers, $result, $index, $rootAlias, false); + unset($rowData[$rootAlias]); + // end hydrate data of the root component for the current row + + // Check for scalar values + if (isset($rowData['scalars'])) { + $scalars = $rowData['scalars']; + unset($rowData['scalars']); + } + + // $prev[$rootAlias] now points to the last element in $result. + // now hydrate the rest of the data found in the current row, that belongs to other + // (related) components. + foreach ($rowData as $dqlAlias => $data) { + $index = false; + $map = $this->_queryComponents[$dqlAlias]; + $componentName = $map['table']->getComponentName(); + + // just event stuff + $event->set('data', $data); + $listeners[$componentName]->preHydrate($event); + //-- + + $parent = $map['parent']; + $relation = $map['relation']; + $relationAlias = $relation->getAlias(); + + $path = $parent . '.' . $dqlAlias; + + $key = key(reset($resultPointers)); + if ($this->_isResultMixed && $parent == $rootAlias && isset($resultPointers[$parent][$key])) { + $baseElement =& $resultPointers[$parent][$key]; + } else if (isset($resultPointers[$parent])) { + $baseElement =& $resultPointers[$parent]; + } else { + continue; + } + + // check the type of the relation (many or single-valued) + if ( ! $relation->isOneToOne()) { + // x-many relation + $oneToOne = false; + if (isset($nonemptyComponents[$dqlAlias])) { + $driver->initRelated($baseElement, $relationAlias); + if ( ! isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]])) { + $element = $driver->getElement($data, $componentName); + + // just event stuff + $event->set('data', $element); + $listeners[$componentName]->postHydrate($event); + //-- + + if ($field = $this->_getCustomIndexField($dqlAlias)) { + // TODO: we should check this earlier. Fields used in INDEXBY + // must be unique. Then this can be removed here. + if (isset($baseElement[$relationAlias][$field])) { + throw Doctrine_Hydrator_Exception::nonUniqueKeyMapping(); + } else if ( ! isset($element[$field])) { + throw Doctrine_Hydrator_Exception::nonExistantFieldUsedAsIndex($field); + } + $baseElement[$relationAlias][$element[$field]] = $element; + } else { + $baseElement[$relationAlias][] = $element; + } + + $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey($baseElement[$relationAlias]); + } else { + $index = $identifierMap[$path][$id[$parent]][$id[$dqlAlias]]; + } + } + } else { + // x-1 relation + $oneToOne = true; + if ( ! isset($nonemptyComponents[$dqlAlias])) { + $baseElement[$relationAlias] = $driver->getNullPointer(); + } else if ( ! isset($baseElement[$relationAlias])) { + $baseElement[$relationAlias] = $driver->getElement($data, $componentName); + } + } + $coll =& $baseElement[$relationAlias]; + $this->_setLastElement($resultPointers, $coll, $index, $dqlAlias, $oneToOne); + } + + // append scalar values + if (isset($scalars)) { + $rowNumber = count($result) - 1; + foreach ($scalars as $name => $value) { + $result[$rowNumber][$name] = $value; + } + } + } + + $stmt->closeCursor(); + $driver->flush(); + + // re-enable lazy loading + foreach ($this->_queryComponents as $dqlAlias => $data) { + $data['table']->setAttribute(Doctrine::ATTR_LOAD_REFERENCES, true); + } + + //$e = microtime(true); + //echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records
'; + + return $result; + } + + /** + * _setLastElement + * + * sets the last element of given data array / collection + * as previous element + * + * @param array $prev The array that contains the pointers to the latest element of each class. + * @param array|Collection The object collection. + * @param boolean|integer $index Index of the element in the collection. + * @param string $dqlAlias + * @param boolean $oneToOne Whether it is a single-valued association or not. + * @return void + * @todo Detailed documentation + */ + protected function _setLastElement(&$resultPointers, &$coll, $index, $dqlAlias, $oneToOne) + { + if ($coll === $this->_nullObject) { + return false; + } + + if ($index !== false) { + // Link element at $index to previous element for the component + // identified by the DQL alias $alias + $resultPointers[$dqlAlias] =& $coll[$index]; + return; + } + + if (is_array($coll) && $coll) { + if ($oneToOne) { + $resultPointers[$dqlAlias] =& $coll; + } else { + end($coll); + $resultPointers[$dqlAlias] =& $coll[key($coll)]; + } + } else if ($coll instanceof Doctrine_Record) { + $resultPointers[$dqlAlias] = $coll; + } else if (count($coll) > 0) { + $resultPointers[$dqlAlias] = $coll->getLast(); + } else if (isset($resultPointers[$dqlAlias])) { + unset($resultPointers[$dqlAlias]); + } + } + + /** + * Puts the fields of a data row into a new array, grouped by the component + * they belong to. The column names in the result set are mapped to their + * field names during this procedure. + * + * @return array An array with all the fields (name => value) of the data row, + * grouped by their component (alias). + */ + protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents) + { + $rowData = array(); + + foreach ($data as $key => $value) { + // Parse each column name only once. Cache the results. + if ( ! isset($cache[$key])) { + // cache general information like the column name <-> field name mapping + $e = explode('__', $key); + $columnName = strtolower(array_pop($e)); + $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))]; + $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; + $classMetadata = $mapper->getClassMetadata(); + // check whether it's an aggregate value or a regular field + if (isset($this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName])) { + $fieldName = $this->_queryComponents[$cache[$key]['dqlAlias']]['agg'][$columnName]; + $cache[$key]['isScalar'] = true; + } else { + $fieldName = $mapper->getFieldName($columnName); + $cache[$key]['isScalar'] = false; + } + + $cache[$key]['fieldName'] = $fieldName; + + // cache identifier information + if ($classMetadata->isIdentifier($fieldName)) { + $cache[$key]['isIdentifier'] = true; + } else { + $cache[$key]['isIdentifier'] = false; + } + + // cache type information + $type = $classMetadata->getTypeOfColumn($columnName); + if ($type == 'integer' || $type == 'string') { + $cache[$key]['isSimpleType'] = true; + } else { + $cache[$key]['type'] = $type; + $cache[$key]['isSimpleType'] = false; + } + } + + $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; + $dqlAlias = $cache[$key]['dqlAlias']; + $fieldName = $cache[$key]['fieldName']; + + if ($cache[$key]['isScalar']) { + $rowData['scalars'][$fieldName] = $value; + continue; + } + + if ($cache[$key]['isIdentifier']) { + $id[$dqlAlias] .= '|' . $value; + } + + if ($cache[$key]['isSimpleType']) { + $rowData[$dqlAlias][$fieldName] = $value; + } else { + $rowData[$dqlAlias][$fieldName] = $mapper->prepareValue( + $fieldName, $value, $cache[$key]['type']); + } + + if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) { + $nonemptyComponents[$dqlAlias] = true; + } + } + + return $rowData; + } + + /** + * Gets the custom field used for indexing for the specified component alias. + * + * @return string The field name of the field used for indexing or NULL + * if the component does not use any custom field indices. + */ + protected function _getCustomIndexField($alias) + { + return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : null; + } + + + private $_isResultMixed = false; + public function setResultMixed($bool) + { + $this->_isResultMixed = $bool; + } + +} diff --git a/lib/Doctrine/Mapper.php b/lib/Doctrine/Mapper.php index 282373063..bc3237413 100644 --- a/lib/Doctrine/Mapper.php +++ b/lib/Doctrine/Mapper.php @@ -90,23 +90,6 @@ class Doctrine_Mapper } } - /** - * createQuery - * creates a new Doctrine_Query object and adds the component name - * of this table as the query 'from' part - * - * @param string Optional alias name for component aliasing. - * - * @return Doctrine_Query - */ - public function createQuery($alias = '') - { - if ( ! empty($alias)) { - $alias = ' ' . trim($alias); - } - return Doctrine_Query::create($this->_conn)->from($this->getComponentName() . $alias); - } - /** * sets the connection for this class * @@ -139,7 +122,7 @@ class Doctrine_Mapper */ public function create(array $array = array()) { - $record = new $this->_domainClassName($this, true); + $record = new $this->_domainClassName(); $record->fromArray($array); return $record; @@ -177,79 +160,6 @@ class Doctrine_Mapper { return $this->_conn->unitOfWork->detach($entity); } - - /** - * Finds an entity by its primary key. - * - * @param $id database row id - * @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD - * @return mixed Array or Doctrine_Record or false if no result - * @todo Remove. Move to EntityRepository. - */ - public function find($id, $hydrationMode = null) - { - if (is_null($id)) { - return false; - } - - $id = is_array($id) ? array_values($id) : array($id); - - return $this->createQuery() - ->where(implode(' = ? AND ', (array) $this->_classMetadata->getIdentifier()) . ' = ?') - ->fetchOne($id, $hydrationMode); - } - - /** - * Finds all entities of the mapper's class. - * Use with care. - * - * @param int $hydrationMode Doctrine::HYDRATE_ARRAY or Doctrine::HYDRATE_RECORD - * @return Doctrine_Collection - * @todo Remove. Move to EntityRepository. - */ - public function findAll($hydrationMode = null) - { - return $this->createQuery()->execute(array(), $hydrationMode); - } - - /** - * findBySql - * finds records with given SQL where clause - * returns a collection of records - * - * @param string $dql DQL after WHERE clause - * @param array $params query parameters - * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD - * @return Doctrine_Collection - * - * @todo This actually takes DQL, not SQL, but it requires column names - * instead of field names. This should be fixed to use raw SQL instead. - * @todo Remove. Move to EntityRepository. - */ - public function findBySql($dql, array $params = array(), $hydrationMode = null) - { - return $this->createQuery()->where($dql)->execute($params, $hydrationMode); - } - - /** - * findByDql - * finds records with given DQL where clause - * returns a collection of records - * - * @param string $dql DQL after WHERE clause - * @param array $params query parameters - * @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD - * @return Doctrine_Collection - * @todo Remove. Move to EntityRepository. - */ - public function findByDql($dql, array $params = array(), $hydrationMode = null) - { - $query = new Doctrine_Query($this->_conn); - $component = $this->getComponentName(); - $dql = 'FROM ' . $component . ' WHERE ' . $dql; - - return $query->query($dql, $params, $hydrationMode); - } /** * Executes a named query. @@ -349,9 +259,7 @@ class Doctrine_Mapper } if ($found) { - $record = new $this->_domainClassName($this, true, $data); - $data = array(); - return $record; + return new $this->_domainClassName(true, $data); } $idHash = $this->_conn->unitOfWork->getIdentifierHash($id); @@ -360,12 +268,12 @@ class Doctrine_Mapper $this->_classMetadata->getRootClassName())) { $record->hydrate($data); } else { - $record = new $this->_domainClassName($this, false, $data); + $record = new $this->_domainClassName(false, $data); $this->_conn->unitOfWork->registerIdentity($record); } $data = array(); } else { - $record = new $this->_domainClassName($this, true, $data); + $record = new $this->_domainClassName(true, $data); } return $record; @@ -545,83 +453,6 @@ class Doctrine_Mapper return $this->_domainClassName; } - /** - * findBy - * - * @param string $column - * @param string $value - * @param string $hydrationMode - * @return void - * @todo Remove. Move to EntityRepository. - */ - protected function findBy($fieldName, $value, $hydrationMode = null) - { - return $this->createQuery()->where($fieldName . ' = ?')->execute(array($value), $hydrationMode); - } - - /** - * findOneBy - * - * @param string $column - * @param string $value - * @param string $hydrationMode - * @return void - * @todo Remove. Move to EntityRepository. - */ - protected function findOneBy($fieldName, $value, $hydrationMode = null) - { - $results = $this->createQuery()->where($fieldName . ' = ?')->limit(1)->execute( - array($value), $hydrationMode); - return $hydrationMode === Doctrine::HYDRATE_ARRAY ? array_shift($results) : $results->getFirst(); - } - - /** - * __call - * - * Adds support for magic finders. - * findByColumnName, findByRelationAlias - * findById, findByContactId, etc. - * - * @return void - * @throws Doctrine_Mapper_Exception If the method called is an invalid find* method - * or no find* method at all and therefore an invalid - * method call. - * @todo Remove. Move to EntityRepository. - */ - public function __call($method, $arguments) - { - if (substr($method, 0, 6) == 'findBy') { - $by = substr($method, 6, strlen($method)); - $method = 'findBy'; - } else if (substr($method, 0, 9) == 'findOneBy') { - $by = substr($method, 9, strlen($method)); - $method = 'findOneBy'; - } else { - throw new Doctrine_Mapper_Exception("Undefined method '$method'."); - } - - if (isset($by)) { - if ( ! isset($arguments[0])) { - throw new Doctrine_Mapper_Exception('You must specify the value to findBy.'); - } - - $fieldName = Doctrine::tableize($by); - $hydrationMode = isset($arguments[1]) ? $arguments[1]:null; - - if ($this->_classMetadata->hasField($fieldName)) { - return $this->$method($fieldName, $arguments[0], $hydrationMode); - } else if ($this->_classMetadata->hasRelation($by)) { - $relation = $this->_classMetadata->getRelation($by); - if ($relation['type'] === Doctrine_Relation::MANY) { - throw new Doctrine_Mapper_Exception('Cannot findBy many relationship.'); - } - return $this->$method($relation['local'], $arguments[0], $hydrationMode); - } else { - throw new Doctrine_Mapper_Exception('Cannot find by: ' . $by . '. Invalid field or relationship alias.'); - } - } - } - /** * Saves an entity and all it's related entities. * @@ -795,7 +626,12 @@ class Doctrine_Mapper $query = 'DELETE FROM ' . $assocTable->getTableName() . ' WHERE ' . $rel->getForeign() . ' = ?' . ' AND ' . $rel->getLocal() . ' = ?'; - $this->_conn->execute($query, array($r->getIncremented(), $record->getIncremented())); + // FIXME: composite key support + $ids1 = $r->identifier(); + $id1 = count($ids1) > 0 ? array_pop($ids1) : null; + $ids2 = $record->identifier(); + $id2 = count($ids2) > 0 ? array_pop($ids2) : null; + $this->_conn->execute($query, array($id1, $id2)); } $assocMapper = $this->_conn->getMapper($assocTable->getComponentName()); @@ -804,7 +640,6 @@ class Doctrine_Mapper $assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r); $assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record); $assocMapper->save($assocRecord); - //$this->saveSingleRecord($assocRecord); } } } @@ -890,19 +725,6 @@ class Doctrine_Mapper return true; } - public function hasAttribute($key) - { - switch ($key) { - case Doctrine::ATTR_LOAD_REFERENCES: - case Doctrine::ATTR_QUERY_LIMIT: - case Doctrine::ATTR_COLL_KEY: - case Doctrine::ATTR_VALIDATE: - return true; - default: - return false; - } - } - public function executeQuery(Doctrine_Query $query) { diff --git a/lib/Doctrine/Mapper/JoinedStrategy.php b/lib/Doctrine/Mapper/JoinedStrategy.php index 237395f0e..205bd7141 100644 --- a/lib/Doctrine/Mapper/JoinedStrategy.php +++ b/lib/Doctrine/Mapper/JoinedStrategy.php @@ -111,7 +111,8 @@ class Doctrine_Mapper_JoinedStrategy extends Doctrine_Mapper_Strategy if ( ! $value->exists()) { $value->save(); } - $record->set($field, $value->getIncremented()); + $idValues = $value->identifier(); + $record->set($field, $idValues[0]); } } diff --git a/lib/Doctrine/Query.php b/lib/Doctrine/Query.php index 4d9af09cb..7e9bb166c 100644 --- a/lib/Doctrine/Query.php +++ b/lib/Doctrine/Query.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Query_Abstract'); + /** * Doctrine_Query * A Doctrine_Query object represents a DQL query. It is used to query databases for diff --git a/lib/Doctrine/Record.php b/lib/Doctrine/Record.php index 1308057a0..5a551f619 100644 --- a/lib/Doctrine/Record.php +++ b/lib/Doctrine/Record.php @@ -117,12 +117,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * @var Doctrine_ClassMetadata */ protected $_class; - - /** - * - * @var Doctrine_Mapper - */ - protected $_mapper; /** * @@ -186,6 +180,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * @var array */ protected $_references = array(); + + /** + * The EntityManager that is responsible for the persistence of the entity. + * + * @var Doctrine_EntityManager + */ + protected $_em; /** * The object identifier of the object. Each object has a unique identifier during runtime. @@ -206,23 +207,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * open connections * @throws Doctrine_Record_Exception if the cleanData operation fails somehow */ - public function __construct($mapper = null, $isNewEntry = false, array $data = array()) + public function __construct($isNewEntry = true, array $data = array()) { - if (isset($mapper) && $mapper instanceof Doctrine_Mapper) { - $class = get_class($this); - $this->_mapper = $mapper; - $this->_class = $this->_mapper->getClassMetadata(); - $exists = ! $isNewEntry; - } else { - $this->_mapper = Doctrine_Manager::getInstance()->getMapper(get_class($this)); - $this->_class = $this->_mapper->getClassMetadata(); - $exists = false; - } - - $this->_entityName = $this->_mapper->getMappedClassName(); - $this->_oid = self::$_index; + $this->_entityName = get_class($this); + $this->_em = Doctrine_Manager::getInstance()->getCurrentConnection(); + $this->_class = $this->_em->getClassMetadata($this->_entityName); - self::$_index++; + $this->_oid = self::$_index++; // get the data array $this->_data = $data; @@ -232,9 +223,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->_values = $this->cleanData($this->_data); - $this->_extractIdentifier($exists); + $this->_extractIdentifier( ! $isNewEntry); - if ( ! $exists) { + if ($isNewEntry) { if ($count > count($this->_values)) { $this->_state = Doctrine_Record::STATE_TDIRTY; } else { @@ -252,7 +243,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } self::$_useAutoAccessorOverride = false; // @todo read from attribute the first time - $this->_mapper->manage($this); + $this->_em->manage($this); $this->construct(); } @@ -510,13 +501,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $tmp = $data; $data = array(); - $fieldNames = $this->_mapper->getFieldNames(); + $fieldNames = $this->_em->getMapper($this->_entityName)->getFieldNames(); foreach ($fieldNames as $fieldName) { if (isset($tmp[$fieldName])) { $data[$fieldName] = $tmp[$fieldName]; } else if (array_key_exists($fieldName, $tmp)) { $data[$fieldName] = Doctrine_Null::$INSTANCE; - } else if (!isset($this->_data[$fieldName])) { + } else if ( ! isset($this->_data[$fieldName])) { $data[$fieldName] = Doctrine_Null::$INSTANCE; } unset($tmp[$fieldName]); @@ -554,7 +545,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite case Doctrine::IDENTIFIER_AUTOINC: case Doctrine::IDENTIFIER_SEQUENCE: case Doctrine::IDENTIFIER_NATURAL: - $name = (array)$this->_class->getIdentifier(); + $name = $this->_class->getIdentifier(); $name = $name[0]; if ($exists) { if (isset($this->_data[$name]) && $this->_data[$name] !== Doctrine_Null::$INSTANCE) { @@ -563,7 +554,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } break; case Doctrine::IDENTIFIER_COMPOSITE: - $names = (array)$this->_class->getIdentifier(); + $names = $this->_class->getIdentifier(); foreach ($names as $name) { if ($this->_data[$name] === Doctrine_Null::$INSTANCE) { @@ -575,6 +566,14 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite break; } } + + /** + * INTERNAL: + */ + final public function setIdentifier(array $identifier) + { + $this->_id = $identifier; + } /** * Serializes the entity. @@ -596,6 +595,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite unset($vars['_errorStack']); unset($vars['_filter']); unset($vars['_node']); + unset($vars['_em']); //$name = (array)$this->_table->getIdentifier(); $this->_data = array_merge($this->_data, $this->_id); @@ -642,13 +642,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->preUnserialize($event); - $manager = Doctrine_Manager::getInstance(); + $manager = Doctrine_Manager::getInstance(); $connection = $manager->getConnectionForComponent(get_class($this)); $this->_oid = self::$_index; self::$_index++; - $this->_mapper = $connection->getMapper(get_class($this)); + $this->_em = $connection; $array = unserialize($serialized); @@ -656,7 +656,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->$k = $v; } - $this->_class = $this->_mapper->getTable(); + $this->_entityName = get_class($this); + $this->_class = $this->_em->getClassMetadata($this->_entityName); foreach ($this->_data as $k => $v) { switch ($this->_class->getTypeOf($k)) { @@ -674,7 +675,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } } - $this->_mapper->manage($this); + $this->_em->manage($this); $this->cleanData($this->_data); $this->_extractIdentifier($this->exists()); @@ -749,7 +750,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $id = array_values($id); if ($deep) { - $query = $this->_mapper->createQuery(); + $query = $this->_class->getConnection()->createQuery()->from($this->_entityName); foreach (array_keys($this->_references) as $name) { $query->leftJoin(get_class($this) . '.' . $name); } @@ -758,7 +759,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $record = $query->fetchOne($id); } else { // Use FETCH_ARRAY to avoid clearing object relations - $record = $this->_mapper->find($id, Doctrine::HYDRATE_ARRAY); + $record = $this->getRepository()->find($this->identifier(), Doctrine::HYDRATE_ARRAY); if ($record) { $this->hydrate($record); } @@ -929,8 +930,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } return $this->_references[$fieldName]; } catch (Doctrine_Relation_Exception $e) { - echo $e->getTraceAsString(); - echo "

"; + //echo $e->getTraceAsString(); + //echo "

"; foreach ($this->_class->getFilters() as $filter) { if (($value = $filter->filterGet($this, $fieldName, $value)) !== null) { return $value; @@ -979,7 +980,9 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if (isset($this->_data[$fieldName])) { if ($value instanceof Doctrine_Record) { $type = $this->_class->getTypeOf($fieldName); - $id = $value->getIncremented(); + // FIXME: composite key support + $ids = $value->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; if ($id !== null && $type !== 'object') { $value = $id; } @@ -998,6 +1001,13 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->_data[$fieldName] = $value; $this->_modified[] = $fieldName; + + /* We can't do this currently because there are tests that change + * the primary key of already persisted entities (ugh). */ + if ($this->isTransient() && $this->_class->isIdentifier($fieldName)) { + $this->_id[$fieldName] = $value; + } + switch ($this->_state) { case Doctrine_Record::STATE_CLEAN: $this->_state = Doctrine_Record::STATE_DIRTY; @@ -1011,8 +1021,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite try { $this->_coreSetRelated($fieldName, $value); } catch (Doctrine_Relation_Exception $e) { - echo $e->getTraceAsString(); - echo "

"; + //echo $e->getTraceAsString(); + //echo "

"; foreach ($this->_class->getFilters() as $filter) { if (($value = $filter->filterSet($this, $fieldName, $value)) !== null) { return $value; @@ -1131,7 +1141,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite public function save(Doctrine_Connection $conn = null) { // TODO: Forward to EntityManager. There: registerNew() OR registerDirty() on UnitOfWork. - $this->_mapper->save($this, $conn); + $this->_em->save($this, $conn); } /** @@ -1175,7 +1185,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite public function replace(Doctrine_Connection $conn = null) { if ($conn === null) { - $conn = $this->_mapper->getConnection(); + $conn = $this->_em; } return $conn->replace($this->_class, $this->getPrepared(), $this->_id); @@ -1249,7 +1259,10 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite break; default: if ($this->_data[$field] instanceof Doctrine_Record) { - $this->_data[$field] = $this->_data[$field]->getIncremented(); + // FIXME: composite key support + $ids = $this->_data[$field]->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; + $this->_data[$field] = $id; } /** TODO: if ($this->_data[$v] === null) { @@ -1313,8 +1326,12 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite if ($this->_class->getIdentifierType() == Doctrine::IDENTIFIER_AUTOINC) { $idFieldNames = (array)$this->_class->getIdentifier(); - $id = $idFieldNames[0]; - $a[$id] = $this->getIncremented(); + $idFieldName = $idFieldNames[0]; + + $ids = $this->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; + + $a[$idFieldName] = $id; } if ($deep) { @@ -1460,17 +1477,41 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite * Checks whether the entity already has a persistent state. * * @return boolean TRUE if the object is new, FALSE otherwise. + * @deprecated Use isTransient() */ public function isNew() { return $this->_state == self::STATE_TCLEAN || $this->_state == self::STATE_TDIRTY; } + + /** + * Checks whether the entity already has a persistent state. + * + * @return boolean TRUE if the object is new, FALSE otherwise. + */ + public function isTransient() + { + return $this->_state == self::STATE_TCLEAN || $this->_state == self::STATE_TDIRTY; + } + + /** + * Checks whether the entity has been modified since it was last synchronized + * with the database. + * + * @return boolean TRUE if the object has been modified, FALSE otherwise. + */ + public function isDirty() + { + return ($this->_state === Doctrine_Record::STATE_DIRTY || + $this->_state === Doctrine_Record::STATE_TDIRTY); + } /** * Checks whether the entity has been modified since it was last synchronized * with the database. * * @return boolean TRUE if the object has been modified, FALSE otherwise. + * @deprecated Use isDirty() */ public function isModified() { @@ -1496,6 +1537,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite /** * getIterator * @return Doctrine_Record_Iterator a Doctrine_Record_Iterator that iterates through the data + * @todo Really needed/useful? */ public function getIterator() { @@ -1513,7 +1555,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite public function delete(Doctrine_Connection $conn = null) { // TODO: Forward to EntityManager. There: registerRemoved() on UnitOfWork - return $this->_mapper->delete($this, $conn); + return $this->_em->remove($this, $conn); } /** @@ -1532,7 +1574,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite unset($data[$id]); } - $ret = $this->_mapper->create($data); + $ret = $this->_em->createEntity($this->_entityName, $data); $modified = array(); foreach ($data as $key => $val) { @@ -1581,7 +1623,7 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite $this->_data[$fieldName] = $value; } } else { - $idFieldNames = (array)$this->_class->getIdentifier(); + $idFieldNames = $this->_class->getIdentifier(); $name = $idFieldNames[0]; $this->_id[$name] = $id; $this->_data[$name] = $id; @@ -1601,23 +1643,6 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite return $this->_id; } - /** - * returns the value of autoincremented primary key of this object (if any) - * - * @return integer - * @todo Better name? Not sure this is the right place here. - * @todo Plays against full composite key support.. - */ - final public function getIncremented() - { - $id = current($this->_id); - if ($id === false) { - return null; - } - - return $id; - } - /** * hasRefence * @param string $name @@ -1964,15 +1989,15 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite { return $this->_class; } - - /** - * Returns the mapper of the entity. - * - * @return Doctrine_Mapper - */ - public function getMapper() + + public function getEntityManager() { - return $this->_mapper; + return $this->_em; + } + + public function getRepository() + { + return $this->_class->getConnection()->getRepository($this->_entityName); } /** @@ -2002,8 +2027,8 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite public function free($deep = false) { if ($this->_state != self::STATE_LOCKED) { - $this->_mapper->detach($this); - $this->_mapper->removeRecord($this); + $this->_em->detach($this); + $this->_em->removeRecord($this); $this->_data = array(); $this->_id = array(); diff --git a/lib/Doctrine/Relation/Association.php b/lib/Doctrine/Relation/Association.php index 81a0ac0ee..dfb744891 100644 --- a/lib/Doctrine/Relation/Association.php +++ b/lib/Doctrine/Relation/Association.php @@ -93,18 +93,15 @@ class Doctrine_Relation_Association extends Doctrine_Relation * @return Doctrine_Record|Doctrine_Collection */ public function fetchRelatedFor(Doctrine_Record $record) - { - $id = $record->getIncremented(); - //var_dump($id); - //echo "

"; + { + // FIXME: composite key support + $ids = $record->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; + if (empty($id) || ! $this->_foreignMapper->getClassMetadata()->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)); - //echo $query->getDql() . "
"; - //echo $query->getSql() . "
"; - //echo "

"; $coll = Doctrine_Query::create()->query($this->getRelationDql(1), array($id)); } $coll->setReference($record, $this); diff --git a/lib/Doctrine/Relation/Association/Self.php b/lib/Doctrine/Relation/Association/Self.php index 3da7ed57c..ec11adb38 100644 --- a/lib/Doctrine/Relation/Association/Self.php +++ b/lib/Doctrine/Relation/Association/Self.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ -Doctrine::autoload('Doctrine_Relation_Association'); + /** * Doctrine_Relation_Association_Self * @@ -75,8 +75,10 @@ class Doctrine_Relation_Association_Self extends Doctrine_Relation_Association } public function fetchRelatedFor(Doctrine_Record $record) - { - $id = $record->getIncremented(); + { + // FIXME: composite key support + $ids = $record->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; $q = new Doctrine_RawSql(); diff --git a/lib/Doctrine/Relation/Nest.php b/lib/Doctrine/Relation/Nest.php index 07909c969..1c7c623fd 100644 --- a/lib/Doctrine/Relation/Nest.php +++ b/lib/Doctrine/Relation/Nest.php @@ -74,35 +74,11 @@ class Doctrine_Relation_Nest extends Doctrine_Relation_Association return $dql; } - /** public function fetchRelatedFor(Doctrine_Record $record) { - $id = $record->getIncremented(); - - if (empty($id) || ! $this->definition['table']->getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) { - return new Doctrine_Collection($this->getTable()); - } else { - $q = new Doctrine_Query(); - - $c = $this->getTable()->getComponentName(); - $a = substr($c, 0, 1); - $c2 = $this->getAssociationTable()->getComponentName(); - $a2 = substr($c2, 0, 1); - - $q->from($c) - ->innerJoin($c . '.' . $c2) - - $sub = 'SELECT ' . $this->getForeign() - . ' FROM ' . $c2 - . ' WHERE ' . $this->getLocal() - . ' = ?'; - } - } - */ - - public function fetchRelatedFor(Doctrine_Record $record) - { - $id = $record->getIncremented(); + // FIXME: composite key support + $ids = $record->identifier(); + $id = count($ids) > 0 ? array_pop($ids) : null; if (empty($id) || ! $this->_foreignMapper->getClassMetadata()->getAttribute(Doctrine::ATTR_LOAD_REFERENCES)) { diff --git a/lib/Doctrine/Relation/Parser.php b/lib/Doctrine/Relation/Parser.php index a60aa1b68..2ff76cb4b 100644 --- a/lib/Doctrine/Relation/Parser.php +++ b/lib/Doctrine/Relation/Parser.php @@ -147,12 +147,6 @@ class Doctrine_Relation_Parser $this->getRelations(); return $this->getRelation($alias, false); } else { - /*try { - throw new Exception(); - } catch (Exception $e) { - //echo "" . "
"; - ///echo $e->getTraceAsString() . "


"; - }*/ throw new Doctrine_Relation_Exception("Unknown relation '$alias'."); } } diff --git a/lib/Doctrine/Validator.php b/lib/Doctrine/Validator.php index e59f08e33..6898a1adf 100644 --- a/lib/Doctrine/Validator.php +++ b/lib/Doctrine/Validator.php @@ -84,7 +84,8 @@ class Doctrine_Validator if ($value === Doctrine_Null::$INSTANCE) { $value = null; } else if ($value instanceof Doctrine_Record) { - $value = $value->getIncremented(); + $ids = $value->identifier(); + $value = count($ids) > 0 ? array_pop($ids) : null; } $dataType = $classMetadata->getTypeOf($fieldName); diff --git a/tests/Orm/Hydration/BasicHydrationTest.php b/tests/Orm/Hydration/BasicHydrationTest.php index 1b21158a9..7c92fbf03 100644 --- a/tests/Orm/Hydration/BasicHydrationTest.php +++ b/tests/Orm/Hydration/BasicHydrationTest.php @@ -9,11 +9,22 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase parent::setUp(); } + /** The data of the hydration mode dataProvider */ + protected static $hydrationModeProviderData = array( + array('hydrationMode' => Doctrine::HYDRATE_RECORD), + array('hydrationMode' => Doctrine::HYDRATE_ARRAY) + ); + /** Getter for the hydration mode dataProvider */ + public static function hydrationModeProvider() + { + return self::$hydrationModeProviderData; + } + /** - * Fakes the DQL query: select u.id, u.name from CmsUser u + * Select u.id, u.name from CmsUser u * */ - public function testBasic() + public function testBasicHydration() { // Faked query components $queryComponents = array( @@ -72,4 +83,285 @@ class Orm_Hydration_BasicHydrationTest extends Doctrine_OrmTestCase $this->assertEquals('jwage', $objectResult[1]->name); } + + /** + * select u.id, u.status, p.phonenumber, upper(u.name) nameUpper from User u + * join u.phonenumbers p + * = + * select u.id, u.status, p.phonenumber, upper(u.name) as u__0 from USERS u + * INNER JOIN PHONENUMBERS p ON u.id = p.user_id + * + * @dataProvider hydrationModeProvider + */ + public function testNewHydrationMixedQueryFetchJoin($hydrationMode) + { + // Faked query components + $queryComponents = array( + 'u' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsUser'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsUser'), + 'parent' => null, + 'relation' => null, + 'map' => null, + 'agg' => array('0' => 'nameUpper') + ), + 'p' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsPhonenumber'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsPhonenumber'), + 'parent' => 'u', + 'relation' => $this->sharedFixture['connection']->getClassMetadata('CmsUser')->getRelation('phonenumbers'), + 'map' => null + ) + ); + + // Faked table alias map + $tableAliasMap = array( + 'u' => 'u', + 'p' => 'p' + ); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__0' => 'ROMANB', + 'p__phonenumber' => '42', + ), + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__0' => 'ROMANB', + 'p__phonenumber' => '43', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'u__0' => 'JWAGE', + 'p__phonenumber' => '91' + ) + ); + + $stmt = new Doctrine_HydratorMockStatement($resultSet); + $hydrator = new Doctrine_HydratorNew(); + $hydrator->setQueryComponents($queryComponents); + + $hydrator->setResultMixed(true); + + $result = $hydrator->hydrateResultSet($stmt, $tableAliasMap, $hydrationMode); + //var_dump($result); + + $this->assertEquals(2, count($result)); + $this->assertTrue(is_array($result)); + $this->assertTrue(is_array($result[0])); + $this->assertTrue(is_array($result[1])); + + $this->assertEquals(3, count($result[0][0])); + // first user => 2 phonenumbers + $this->assertEquals(2, count($result[0][0]['phonenumbers'])); + $this->assertEquals('ROMANB', $result[0]['nameUpper']); + // second user => 1 phonenumber + $this->assertEquals(1, count($result[1][0]['phonenumbers'])); + $this->assertEquals('JWAGE', $result[1]['nameUpper']); + + $this->assertEquals(42, $result[0][0]['phonenumbers'][0]['phonenumber']); + $this->assertEquals(43, $result[0][0]['phonenumbers'][1]['phonenumber']); + $this->assertEquals(91, $result[1][0]['phonenumbers'][0]['phonenumber']); + + if ($hydrationMode == Doctrine::HYDRATE_RECORD) { + $this->assertTrue($result[0][0] instanceof Doctrine_Record); + $this->assertTrue($result[0][0]['phonenumbers'] instanceof Doctrine_Collection); + $this->assertTrue($result[0][0]['phonenumbers'][0] instanceof Doctrine_Record); + $this->assertTrue($result[0][0]['phonenumbers'][1] instanceof Doctrine_Record); + $this->assertTrue($result[1][0] instanceof Doctrine_Record); + $this->assertTrue($result[1][0]['phonenumbers'] instanceof Doctrine_Collection); + } + } + + /** + * select u.id, u.status, count(p.phonenumber) numPhones from User u + * join u.phonenumbers p group by u.status, u.id + * = + * select u.id, u.status, count(p.phonenumber) as p__0 from USERS u + * INNER JOIN PHONENUMBERS p ON u.id = p.user_id group by u.id, u.status + * + * @dataProvider hydrationModeProvider + */ + public function testNewHydrationBasicsMixedQueryNormalJoin($hydrationMode) + { + // Faked query components + $queryComponents = array( + 'u' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsUser'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsUser'), + 'parent' => null, + 'relation' => null, + 'map' => null + ), + 'p' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsPhonenumber'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsPhonenumber'), + 'parent' => 'u', + 'relation' => $this->sharedFixture['connection']->getClassMetadata('CmsUser')->getRelation('phonenumbers'), + 'map' => null, + 'agg' => array('0' => 'numPhones') + ) + ); + + // Faked table alias map + $tableAliasMap = array( + 'u' => 'u', + 'p' => 'p' + ); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'p__0' => '2', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'p__0' => '1', + ) + ); + + + $stmt = new Doctrine_HydratorMockStatement($resultSet); + $hydrator = new Doctrine_HydratorNew(); + $hydrator->setQueryComponents($queryComponents); + + $hydrator->setResultMixed(true); + + $result = $hydrator->hydrateResultSet($stmt, $tableAliasMap, $hydrationMode); + //var_dump($result); + + $this->assertEquals(2, count($result)); + $this->assertTrue(is_array($result)); + $this->assertTrue(is_array($result[0])); + $this->assertTrue(is_array($result[1])); + + // first user => 2 phonenumbers + $this->assertEquals(2, $result[0]['numPhones']); + // second user => 1 phonenumber + $this->assertEquals(1, $result[1]['numPhones']); + + if ($hydrationMode == Doctrine::HYDRATE_RECORD) { + $this->assertTrue($result[0][0] instanceof Doctrine_Record); + $this->assertTrue($result[1][0] instanceof Doctrine_Record); + } + } + + /** + * select u.id, u.status, upper(u.name) nameUpper from User u index by u.id + * join u.phonenumbers p indexby p.phonenumber + * = + * select u.id, u.status, upper(u.name) as p__0 from USERS u + * INNER JOIN PHONENUMBERS p ON u.id = p.user_id + * + * @dataProvider hydrationModeProvider + */ + public function testNewHydrationMixedQueryFetchJoinCustomIndex($hydrationMode) + { + // Faked query components + $queryComponents = array( + 'u' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsUser'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsUser'), + 'parent' => null, + 'relation' => null, + 'agg' => array('0' => 'nameUpper'), + 'map' => 'id' + ), + 'p' => array( + 'table' => $this->sharedFixture['connection']->getClassMetadata('CmsPhonenumber'), + 'mapper' => $this->sharedFixture['connection']->getMapper('CmsPhonenumber'), + 'parent' => 'u', + 'relation' => $this->sharedFixture['connection']->getClassMetadata('CmsUser')->getRelation('phonenumbers'), + 'map' => 'phonenumber' + ) + ); + + // Faked table alias map + $tableAliasMap = array( + 'u' => 'u', + 'p' => 'p' + ); + + // Faked result set + $resultSet = array( + //row1 + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__0' => 'ROMANB', + 'p__phonenumber' => '42', + ), + array( + 'u__id' => '1', + 'u__status' => 'developer', + 'u__0' => 'ROMANB', + 'p__phonenumber' => '43', + ), + array( + 'u__id' => '2', + 'u__status' => 'developer', + 'u__0' => 'JWAGE', + 'p__phonenumber' => '91' + ) + ); + + + $stmt = new Doctrine_HydratorMockStatement($resultSet); + $hydrator = new Doctrine_HydratorNew(); + $hydrator->setQueryComponents($queryComponents); + + // give the hydrator an artificial hint + $hydrator->setResultMixed(true); + + $result = $hydrator->hydrateResultSet($stmt, $tableAliasMap, $hydrationMode); + if ($hydrationMode == Doctrine::HYDRATE_ARRAY) { + //var_dump($result); + } + + $this->assertEquals(2, count($result)); + $this->assertTrue(is_array($result)); + $this->assertTrue(is_array($result[0])); + $this->assertTrue(is_array($result[1])); + + + // first user => 2 phonenumbers. notice the custom indexing by user id + $this->assertEquals(2, count($result[0]['1']['phonenumbers'])); + // second user => 1 phonenumber. notice the custom indexing by user id + $this->assertEquals(1, count($result[1]['2']['phonenumbers'])); + + // test the custom indexing of the phonenumbers + $this->assertTrue(isset($result[0]['1']['phonenumbers']['42'])); + $this->assertTrue(isset($result[0]['1']['phonenumbers']['43'])); + $this->assertTrue(isset($result[1]['2']['phonenumbers']['91'])); + + // test the scalar values + $this->assertEquals('ROMANB', $result[0]['nameUpper']); + $this->assertEquals('JWAGE', $result[1]['nameUpper']); + + if ($hydrationMode == Doctrine::HYDRATE_RECORD) { + $this->assertTrue($result[0]['1'] instanceof Doctrine_Record); + $this->assertTrue($result[1]['2'] instanceof Doctrine_Record); + $this->assertTrue($result[0]['1']['phonenumbers'] instanceof Doctrine_Collection); + $this->assertEquals(2, count($result[0]['1']['phonenumbers'])); + } + + } + + + + + + + + } \ No newline at end of file diff --git a/tests/Orm/UnitOfWorkTestCase.php b/tests/Orm/UnitOfWorkTestCase.php index a4f9dd985..819f43cf2 100644 --- a/tests/Orm/UnitOfWorkTestCase.php +++ b/tests/Orm/UnitOfWorkTestCase.php @@ -18,6 +18,8 @@ class Orm_UnitOfWorkTestCase extends Doctrine_OrmTestCase public function testRegisterNew() { + $this->_user->username = 'romanb'; + $this->_user->id = 1; $this->_unitOfWork->registerNew($this->_user); $this->assertFalse($this->_unitOfWork->contains($this->_user)); $this->assertTrue($this->_unitOfWork->isRegisteredNew($this->_user)); @@ -35,7 +37,15 @@ class Orm_UnitOfWorkTestCase extends Doctrine_OrmTestCase $this->assertTrue($this->_unitOfWork->isRegisteredDirty($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredNew($this->_user)); $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); - + } + + public function testRegisterRemovedOnTransientEntityIsIgnored() + { + $this->_user->username = 'romanb'; + $this->_user->id = 1; + $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); + $this->_unitOfWork->registerRemoved($this->_user); + $this->assertFalse($this->_unitOfWork->isRegisteredRemoved($this->_user)); } /*public function testSavedEntityHasIdentityAndIsManaged() diff --git a/tests/models/cms/CmsPhonenumber.php b/tests/models/cms/CmsPhonenumber.php new file mode 100644 index 000000000..a095a8ba9 --- /dev/null +++ b/tests/models/cms/CmsPhonenumber.php @@ -0,0 +1,9 @@ +mapColumn('user_id', 'integer', 4); + $class->mapColumn('phonenumber', 'string', 50, array('primary' => true)); + } +} diff --git a/tests/models/cms/CmsUser.php b/tests/models/cms/CmsUser.php index cfa93db2d..af8f17a2e 100644 --- a/tests/models/cms/CmsUser.php +++ b/tests/models/cms/CmsUser.php @@ -6,5 +6,8 @@ class CmsUser extends Doctrine_Record $class->mapColumn('id', 'integer', 4, array('primary' => true, 'autoincrement' => true)); $class->mapColumn('username', 'string', 255); $class->mapColumn('name', 'string', 255); + + $class->hasMany('CmsPhonenumber as phonenumbers', array( + 'local' => 'id', 'foreign' => 'user_id')); } } diff --git a/tests_old/AccessTestCase.php b/tests_old/AccessTestCase.php index 1ccb6d730..e31ea50de 100644 --- a/tests_old/AccessTestCase.php +++ b/tests_old/AccessTestCase.php @@ -77,7 +77,7 @@ class Doctrine_Access_TestCase extends Doctrine_UnitTestCase $user->save(); - $user = $this->connection->getMapper('User')->find($user->identifier()); + $user = $this->connection->getRepository('User')->find($user->identifier()); $this->assertEqual($user->name, 'Jack'); $user['name'] = 'Jack'; @@ -97,7 +97,7 @@ class Doctrine_Access_TestCase extends Doctrine_UnitTestCase $user->save(); - $user = $this->connection->getMapper('User')->find($user->identifier()); + $user = $this->connection->getRepository('User')->find($user->identifier()); $this->assertEqual($user->name, 'Jack'); $user->name = 'Jack'; @@ -115,7 +115,7 @@ class Doctrine_Access_TestCase extends Doctrine_UnitTestCase $user->save(); - $user = $this->connection->getMapper('User')->find($user->identifier()); + $user = $this->connection->getRepository('User')->find($user->identifier()); $this->assertEqual($user->get('name'), 'Jack'); diff --git a/tests_old/ClassTableInheritanceTestCase.php b/tests_old/ClassTableInheritanceTestCase.php index 815cd94c3..85f839638 100644 --- a/tests_old/ClassTableInheritanceTestCase.php +++ b/tests_old/ClassTableInheritanceTestCase.php @@ -168,7 +168,7 @@ class Doctrine_ClassTableInheritance_TestCase extends Doctrine_UnitTestCase $profiler = new Doctrine_Connection_Profiler(); $this->conn->addListener($profiler); - $record = $this->conn->getMapper('CTITest')->find(1); + $record = $this->conn->getRepository('CTITest')->find(1); $record->age = 11; $record->name = 'Jack'; @@ -193,7 +193,7 @@ class Doctrine_ClassTableInheritance_TestCase extends Doctrine_UnitTestCase { $this->conn->clear(); - $record = $this->conn->getMapper('CTITest')->find(1); + $record = $this->conn->getRepository('CTITest')->find(1); $this->assertEqual($record->id, 1); $this->assertEqual($record->name, 'Jack'); @@ -209,7 +209,7 @@ class Doctrine_ClassTableInheritance_TestCase extends Doctrine_UnitTestCase $profiler = new Doctrine_Connection_Profiler(); $this->conn->addListener($profiler); - $record = $this->conn->getMapper('CTITest')->find(1); + $record = $this->conn->getRepository('CTITest')->find(1); $record->delete(); diff --git a/tests_old/CustomPrimaryKeyTestCase.php b/tests_old/CustomPrimaryKeyTestCase.php index d31c200b3..445232090 100644 --- a/tests_old/CustomPrimaryKeyTestCase.php +++ b/tests_old/CustomPrimaryKeyTestCase.php @@ -53,7 +53,7 @@ class Doctrine_CustomPrimaryKey_TestCase extends Doctrine_UnitTestCase $this->assertEqual($c->identifier(), array('uid' => 1)); $this->connection->clear(); - $c = $this->connection->getMapper('CustomPK')->find(1); + $c = $this->connection->getRepository('CustomPK')->find(1); $this->assertEqual($c->identifier(), array('uid' => 1)); } diff --git a/tests_old/DataType/BooleanTestCase.php b/tests_old/DataType/BooleanTestCase.php index 92afbf534..72e92d56a 100644 --- a/tests_old/DataType/BooleanTestCase.php +++ b/tests_old/DataType/BooleanTestCase.php @@ -60,7 +60,7 @@ class Doctrine_DataType_Boolean_TestCase extends Doctrine_UnitTestCase { $this->connection->clear(); - $test = $test->getMapper()->find($test->id); + $test = $test->getRepository()->find($test->id); $this->assertIdentical($test->is_working, true); } public function testNormalQuerying() { diff --git a/tests_old/DataType/EnumTestCase.php b/tests_old/DataType/EnumTestCase.php index b7c3de088..1a5fe6af2 100644 --- a/tests_old/DataType/EnumTestCase.php +++ b/tests_old/DataType/EnumTestCase.php @@ -172,7 +172,7 @@ class Doctrine_DataType_Enum_TestCase extends Doctrine_UnitTestCase public function testFailingRefresh() { - $enum = $this->connection->getMapper('EnumTest')->find(1); + $enum = $this->connection->getRepository('EnumTest')->find(1); $this->conn->exec('DELETE FROM enum_test WHERE id = 1'); diff --git a/tests_old/DoctrineTest/Doctrine_UnitTestCase.php b/tests_old/DoctrineTest/Doctrine_UnitTestCase.php index 0028f8a64..4299675ff 100644 --- a/tests_old/DoctrineTest/Doctrine_UnitTestCase.php +++ b/tests_old/DoctrineTest/Doctrine_UnitTestCase.php @@ -197,7 +197,7 @@ class Doctrine_UnitTestCase extends UnitTestCase //echo "exporting : " . var_dump($this->tables); //echo "

"; $this->conn->export->exportClasses($this->tables); - $this->objTable = $this->connection->getMapper('User'); + $this->objTable = $this->connection->getRepository('User'); } public function prepareData() diff --git a/tests_old/Inheritance/JoinedTestCase.php b/tests_old/Inheritance/JoinedTestCase.php index e97078ddb..00cfb0d57 100644 --- a/tests_old/Inheritance/JoinedTestCase.php +++ b/tests_old/Inheritance/JoinedTestCase.php @@ -152,7 +152,7 @@ class Doctrine_Inheritance_Joined_TestCase extends Doctrine_UnitTestCase public function testDqlQueryJoinsTransparentlyAcrossParents() { $this->_createManager(); - $this->conn->getMapper('CTI_Manager')->clear(); + $this->conn->clear('CTI_Manager'); $query = $this->conn->createQuery(); $query->parseQuery("SELECT m.* FROM CTI_Manager m"); @@ -167,8 +167,8 @@ class Doctrine_Inheritance_Joined_TestCase extends Doctrine_UnitTestCase public function testQueryingBaseClassOuterJoinsSubClassesAndReturnsSubclassInstances() { $this->_createManager(); - $this->conn->getMapper('CTI_Manager')->clear(); - $this->conn->getMapper('CTI_User')->clear(); + $this->conn->clear('CTI_Manager'); + $this->conn->clear('CTI_User'); $query = $this->conn->createQuery(); $query->parseQuery("SELECT u.* FROM CTI_User u"); diff --git a/tests_old/Query/JoinCondition2TestCase.php b/tests_old/Query/JoinCondition2TestCase.php index d75e3ca3a..6530403a0 100755 --- a/tests_old/Query/JoinCondition2TestCase.php +++ b/tests_old/Query/JoinCondition2TestCase.php @@ -41,9 +41,9 @@ class Doctrine_Query_JoinCondition2_TestCase extends Doctrine_UnitTestCase public function prepareData() { - $this->conn->getMapper('User')->clear(); - $this->conn->getMapper('Group')->clear(); - $this->conn->getMapper('Groupuser')->clear(); + $this->conn->clear('User'); + $this->conn->clear('Group'); + $this->conn->clear('Groupuser'); $zYne = new User(); $zYne->name = 'zYne'; diff --git a/tests_old/Query/MultiJoinTestCase.php b/tests_old/Query/MultiJoinTestCase.php index 3c9fce0fb..00f7d8d84 100644 --- a/tests_old/Query/MultiJoinTestCase.php +++ b/tests_old/Query/MultiJoinTestCase.php @@ -43,7 +43,7 @@ class Doctrine_Query_MultiJoin_TestCase extends Doctrine_UnitTestCase $query = new Doctrine_Query($this->connection); - $user = $this->connection->getMapper('User')->find(4); + $user = $this->connection->getRepository('User')->find(4); $album = $this->connection->create('Album'); @@ -73,7 +73,7 @@ class Doctrine_Query_MultiJoin_TestCase extends Doctrine_UnitTestCase $this->assertEqual(count($user->Album[1]->Song), 4); - $user = $this->connection->getMapper('User')->find(5); + $user = $this->connection->getRepository('User')->find(5); $user->Album[0]->name = 'Clayman'; $user->Album[1]->name = 'Colony'; @@ -122,7 +122,7 @@ class Doctrine_Query_MultiJoin_TestCase extends Doctrine_UnitTestCase public function testInitializeMoreData() { - $user = $this->connection->getMapper('User')->find(4); + $user = $this->connection->getRepository('User')->find(4); $user->Book[0]->name = 'The Prince'; $user->Book[0]->Author[0]->name = 'Niccolo Machiavelli'; $user->Book[0]->Author[1]->name = 'Someone'; @@ -133,7 +133,7 @@ class Doctrine_Query_MultiJoin_TestCase extends Doctrine_UnitTestCase $user->save(); - $user = $this->connection->getMapper('User')->find(5); + $user = $this->connection->getRepository('User')->find(5); $user->Book[0]->name = 'Zadig'; $user->Book[0]->Author[0]->name = 'Voltaire'; $user->Book[0]->Author[1]->name = 'Someone'; diff --git a/tests_old/Query/ReferenceModelTestCase.php b/tests_old/Query/ReferenceModelTestCase.php index ccf2bf0de..b4c545918 100644 --- a/tests_old/Query/ReferenceModelTestCase.php +++ b/tests_old/Query/ReferenceModelTestCase.php @@ -60,7 +60,7 @@ class Doctrine_Query_ReferenceModel_TestCase extends Doctrine_UnitTestCase { $this->connection->unitOfWork->saveAll(); $this->connection->clear(); - $category = $category->getMapper()->find($category->id); + $category = $category->getRepository()->find($category->id); $this->assertEqual($category->name, 'Root'); $this->assertEqual($category->Subcategory[0]->name, 'Sub 1'); diff --git a/tests_old/Query/RegistryTestCase.php b/tests_old/Query/RegistryTestCase.php index 2755df600..a1e85a72f 100644 --- a/tests_old/Query/RegistryTestCase.php +++ b/tests_old/Query/RegistryTestCase.php @@ -62,6 +62,6 @@ class Doctrine_Query_Registry_TestCase extends Doctrine_UnitTestCase $user = new User(); - $user->getMapper()->executeNamedQuery('User.all'); + $user->getEntityManager()->executeNamedQuery('User.all'); } } diff --git a/tests_old/RecordTestCase.php b/tests_old/RecordTestCase.php index 560aff6c9..666b35070 100644 --- a/tests_old/RecordTestCase.php +++ b/tests_old/RecordTestCase.php @@ -89,7 +89,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($account->amount, 2000); - $user = $user->getMapper()->find($user->id); + $user = $user->getRepository()->find($user->id); $this->assertEqual($user->state(), Doctrine_Record::STATE_CLEAN); @@ -148,7 +148,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($gzip->gzip, "compressed"); $this->connection->clear(); - $gzip = $gzip->getMapper()->find($gzip->id); + $gzip = $gzip->getRepository()->find($gzip->id); $this->assertEqual($gzip->gzip, "compressed"); $gzip->gzip = "compressed 2"; @@ -209,7 +209,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertTrue(array_key_exists('id', $a)); $this->assertTrue(is_numeric($a['id'])); $this->connection->clear(); - $user = $user->getMapper()->find($user->id); + $user = $user->getRepository()->find($user->id); $a = $user->toArray(); @@ -231,7 +231,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testUpdatingWithNullValue() { - $user = $this->connection->getMapper('User')->find(5); + $user = $this->connection->getRepository('User')->find(5); $user->name = null; $this->assertEqual($user->name, null); @@ -241,7 +241,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->connection->clear(); - $user = $this->connection->getMapper('User')->find(5); + $user = $this->connection->getRepository('User')->find(5); $this->assertEqual($user->name, null); @@ -249,7 +249,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testSerialize() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $str = serialize($user); $user2 = unserialize($str); @@ -279,7 +279,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($record->entity2, 4); $this->assertEqual($record->entity1, 3); $this->assertEqual($record->state(), Doctrine_Record::STATE_TDIRTY); - $this->assertEqual($record->identifier(), array("entity1" => null, "entity2" => null)); + $this->assertEqual($record->identifier(), array("entity1" => 3, "entity2" => 4)); $record->save(); $this->assertEqual($record->state(), Doctrine_Record::STATE_CLEAN); @@ -287,7 +287,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($record->entity1, 3); $this->assertEqual($record->identifier(), array("entity1" => 3, "entity2" => 4)); - $record = $record->getMapper()->find($record->identifier()); + $record = $record->getRepository()->find($record->identifier()); $this->assertEqual($record->state(), Doctrine_Record::STATE_CLEAN); $this->assertEqual($record->entity2, 4); $this->assertEqual($record->entity1, 3); @@ -300,20 +300,21 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($record->entity2, 5); $this->assertEqual($record->entity1, 2); $this->assertEqual($record->identifier(), array("entity1" => 3, "entity2" => 4)); - + $record->save(); $this->assertEqual($record->state(), Doctrine_Record::STATE_CLEAN); $this->assertEqual($record->entity2, 5); $this->assertEqual($record->entity1, 2); $this->assertEqual($record->identifier(), array("entity1" => 2, "entity2" => 5)); - $record = $record->getMapper()->find($record->identifier()); - + $record = $record->getRepository()->find($record->identifier()); + $this->assertEqual($record->state(), Doctrine_Record::STATE_CLEAN); $this->assertEqual($record->entity2, 5); $this->assertEqual($record->entity1, 2); $this->assertEqual($record->identifier(), array("entity1" => 2, "entity2" => 5)); $record->refresh(); + $this->assertEqual($record->state(), Doctrine_Record::STATE_CLEAN); $this->assertEqual($record->entity2, 5); $this->assertEqual($record->entity1, 2); @@ -368,7 +369,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->connection->unitOfWork->saveAll(); - $task = $task->getMapper()->find($task->identifier()); + $task = $task->getRepository()->find($task->identifier()); $this->assertEqual($task->name, "Task 1"); $this->assertEqual($task->ResourceAlias[0]->name, "Resource 1"); @@ -386,7 +387,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($user->updated, null); $user->save(); $id = $user->identifier(); - $user = $user->getMapper()->find($id); + $user = $user->getRepository()->find($id); $this->assertEqual($user->name, "Jack Daniels"); $this->assertEqual($user->created, null); $this->assertEqual($user->updated, null); @@ -439,12 +440,12 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $elements = $this->connection->query("FROM Element"); $this->assertEqual($elements->count(), 5); - $e = $e->getMapper()->find(1); + $e = $e->getRepository()->find(1); $this->assertEqual($e->name,"parent"); $this->assertEqual($e->Child[0]->name,"child 1"); - $c = $e->getMapper()->find(2); + $c = $e->getRepository()->find(2); $this->assertEqual($c->name, "child 1"); $this->assertEqual($e->Child[0]->parent_id, 1); @@ -553,7 +554,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testUpdate() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $user->set("name","Jack Daniels",true); @@ -566,7 +567,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testCopy() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $new = $user->copy(); $this->assertTrue($new instanceof Doctrine_Record); @@ -583,7 +584,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testCopyAndModify() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $new = $user->copy(); $this->assertTrue($new instanceof Doctrine_Record); @@ -604,7 +605,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testReferences() { - $user = $this->connection->getMapper('User')->find(5); + $user = $this->connection->getRepository('User')->find(5); $this->assertTrue($user->Phonenumber instanceof Doctrine_Collection); $this->assertEqual($user->Phonenumber->count(), 3); @@ -615,7 +616,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase $this->assertEqual($user->Phonenumber->count(), 0); $user->save(); - $user->getMapper()->clear(); + $user->getEntityManager()->clear('User'); $user = $this->objTable->find(5); @@ -736,15 +737,11 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testSaveAssociations() { - $userMapper = $this->connection->getMapper('User'); + $userMapper = $this->connection->getRepository('User'); $user = $userMapper->find(5); - $this->assertTrue($userMapper === $user->getMapper()); - $this->assertTrue($userMapper->getTable() === $user->getMapper()->getTable()); - $this->assertTrue($userMapper->getTable() === $this->conn->getClassMetadata('User')); - $this->assertTrue($this->conn === $userMapper->getConnection()); - $userTable = $userMapper->getTable(); + $userTable = $this->connection->getClassMetadata('User'); /*echo get_class($rel1) . "
"; echo get_class($rel2) . "
"; echo get_class($userTable->getRelation('Group')); @@ -753,7 +750,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase echo "local:" . $rel2->getLocal() . "---foreign:" . $rel2->getForeign() . "
"; echo "........
";*/ - $gf = $this->connection->getMapper("Group"); + $gf = $this->connection->getRepository("Group"); //echo "start"; $this->assertTrue($user->Group instanceof Doctrine_Collection); //echo "end"; @@ -865,14 +862,14 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testCount() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $this->assertTrue(is_integer($user->count())); } public function testGetReference() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $this->assertTrue($user->Email instanceof Doctrine_Record); $this->assertTrue($user->Phonenumber instanceof Doctrine_Collection); @@ -882,13 +879,13 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase } public function testGetIterator() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $this->assertTrue($user->getIterator() instanceof ArrayIterator); } public function testRefreshRelated() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $user->Address[0]->address = "Address #1"; $user->Address[1]->address = "Address #2"; $user->save(); @@ -904,7 +901,7 @@ class Doctrine_Record_TestCase extends Doctrine_UnitTestCase public function testRefreshDeep() { - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); $user->Address[0]->address = "Address #1"; $user->Address[1]->address = "Address #2"; $user->save(); diff --git a/tests_old/Relation/NestTestCase.php b/tests_old/Relation/NestTestCase.php index 6da81c467..263f94c10 100644 --- a/tests_old/Relation/NestTestCase.php +++ b/tests_old/Relation/NestTestCase.php @@ -117,7 +117,7 @@ class Doctrine_Relation_Nest_TestCase extends Doctrine_UnitTestCase $this->connection->clear(); - $e = $e->getMapper()->find($e->id); + $e = $e->getRepository()->find($e->id); $count = count($this->conn); diff --git a/tests_old/TableTestCase.php b/tests_old/TableTestCase.php index 9da171a4a..72f043653 100644 --- a/tests_old/TableTestCase.php +++ b/tests_old/TableTestCase.php @@ -83,7 +83,7 @@ class Doctrine_Table_TestCase extends Doctrine_UnitTestCase $this->connection->clear(); - $t = $this->connection->getMapper('FieldNameTest')->find(1); + $t = $this->connection->getRepository('FieldNameTest')->find(1); $this->assertEqual($t->someColumn, 'abc'); $this->assertEqual($t->someEnum, 'php'); diff --git a/tests_old/Ticket/626DTestCase.php b/tests_old/Ticket/626DTestCase.php index beee47686..a2388c215 100644 --- a/tests_old/Ticket/626DTestCase.php +++ b/tests_old/Ticket/626DTestCase.php @@ -37,7 +37,7 @@ class Doctrine_Ticket_626D_TestCase extends Doctrine_UnitTestCase $student1 = $this->newStudent('T626D_Student1', '07090002', 'First Student'); try { - $student = $this->conn->getMapper('T626D_Student1')->find('07090002'); + $student = $this->conn->getRepository('T626D_Student1')->find('07090002'); $this->pass(); } catch (Exception $e) { $this->fail($e->__toString()); diff --git a/tests_old/ValidatorTestCase.php b/tests_old/ValidatorTestCase.php index 6ea8c1174..1c4636e03 100644 --- a/tests_old/ValidatorTestCase.php +++ b/tests_old/ValidatorTestCase.php @@ -140,7 +140,7 @@ class Doctrine_Validator_TestCase extends Doctrine_UnitTestCase public function testValidate() { $this->manager->setAttribute(Doctrine::ATTR_VALIDATE, Doctrine::VALIDATE_ALL); - $user = $this->connection->getMapper('User')->find(4); + $user = $this->connection->getRepository('User')->find(4); $set = array('password' => 'this is an example of too long password', 'loginname' => 'this is an example of too long loginname', @@ -200,7 +200,7 @@ class Doctrine_Validator_TestCase extends Doctrine_UnitTestCase public function testSave() { $this->manager->setAttribute(Doctrine::ATTR_VALIDATE, Doctrine::VALIDATE_ALL); - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); try { $user->name = "this is an example of too long name not very good example but an example nevertheless"; $user->save(); @@ -260,7 +260,7 @@ class Doctrine_Validator_TestCase extends Doctrine_UnitTestCase } // Tests validateOnUpdate() - $user = $this->connection->getMapper("User")->find(4); + $user = $this->connection->getRepository("User")->find(4); try { $user->name = "The Saint"; // Set correct name $user->password = "Top Secret"; // Set correct password @@ -337,7 +337,7 @@ class Doctrine_Validator_TestCase extends Doctrine_UnitTestCase $r->identifier = '1234'; $r->save(); - $r = $this->connection->getMapper('ValidatorTest_Person')->findAll()->getFirst(); + $r = $this->connection->getRepository('ValidatorTest_Person')->findAll()->getFirst(); $r->identifier = 1234; try { $r->save();