1
0
mirror of synced 2024-12-05 03:06:05 +03:00

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.

This commit is contained in:
Guilherme Blanco 2014-04-25 03:40:54 +00:00
parent e8e86205f5
commit 21437bb276
6 changed files with 78 additions and 111 deletions

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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;
}
}