diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php index 1305af98a..80973138f 100644 --- a/lib/Doctrine/ClassMetadata.php +++ b/lib/Doctrine/ClassMetadata.php @@ -555,6 +555,33 @@ class Doctrine_ClassMetadata extends Doctrine_Configurable implements Serializab return isset($this->_fieldNames[$columnName]) ? $this->_fieldNames[$columnName] : $columnName; } + + private $_subclassFieldNames = array(); + + /** + * + */ + public function lookupFieldName($columnName) + { + if (isset($this->_fieldNames[$columnName])) { + return $this->_fieldNames[$columnName]; + } else if (isset($this->_subclassFieldNames[$columnName])) { + return $this->_subclassFieldNames[$columnName]; + } + + $classMetadata = $this; + $conn = $this->_conn; + + foreach ($classMetadata->getSubclasses() as $subClass) { + $subClassMetadata = $conn->getClassMetadata($subClass); + if ($subClassMetadata->hasColumn($columnName)) { + $this->_subclassFieldNames[$columnName] = $subClassMetadata->getFieldName($columnName); + return $this->_subclassFieldNames[$columnName]; + } + } + + throw new Doctrine_ClassMetadata_Exception("No field name found for column name '$columnName' during lookup."); + } /** * @deprecated diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php index a3fd61719..2131b11a5 100644 --- a/lib/Doctrine/Connection.php +++ b/lib/Doctrine/Connection.php @@ -220,10 +220,6 @@ abstract class Doctrine_Connection extends Doctrine_Configurable implements Coun } } - - /* - * ----------- Connection methods --------------- - */ /** * getOption * diff --git a/lib/Doctrine/EntityPersister/Abstract.php b/lib/Doctrine/EntityPersister/Abstract.php index 73cbb1dd2..9f17d3687 100644 --- a/lib/Doctrine/EntityPersister/Abstract.php +++ b/lib/Doctrine/EntityPersister/Abstract.php @@ -167,221 +167,6 @@ abstract class Doctrine_EntityPersister_Abstract return $this->_em; } - /** - * creates a new record - * - * @param $array an array where keys are field names and - * values representing field values - * @return Doctrine_Entity the created record object - * @todo To EntityManager. - */ - public function create(array $array = array()) - { - $record = new $this->_domainClassName(); - $record->fromArray($array); - - return $record; - } - - public function addEntityListener(Doctrine_Record_Listener $listener) - { - if ( ! in_array($listener, $this->_entityListeners)) { - $this->_entityListeners[] = $listener; - return true; - } - return false; - } - - public function removeEntityListener(Doctrine_Record_Listener $listener) - { - if ($key = array_search($listener, $this->_entityListeners, true)) { - unset($this->_entityListeners[$key]); - return true; - } - return false; - } - - public function notifyEntityListeners(Doctrine_Entity $entity, $callback, $eventType) - { - if ($this->_entityListeners) { - $event = new Doctrine_Event($entity, $eventType); - foreach ($this->_entityListeners as $listener) { - $listener->$callback($event); - } - } - } - - /** - * Enter description here... - * - * @param Doctrine_Entity $entity - * @return unknown - * @todo To EntityManager - */ - public function detach(Doctrine_Entity $entity) - { - return $this->_conn->getUnitOfWork()->detach($entity); - } - - /** - * clear - * clears the first level cache (identityMap) - * - * @return void - * @todo what about a more descriptive name? clearIdentityMap? - * @todo To EntityManager - */ - public function clear() - { - $this->_conn->getUnitOfWork()->clearIdentitiesForEntity($this->_classMetadata->getRootClassName()); - } - - /** - * addRecord - * adds a record to identity map - * - * @param Doctrine_Entity $record record to be added - * @return boolean - * @todo Better name? registerRecord? Move elsewhere to the new location of the identity maps. - * @todo Remove. - */ - public function addRecord(Doctrine_Entity $record) - { - if ($this->_conn->unitOfWork->contains($record)) { - return false; - } - $this->_conn->unitOfWork->registerIdentity($record); - - return true; - } - - /** - * Tells the mapper to manage the entity if it's not already managed. - * - * @return boolean TRUE if the entity was previously not managed and is now managed, - * FALSE otherwise (the entity is already managed). - * @todo Remove. - */ - public function manage(Doctrine_Entity $record) - { - return $this->_conn->unitOfWork->manage($record); - } - - /** - * removeRecord - * removes a record from the identity map, returning true if the record - * was found and removed and false if the record wasn't found. - * - * @param Doctrine_Entity $record record to be removed - * @return boolean - * @todo Move elsewhere to the new location of the identity maps. - */ - public function removeRecord(Doctrine_Entity $record) - { - if ($this->_conn->unitOfWork->contains($record)) { - $this->_conn->unitOfWork->unregisterIdentity($record); - return true; - } - - return false; - } - - /** - * getRecord - * First checks if record exists in identityMap, if not - * returns a new record. - * - * @return Doctrine_Entity - * @todo To EntityManager. - */ - public function getRecord(array $data) - { - if ( ! empty($data)) { - $identifierFieldNames = $this->_classMetadata->getIdentifier(); - - $found = false; - foreach ($identifierFieldNames as $fieldName) { - if ( ! isset($data[$fieldName])) { - // primary key column not found return new record - $found = true; - break; - } - $id[] = $data[$fieldName]; - } - - if ($found) { - return new $this->_domainClassName(true, $data); - } - - $idHash = $this->_conn->unitOfWork->getIdentifierHash($id); - - if ($record = $this->_conn->unitOfWork->tryGetByIdHash($idHash, - $this->_classMetadata->getRootClassName())) { - $record->hydrate($data); - } else { - $record = new $this->_domainClassName(false, $data); - $this->_conn->unitOfWork->registerIdentity($record); - } - $data = array(); - } else { - $record = new $this->_domainClassName(true, $data); - } - - return $record; - } - - /** - * @param $id database row id - * @todo Looks broken. Figure out an implementation and decide whether its needed. - */ - final public function getProxy($id = null) - { - if ($id !== null) { - $identifierColumnNames = $this->_classMetadata->getIdentifierColumnNames(); - $query = 'SELECT ' . implode(', ', $identifierColumnNames) - . ' FROM ' . $this->_classMetadata->getTableName() - . ' WHERE ' . implode(' = ? && ', $identifierColumnNames) . ' = ?'; - $query = $this->applyInheritance($query); - - $params = array_merge(array($id),array()); - - $data = $this->_conn->execute($query, $params)->fetch(PDO::FETCH_ASSOC); - - if ($data === false) { - return false; - } - } - - return $this->getRecord($data); - } - - /** - * applyInheritance - * @param $where query where part to be modified - * @return string query where part with column aggregation inheritance added - * @todo What to do with this? Remove if possible. - */ - final public function applyInheritance($where) - { - $discCol = $this->_classMetadata->getInheritanceOption('discriminatorColumn'); - if ( ! $discCol) { - return $where; - } - - $discMap = $this->_classMetadata->getInheritanceOption('discriminatorMap'); - $inheritanceMap = array($discCol => array_search($this->_domainClassName, $discMap)); - if ( ! empty($inheritanceMap)) { - $a = array(); - foreach ($inheritanceMap as $column => $value) { - $a[] = $column . ' = ?'; - } - $i = implode(' AND ', $a); - $where .= ' AND ' . $i; - } - - return $where; - } - /** * prepareValue * this method performs special data preparation depending on @@ -410,7 +195,7 @@ abstract class Doctrine_EntityPersister_Abstract * @todo To EntityManager. Make private and use in createEntity(). * .. Or, maybe better: Move to hydrator for performance reasons. */ - public function prepareValue($fieldName, $value, $typeHint = null) + /*public function prepareValue($fieldName, $value, $typeHint = null) { if ($value === $this->_nullObject) { return $this->_nullObject; @@ -449,7 +234,7 @@ abstract class Doctrine_EntityPersister_Abstract } } return $value; - } + }*/ /** * getComponentName @@ -462,14 +247,6 @@ abstract class Doctrine_EntityPersister_Abstract return $this->_domainClassName; } - /** - * Gets the name of the class the mapper is used for. - */ - public function getMappedClassName() - { - return $this->_domainClassName; - } - /** * Saves an entity and all it's related entities. * @@ -788,10 +565,10 @@ abstract class Doctrine_EntityPersister_Abstract return $this->_classMetadata; } - public function getFieldName($columnName) + /*public function getFieldName($columnName) { return $this->_classMetadata->getFieldName($columnName); - } + }*/ public function getFieldNames() { diff --git a/lib/Doctrine/EntityPersister/JoinedSubclass.php b/lib/Doctrine/EntityPersister/JoinedSubclass.php index 65c9b8b5e..05ea1117c 100644 --- a/lib/Doctrine/EntityPersister/JoinedSubclass.php +++ b/lib/Doctrine/EntityPersister/JoinedSubclass.php @@ -226,7 +226,7 @@ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_A /** * */ - public function getFieldName($columnName) + /*public function getFieldName($columnName) { if (isset($this->_columnNameFieldNameMap[$columnName])) { return $this->_columnNameFieldNameMap[$columnName]; @@ -249,7 +249,7 @@ class Doctrine_EntityPersister_JoinedSubclass extends Doctrine_EntityPersister_A } throw new Doctrine_Mapper_Exception("No field name found for column name '$columnName'."); - } + }*/ /** * diff --git a/lib/Doctrine/HydratorNew.php b/lib/Doctrine/HydratorNew.php index 5c85bae06..cafdcc484 100644 --- a/lib/Doctrine/HydratorNew.php +++ b/lib/Doctrine/HydratorNew.php @@ -368,14 +368,13 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract $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(); + $classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; // 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); + $fieldName = $classMetadata->lookupFieldName($columnName); $cache[$key]['isScalar'] = false; } @@ -398,7 +397,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract } } - $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; + $class = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $dqlAlias = $cache[$key]['dqlAlias']; $fieldName = $cache[$key]['fieldName']; @@ -414,7 +413,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract if ($cache[$key]['isSimpleType']) { $rowData[$dqlAlias][$fieldName] = $value; } else { - $rowData[$dqlAlias][$fieldName] = $mapper->prepareValue( + $rowData[$dqlAlias][$fieldName] = $class->prepareValue( $fieldName, $value, $cache[$key]['type']); } @@ -453,14 +452,13 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract $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(); + $classMetadata = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; // 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); + $fieldName = $classMetadata->lookupFieldName($columnName); $cache[$key]['isScalar'] = false; } @@ -476,15 +474,15 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract } } - $mapper = $this->_queryComponents[$cache[$key]['dqlAlias']]['mapper']; + $class = $this->_queryComponents[$cache[$key]['dqlAlias']]['table']; $dqlAlias = $cache[$key]['dqlAlias']; $fieldName = $cache[$key]['fieldName']; if ($cache[$key]['isSimpleType'] || $cache[$key]['isScalar']) { $rowData[$dqlAlias . '_' . $fieldName] = $value; } else { - $rowData[$dqlAlias . '_' . $fieldName] = $mapper->prepareValue( - $fieldName, $value, $cache[$key]['type']); + $rowData[$dqlAlias . '_' . $fieldName] = $this->prepareValue( + $class, $fieldName, $value, $cache[$key]['type']); } } @@ -515,6 +513,77 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract return $name == 'doctrine_rownum'; } + /** + * prepareValue + * this method performs special data preparation depending on + * the type of the given column + * + * 1. It unserializes array and object typed columns + * 2. Uncompresses gzip typed columns + * 3. Gets the appropriate enum values for enum typed columns + * 4. Initializes special null object pointer for null values (for fast column existence checking purposes) + * + * example: + * + * $field = 'name'; + * $value = null; + * $table->prepareValue($field, $value); // Doctrine_Null + * + * + * @throws Doctrine_Table_Exception if unserialization of array/object typed column fails or + * @throws Doctrine_Table_Exception if uncompression of gzip typed column fails * + * @param string $field the name of the field + * @param string $value field value + * @param string $typeHint A hint on the type of the value. If provided, the type lookup + * for the field can be skipped. Used i.e. during hydration to + * improve performance on large and/or complex results. + * @return mixed prepared value + * @todo To EntityManager. Make private and use in createEntity(). + * .. Or, maybe better: Move to hydrator for performance reasons. + */ + public function prepareValue(Doctrine_ClassMetadata $class, $fieldName, $value, $typeHint = null) + { + if ($value === $this->_nullObject) { + return $this->_nullObject; + } else if ($value === null) { + return null; + } else { + $type = is_null($typeHint) ? $class->getTypeOf($fieldName) : $typeHint; + switch ($type) { + case 'integer': + case 'string'; + // don't do any casting here PHP INT_MAX is smaller than what the databases support + break; + case 'enum': + return $class->enumValue($fieldName, $value); + break; + case 'boolean': + return (boolean) $value; + break; + case 'array': + case 'object': + if (is_string($value)) { + $value = unserialize($value); + if ($value === false) { + throw new Doctrine_Hydrator_Exception('Unserialization of ' . $fieldName . ' failed.'); + } + return $value; + } + break; + case 'gzip': + $value = gzuncompress($value); + if ($value === false) { + throw new Doctrine_Hydrator_Exception('Uncompressing of ' . $fieldName . ' failed.'); + } + return $value; + break; + } + } + return $value; + } + + + /** Needed only temporarily until the new parser is ready */ private $_isResultMixed = false; public function setResultMixed($bool) diff --git a/lib/Doctrine/Query/Production/RangeVariableDeclaration.php b/lib/Doctrine/Query/Production/RangeVariableDeclaration.php index 700aa47c7..8378bb684 100644 --- a/lib/Doctrine/Query/Production/RangeVariableDeclaration.php +++ b/lib/Doctrine/Query/Production/RangeVariableDeclaration.php @@ -126,7 +126,6 @@ class Doctrine_Query_Production_RangeVariableDeclaration extends Doctrine_Query_ // Building queryComponent $queryComponent = array( 'metadata' => $classMetadata, - 'mapper' => $manager->getEntityPersister($componentName), 'parent' => null, 'relation' => null, 'map' => null, diff --git a/lib/Doctrine/Query/Production/VariableDeclaration.php b/lib/Doctrine/Query/Production/VariableDeclaration.php index 91eabe90e..c2509da8f 100644 --- a/lib/Doctrine/Query/Production/VariableDeclaration.php +++ b/lib/Doctrine/Query/Production/VariableDeclaration.php @@ -93,7 +93,6 @@ class Doctrine_Query_Production_VariableDeclaration extends Doctrine_Query_Produ // Building queryComponent $queryComponent = array( 'metadata' => $classMetadata, - 'mapper' => $manager->getEntityPersister($this->_componentName), 'parent' => null, 'relation' => null, 'map' => null,