From 21437bb27603654fde16f23dbab6a42f7d1259e9 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Fri, 25 Apr 2014 03:40:54 +0000 Subject: [PATCH] Heavily simplified code on simple object hydrator. Code cleanup on column information cache; by reference cache variable is no longer needed and protected variable is used in a standardized way everywhere. --- UPGRADE.md | 8 ++ .../Internal/Hydration/AbstractHydrator.php | 85 ++++++++++--------- .../ORM/Internal/Hydration/ArrayHydrator.php | 7 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 8 +- .../ORM/Internal/Hydration/ScalarHydrator.php | 6 +- .../Hydration/SimpleObjectHydrator.php | 75 ++++------------ 6 files changed, 78 insertions(+), 111 deletions(-) diff --git a/UPGRADE.md b/UPGRADE.md index ea7489585..cc1d07302 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,13 @@ # Upgrade to 2.5 +## Minor BC BREAK: Custom Hydrators API change + +As of 2.5, `AbstractHydrator` does not enforce the usage of cache as part of +API, and now provides you a clean API for column information through the method +`hydrateColumnInfo($column)`. +Cache variable being passed around by reference is no longer needed since +Hydrators are per query instantiated since Doctrine 2.4. + ## Minor BC BREAK: Entity based ``EntityManager#clear()`` calls follow cascade detach Whenever ``EntityManager#clear()`` method gets called with a given entity class diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 62311c80c..15c573e7d 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -162,7 +162,7 @@ abstract class AbstractHydrator $result = array(); - $this->hydrateRowData($row, $this->_cache, $result); + $this->hydrateRowData($row, $result); return $result; } @@ -209,14 +209,13 @@ abstract class AbstractHydrator * Template method. * * @param array $data The row data. - * @param array $cache The cache to use. * @param array $result The result to fill. * * @return void * * @throws HydrationException */ - protected function hydrateRowData(array $data, array &$cache, array &$result) + protected function hydrateRowData(array $data, array &$result) { throw new HydrationException("hydrateRowData() not implemented by this hydrator."); } @@ -238,19 +237,18 @@ abstract class AbstractHydrator * the values applied. Scalar values are kept in a specific key 'scalars'. * * @param array $data SQL Result Row. - * @param array &$cache Cache for column to field result information. * @param array &$id Dql-Alias => ID-Hash. * @param array &$nonemptyComponents Does this DQL-Alias has at least one non NULL value? * * @return array An array with all the fields (name => value) of the data row, * grouped by their component alias. */ - protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents) + protected function gatherRowData(array $data, array &$id, array &$nonemptyComponents) { $rowData = array('data' => array()); foreach ($data as $key => $value) { - $cacheKeyInfo = $this->getColumnCacheInfo($key, $cache); + $cacheKeyInfo = $this->hydrateColumnInfo($key); if ( ! $cacheKeyInfo) { continue; @@ -332,16 +330,15 @@ abstract class AbstractHydrator * of elements as before. * * @param array $data - * @param array $cache * * @return array The processed row. */ - protected function gatherScalarRowData(&$data, &$cache) + protected function gatherScalarRowData(&$data) { $rowData = array(); foreach ($data as $key => $value) { - $cacheKeyInfo = $this->getColumnCacheInfo($key, $cache); + $cacheKeyInfo = $this->hydrateColumnInfo($key); if ( ! $cacheKeyInfo) { continue; @@ -379,17 +376,16 @@ abstract class AbstractHydrator } /** - * Retrieve column information from cache. + * Retrieve column information from ResultSetMapping. * - * @param string $key Column name - * @param array &$cache Cache for column to field result information. + * @param string $key Column name * * @return array|null */ - protected function getColumnCacheInfo($key, &$cache) + protected function hydrateColumnInfo($key) { - if (isset($cache[$key])) { - return $cache[$key]; + if (isset($this->_cache[$key])) { + return $this->_cache[$key]; } switch (true) { @@ -398,45 +394,50 @@ abstract class AbstractHydrator $fieldName = $this->_rsm->fieldMappings[$key]; $classMetadata = $this->_em->getClassMetadata($this->_rsm->declaringClasses[$key]); - $cache[$key]['fieldName'] = $fieldName; - $cache[$key]['type'] = Type::getType($classMetadata->fieldMappings[$fieldName]['type']); - $cache[$key]['isIdentifier'] = $classMetadata->isIdentifier($fieldName); - $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; - - return $cache[$key]; + return $this->_cache[$key] = array( + 'isIdentifier' => $classMetadata->isIdentifier($fieldName), + 'fieldName' => $fieldName, + 'type' => Type::getType($classMetadata->fieldMappings[$fieldName]['type']), + 'dqlAlias' => $this->_rsm->columnOwnerMap[$key], + ); case (isset($this->_rsm->newObjectMappings[$key])): // WARNING: A NEW object is also a scalar, so it must be declared before! $mapping = $this->_rsm->newObjectMappings[$key]; - $cache[$key]['isScalar'] = true; - $cache[$key]['isNewObjectParameter'] = true; - $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; - $cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]); - $cache[$key]['argIndex'] = $mapping['argIndex']; - $cache[$key]['objIndex'] = $mapping['objIndex']; - $cache[$key]['class'] = new \ReflectionClass($mapping['className']); - - return $cache[$key]; + return $this->_cache[$key] = array( + 'isScalar' => true, + 'isNewObjectParameter' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + 'argIndex' => $mapping['argIndex'], + 'objIndex' => $mapping['objIndex'], + 'class' => new \ReflectionClass($mapping['className']), + ); case (isset($this->_rsm->scalarMappings[$key])): - $cache[$key]['fieldName'] = $this->_rsm->scalarMappings[$key]; - $cache[$key]['type'] = Type::getType($this->_rsm->typeMappings[$key]); - $cache[$key]['isScalar'] = true; - - return $cache[$key]; + return $this->_cache[$key] = array( + 'isScalar' => true, + 'fieldName' => $this->_rsm->scalarMappings[$key], + 'type' => Type::getType($this->_rsm->typeMappings[$key]), + ); case (isset($this->_rsm->metaMappings[$key])): // Meta column (has meaning in relational schema only, i.e. foreign keys or discriminator columns). $fieldName = $this->_rsm->metaMappings[$key]; - $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$this->_rsm->columnOwnerMap[$key]]); + $dqlAlias = $this->_rsm->columnOwnerMap[$key]; + $classMetadata = $this->_em->getClassMetadata($this->_rsm->aliasMap[$dqlAlias]); + $type = isset($this->_rsm->typeMappings[$key]) + ? Type::getType($this->_rsm->typeMappings[$key]) + : null; - $cache[$key]['isMetaColumn'] = true; - $cache[$key]['fieldName'] = $fieldName; - $cache[$key]['dqlAlias'] = $this->_rsm->columnOwnerMap[$key]; - $cache[$key]['isIdentifier'] = isset($this->_rsm->isIdentifierColumn[$cache[$key]['dqlAlias']][$key]); - - return $cache[$key]; + return $this->_cache[$key] = array( + 'isIdentifier' => isset($this->_rsm->isIdentifierColumn[$dqlAlias][$key]), + 'isMetaColumn' => true, + 'fieldName' => $fieldName, + 'type' => $type, + 'dqlAlias' => $dqlAlias, + ); } // this column is a left over, maybe from a LIMIT query hack for example in Oracle or DB2 diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index f6cab279d..e69b84ffc 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -91,10 +91,9 @@ class ArrayHydrator extends AbstractHydrator protected function hydrateAllData() { $result = array(); - $cache = array(); while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { - $this->hydrateRowData($data, $cache, $result); + $this->hydrateRowData($data, $result); } return $result; @@ -103,12 +102,12 @@ class ArrayHydrator extends AbstractHydrator /** * {@inheritdoc} */ - protected function hydrateRowData(array $row, array &$cache, array &$result) + protected function hydrateRowData(array $row, array &$result) { // 1) Initialize $id = $this->_idTemplate; // initialize the id-memory $nonemptyComponents = array(); - $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); // 2) Now hydrate the data found in the current row. foreach ($rowData['data'] as $dqlAlias => $data) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index d39e30aaa..a58d1b632 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -173,10 +173,9 @@ class ObjectHydrator extends AbstractHydrator protected function hydrateAllData() { $result = array(); - $cache = array(); while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { - $this->hydrateRowData($row, $cache, $result); + $this->hydrateRowData($row, $result); } // Take snapshots from all newly initialized collections @@ -351,18 +350,17 @@ class ObjectHydrator extends AbstractHydrator * specified by the FROM clause in a DQL query. * * @param array $row The data of the row to process. - * @param array $cache The cache to use. * @param array $result The result array to fill. * * @return void */ - protected function hydrateRowData(array $row, array &$cache, array &$result) + protected function hydrateRowData(array $row, array &$result) { // Initialize $id = $this->idTemplate; // initialize the id-memory $nonemptyComponents = array(); // Split the row data into chunks of class data. - $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents); + $rowData = $this->gatherRowData($row, $id, $nonemptyComponents); // Hydrate the data chunks foreach ($rowData['data'] as $dqlAlias => $data) { diff --git a/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php index 23b0abe2c..1890f75d0 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php @@ -39,7 +39,7 @@ class ScalarHydrator extends AbstractHydrator $cache = array(); while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { - $this->hydrateRowData($data, $cache, $result); + $this->hydrateRowData($data, $result); } return $result; @@ -48,8 +48,8 @@ class ScalarHydrator extends AbstractHydrator /** * {@inheritdoc} */ - protected function hydrateRowData(array $data, array &$cache, array &$result) + protected function hydrateRowData(array $data, array &$result) { - $result[] = $this->gatherScalarRowData($data, $cache); + $result[] = $this->gatherScalarRowData($data); } } diff --git a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php index 504d82c51..5f5b06a2a 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php @@ -42,10 +42,9 @@ class SimpleObjectHydrator extends AbstractHydrator protected function hydrateAllData() { $result = array(); - $cache = array(); while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { - $this->hydrateRowData($row, $cache, $result); + $this->hydrateRowData($row, $result); } $this->_em->getUnitOfWork()->triggerEagerLoads(); @@ -81,7 +80,7 @@ class SimpleObjectHydrator extends AbstractHydrator /** * {@inheritdoc} */ - protected function hydrateRowData(array $sqlResult, array &$cache, array &$result) + protected function hydrateRowData(array $sqlResult, array &$result) { $entityName = $this->class->name; $data = array(); @@ -103,30 +102,35 @@ class SimpleObjectHydrator extends AbstractHydrator if ( ! isset($discrMap[$sqlResult[$discrColumnName]])) { throw HydrationException::invalidDiscriminatorValue($sqlResult[$discrColumnName], array_keys($discrMap)); } - + $entityName = $discrMap[$sqlResult[$discrColumnName]]; unset($sqlResult[$discrColumnName]); } foreach ($sqlResult as $column => $value) { - // Hydrate column information if not yet present - if ( ! isset($cache[$column])) { - if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) { - continue; - } + // An ObjectHydrator should be used instead of SimpleObjectHydrator + if (isset($this->_rsm->relationMap[$column])) { + throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column)); + } - $cache[$column] = $info; + $cacheKeyInfo = $this->hydrateColumnInfo($column); + + if ( ! $cacheKeyInfo) { + continue; } // Convert field to a valid PHP value - if (isset($cache[$column]['type'])) { - $value = Type::getType($cache[$column]['type'])->convertToPHPValue($value, $this->_platform); + if (isset($cacheKeyInfo['type'])) { + $type = $cacheKeyInfo['type']; + $value = $type->convertToPHPValue($value, $this->_platform); } + $fieldName = $cacheKeyInfo['fieldName']; + // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator) - if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) { - $data[$cache[$column]['name']] = $value; + if ( ! isset($data[$fieldName]) || $value !== null) { + $data[$fieldName] = $value; } } @@ -139,47 +143,4 @@ class SimpleObjectHydrator extends AbstractHydrator $result[] = $entity; } - - /** - * Retrieve column information form ResultSetMapping. - * - * @param string $entityName - * @param string $column - * - * @return array - */ - protected function hydrateColumnInfo($entityName, $column) - { - - if (isset($this->_rsm->fieldMappings[$column])) { - $name = $this->_rsm->fieldMappings[$column]; - $class = isset($this->declaringClasses[$column]) - ? $this->declaringClasses[$column] - : $this->class; - - // If class is not part of the inheritance, ignore - if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) { - return null; - } - - return array( - 'name' => $name, - 'type' => $class->fieldMappings[$name]['type'] - ); - } - - if (isset($this->_rsm->metaMappings[$column])) { - return array( - 'name' => $this->_rsm->metaMappings[$column], - 'type' => (isset($this->_rsm->typeMappings[$column]) ? $this->_rsm->typeMappings[$column] : null) - ); - } - - // An ObjectHydrator should be used instead of SimpleObjectHydrator - if (isset($this->_rsm->relationMap[$column])) { - throw new \Exception(sprintf('Unable to retrieve association information for column "%s"', $column)); - } - - return null; - } }