1
0
mirror of synced 2025-01-19 15:01:40 +03:00

Initial code optimization in Hydrators.

This commit is contained in:
Guilherme Blanco 2011-11-02 22:08:24 -02:00
parent d532de9da3
commit d1bfd57fd9
6 changed files with 257 additions and 145 deletions

View File

@ -22,15 +22,17 @@ namespace Doctrine\ORM\Internal\Hydration;
use PDO, use PDO,
Doctrine\DBAL\Connection, Doctrine\DBAL\Connection,
Doctrine\DBAL\Types\Type, Doctrine\DBAL\Types\Type,
Doctrine\ORM\EntityManager; Doctrine\ORM\EntityManager,
Doctrine\ORM\Mapping\ClassMetadata;
/** /**
* Base class for all hydrators. A hydrator is a class that provides some form * Base class for all hydrators. A hydrator is a class that provides some form
* of transformation of an SQL result set into another structure. * of transformation of an SQL result set into another structure.
* *
* @since 2.0 * @since 2.0
* @author Konsta Vesterinen <kvesteri@cc.hut.fi> * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*/ */
abstract class AbstractHydrator abstract class AbstractHydrator
{ {
@ -62,9 +64,9 @@ abstract class AbstractHydrator
*/ */
public function __construct(EntityManager $em) public function __construct(EntityManager $em)
{ {
$this->_em = $em; $this->_em = $em;
$this->_platform = $em->getConnection()->getDatabasePlatform(); $this->_platform = $em->getConnection()->getDatabasePlatform();
$this->_uow = $em->getUnitOfWork(); $this->_uow = $em->getUnitOfWork();
} }
/** /**
@ -72,14 +74,17 @@ abstract class AbstractHydrator
* *
* @param object $stmt * @param object $stmt
* @param object $resultSetMapping * @param object $resultSetMapping
*
* @return IterableResult * @return IterableResult
*/ */
public function iterate($stmt, $resultSetMapping, array $hints = array()) public function iterate($stmt, $resultSetMapping, array $hints = array())
{ {
$this->_stmt = $stmt; $this->_stmt = $stmt;
$this->_rsm = $resultSetMapping; $this->_rsm = $resultSetMapping;
$this->_hints = $hints; $this->_hints = $hints;
$this->_prepare();
$this->prepare();
return new IterableResult($this); return new IterableResult($this);
} }
@ -92,12 +97,16 @@ abstract class AbstractHydrator
*/ */
public function hydrateAll($stmt, $resultSetMapping, array $hints = array()) public function hydrateAll($stmt, $resultSetMapping, array $hints = array())
{ {
$this->_stmt = $stmt; $this->_stmt = $stmt;
$this->_rsm = $resultSetMapping; $this->_rsm = $resultSetMapping;
$this->_hints = $hints; $this->_hints = $hints;
$this->_prepare();
$result = $this->_hydrateAll(); $this->prepare();
$this->_cleanup();
$result = $this->hydrateAllData();
$this->cleanup();
return $result; return $result;
} }
@ -110,12 +119,17 @@ abstract class AbstractHydrator
public function hydrateRow() public function hydrateRow()
{ {
$row = $this->_stmt->fetch(PDO::FETCH_ASSOC); $row = $this->_stmt->fetch(PDO::FETCH_ASSOC);
if ( ! $row) { if ( ! $row) {
$this->_cleanup(); $this->cleanup();
return false; return false;
} }
$result = array(); $result = array();
$this->_hydrateRow($row, $this->_cache, $result);
$this->hydrateRowData($row, $this->_cache, $result);
return $result; return $result;
} }
@ -123,16 +137,17 @@ abstract class AbstractHydrator
* Excutes one-time preparation tasks, once each time hydration is started * Excutes one-time preparation tasks, once each time hydration is started
* through {@link hydrateAll} or {@link iterate()}. * through {@link hydrateAll} or {@link iterate()}.
*/ */
protected function _prepare() protected function prepare()
{} {}
/** /**
* Excutes one-time cleanup tasks at the end of a hydration that was initiated * Excutes one-time cleanup tasks at the end of a hydration that was initiated
* through {@link hydrateAll} or {@link iterate()}. * through {@link hydrateAll} or {@link iterate()}.
*/ */
protected function _cleanup() protected function cleanup()
{ {
$this->_rsm = null; $this->_rsm = null;
$this->_stmt->closeCursor(); $this->_stmt->closeCursor();
$this->_stmt = null; $this->_stmt = null;
} }
@ -146,15 +161,15 @@ abstract class AbstractHydrator
* @param array $cache The cache to use. * @param array $cache The cache to use.
* @param mixed $result The result to fill. * @param mixed $result The result to fill.
*/ */
protected function _hydrateRow(array $data, array &$cache, array &$result) protected function hydrateRowData(array $data, array &$cache, array &$result)
{ {
throw new HydrationException("_hydrateRow() not implemented by this hydrator."); throw new HydrationException("hydrateRowData() not implemented by this hydrator.");
} }
/** /**
* Hydrates all rows from the current statement instance at once. * Hydrates all rows from the current statement instance at once.
*/ */
abstract protected function _hydrateAll(); abstract protected function hydrateAllData();
/** /**
* Processes a row of the result set. * Processes a row of the result set.
@ -173,7 +188,7 @@ abstract class AbstractHydrator
* @return array An array with all the fields (name => value) of the data row, * @return array An array with all the fields (name => value) of the data row,
* grouped by their component alias. * grouped by their component alias.
*/ */
protected function _gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents) protected function gatherRowData(array $data, array &$cache, array &$id, array &$nonemptyComponents)
{ {
$rowData = array(); $rowData = array();
@ -207,6 +222,7 @@ abstract class AbstractHydrator
if (isset($cache[$key]['isScalar'])) { if (isset($cache[$key]['isScalar'])) {
$rowData['scalars'][$cache[$key]['fieldName']] = $value; $rowData['scalars'][$cache[$key]['fieldName']] = $value;
continue; continue;
} }
@ -220,10 +236,11 @@ abstract class AbstractHydrator
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) { if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value; $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
} }
continue; continue;
} }
// in an inheritance hierachy the same field could be defined several times. // in an inheritance hierarchy the same field could be defined several times.
// We overwrite this value so long we dont have a non-null value, that value we keep. // We overwrite this value so long we dont have a non-null value, that value we keep.
// Per definition it cannot be that a field is defined several times and has several values. // Per definition it cannot be that a field is defined several times and has several values.
if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) { if (isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) && $value === null) {
@ -250,9 +267,10 @@ abstract class AbstractHydrator
* *
* @param array $data * @param array $data
* @param array $cache * @param array $cache
*
* @return array The processed row. * @return array The processed row.
*/ */
protected function _gatherScalarRowData(&$data, &$cache) protected function gatherScalarRowData(&$data, &$cache)
{ {
$rowData = array(); $rowData = array();
@ -295,7 +313,14 @@ abstract class AbstractHydrator
return $rowData; return $rowData;
} }
protected function registerManaged($class, $entity, $data) /**
* Register entity as managed in UnitOfWork.
*
* @param Doctrine\ORM\Mapping\ClassMetadata $class
* @param object $entity
* @param array $data
*/
protected function registerManaged(ClassMetadata $class, $entity, array $data)
{ {
if ($class->isIdentifierComposite) { if ($class->isIdentifierComposite) {
$id = array(); $id = array();
@ -313,6 +338,7 @@ abstract class AbstractHydrator
$id = array($class->identifier[0] => $data[$class->identifier[0]]); $id = array($class->identifier[0] => $data[$class->identifier[0]]);
} }
} }
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
} }
} }

View File

@ -25,8 +25,9 @@ use PDO, Doctrine\DBAL\Connection, Doctrine\ORM\Mapping\ClassMetadata;
* The ArrayHydrator produces a nested array "graph" that is often (not always) * The ArrayHydrator produces a nested array "graph" that is often (not always)
* interchangeable with the corresponding object graph for read-only access. * interchangeable with the corresponding object graph for read-only access.
* *
* @since 2.0
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 1.0 * @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*/ */
class ArrayHydrator extends AbstractHydrator class ArrayHydrator extends AbstractHydrator
{ {
@ -38,45 +39,54 @@ class ArrayHydrator extends AbstractHydrator
private $_idTemplate = array(); private $_idTemplate = array();
private $_resultCounter = 0; private $_resultCounter = 0;
/** @override */ /**
protected function _prepare() * {@inheritdoc}
*/
protected function prepare()
{ {
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1; $this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
$this->_identifierMap = array(); $this->_identifierMap = array();
$this->_resultPointers = array(); $this->_resultPointers = array();
$this->_idTemplate = array(); $this->_idTemplate = array();
$this->_resultCounter = 0; $this->_resultCounter = 0;
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) { foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array(); $this->_identifierMap[$dqlAlias] = array();
$this->_resultPointers[$dqlAlias] = array(); $this->_resultPointers[$dqlAlias] = array();
$this->_idTemplate[$dqlAlias] = ''; $this->_idTemplate[$dqlAlias] = '';
} }
} }
/** @override */ /**
protected function _hydrateAll() * {@inheritdoc}
*/
protected function hydrateAllData()
{ {
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { while ($data = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($data, $cache, $result); $this->hydrateRowData($data, $cache, $result);
} }
return $result; return $result;
} }
/** @override */ /**
protected function _hydrateRow(array $row, array &$cache, array &$result) * {@inheritdoc}
*/
protected function hydrateRowData(array $row, array &$cache, array &$result)
{ {
// 1) Initialize // 1) Initialize
$id = $this->_idTemplate; // initialize the id-memory $id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array(); $nonemptyComponents = array();
$rowData = $this->_gatherRowData($row, $cache, $id, $nonemptyComponents); $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end. // Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) { if (isset($rowData['scalars'])) {
$scalars = $rowData['scalars']; $scalars = $rowData['scalars'];
unset($rowData['scalars']); unset($rowData['scalars']);
if (empty($rowData)) { if (empty($rowData)) {
++$this->_resultCounter; ++$this->_resultCounter;
} }
@ -111,7 +121,7 @@ class ArrayHydrator extends AbstractHydrator
} }
$relationAlias = $this->_rsm->relationMap[$dqlAlias]; $relationAlias = $this->_rsm->relationMap[$dqlAlias];
$relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias]; $relation = $this->getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias];
// Check the type of the relation (many or single-valued) // Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) { if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
@ -230,28 +240,45 @@ class ArrayHydrator extends AbstractHydrator
{ {
if ($coll === null) { if ($coll === null) {
unset($this->_resultPointers[$dqlAlias]); // Ticket #1228 unset($this->_resultPointers[$dqlAlias]); // Ticket #1228
return; return;
} }
if ($index !== false) { if ($index !== false) {
$this->_resultPointers[$dqlAlias] =& $coll[$index]; $this->_resultPointers[$dqlAlias] =& $coll[$index];
return; return;
} else {
if ($coll) {
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
} else {
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
}
}
} }
if ( ! $coll) {
return;
}
if ($oneToOne) {
$this->_resultPointers[$dqlAlias] =& $coll;
return;
}
end($coll);
$this->_resultPointers[$dqlAlias] =& $coll[key($coll)];
return;
} }
private function _getClassMetadata($className) /**
* Retrieve ClassMetadata associated to entity class name.
*
* @param string $className
*
* @return Doctrine\ORM\Mapping\ClassMetadata
*/
private function getClassMetadata($className)
{ {
if ( ! isset($this->_ce[$className])) { if ( ! isset($this->_ce[$className])) {
$this->_ce[$className] = $this->_em->getClassMetadata($className); $this->_ce[$className] = $this->_em->getClassMetadata($className);
} }
return $this->_ce[$className]; return $this->_ce[$className];
} }
} }

View File

@ -29,8 +29,10 @@ use PDO,
/** /**
* The ObjectHydrator constructs an object graph out of an SQL result set. * The ObjectHydrator constructs an object graph out of an SQL result set.
* *
* @since 2.0
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
*
* @internal Highly performance-sensitive code. * @internal Highly performance-sensitive code.
*/ */
class ObjectHydrator extends AbstractHydrator class ObjectHydrator extends AbstractHydrator
@ -53,7 +55,7 @@ class ObjectHydrator extends AbstractHydrator
/** @override */ /** @override */
protected function _prepare() protected function prepare()
{ {
$this->_identifierMap = $this->_identifierMap =
$this->_resultPointers = $this->_resultPointers =
@ -113,11 +115,12 @@ class ObjectHydrator extends AbstractHydrator
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function _cleanup() protected function cleanup()
{ {
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true; $eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup(); parent::cleanup();
$this->_identifierMap = $this->_identifierMap =
$this->_initializedCollections = $this->_initializedCollections =
$this->_existingCollections = $this->_existingCollections =
@ -131,13 +134,13 @@ class ObjectHydrator extends AbstractHydrator
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
protected function _hydrateAll() protected function hydrateAllData()
{ {
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result); $this->hydrateRowData($row, $cache, $result);
} }
// Take snapshots from all newly initialized collections // Take snapshots from all newly initialized collections
@ -278,13 +281,13 @@ class ObjectHydrator extends AbstractHydrator
* @param array $cache The cache to use. * @param array $cache The cache to use.
* @param array $result The result array to fill. * @param array $result The result array to fill.
*/ */
protected function _hydrateRow(array $row, array &$cache, array &$result) protected function hydrateRowData(array $row, array &$cache, array &$result)
{ {
// Initialize // Initialize
$id = $this->_idTemplate; // initialize the id-memory $id = $this->_idTemplate; // initialize the id-memory
$nonemptyComponents = array(); $nonemptyComponents = array();
// Split the row data into chunks of class data. // Split the row data into chunks of class data.
$rowData = $this->_gatherRowData($row, $cache, $id, $nonemptyComponents); $rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
// Extract scalar values. They're appended at the end. // Extract scalar values. They're appended at the end.
if (isset($rowData['scalars'])) { if (isset($rowData['scalars'])) {

View File

@ -26,25 +26,32 @@ use Doctrine\DBAL\Connection;
* The created result is almost the same as a regular SQL result set, except * The created result is almost the same as a regular SQL result set, except
* that column names are mapped to field names and data type conversions take place. * that column names are mapped to field names and data type conversions take place.
* *
* @since 2.0
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/ */
class ScalarHydrator extends AbstractHydrator class ScalarHydrator extends AbstractHydrator
{ {
/** @override */ /**
protected function _hydrateAll() * {@inheritdoc}
*/
protected function hydrateAllData()
{ {
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) { while ($data = $this->_stmt->fetch(\PDO::FETCH_ASSOC)) {
$result[] = $this->_gatherScalarRowData($data, $cache); $this->hydrateRowData($data, $cache, $result);
} }
return $result; return $result;
} }
/** @override */ /**
protected function _hydrateRow(array $data, array &$cache, array &$result) * {@inheritdoc}
*/
protected function hydrateRowData(array $data, array &$cache, array &$result)
{ {
$result[] = $this->_gatherScalarRowData($data, $cache); $result[] = $this->gatherScalarRowData($data, $cache);
} }
} }

View File

@ -17,7 +17,6 @@
* <http://www.doctrine-project.org>. * <http://www.doctrine-project.org>.
*/ */
namespace Doctrine\ORM\Internal\Hydration; namespace Doctrine\ORM\Internal\Hydration;
use \PDO; use \PDO;
@ -32,15 +31,21 @@ class SimpleObjectHydrator extends AbstractHydrator
*/ */
private $class; private $class;
/**
* @var array
*/
private $declaringClasses = array(); private $declaringClasses = array();
protected function _hydrateAll() /**
* {@inheritdoc}
*/
protected function hydrateAllData()
{ {
$result = array(); $result = array();
$cache = array(); $cache = array();
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) { while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
$this->_hydrateRow($row, $cache, $result); $this->hydrateRowData($row, $cache, $result);
} }
$this->_em->getUnitOfWork()->triggerEagerLoads(); $this->_em->getUnitOfWork()->triggerEagerLoads();
@ -48,82 +53,71 @@ class SimpleObjectHydrator extends AbstractHydrator
return $result; return $result;
} }
protected function _prepare() /**
* {@inheritdoc}
*/
protected function prepare()
{ {
if (count($this->_rsm->aliasMap) == 1) { if (count($this->_rsm->aliasMap) !== 1) {
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap)); throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.");
if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
}
} else {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping not containing exactly one object result.");
} }
if ($this->_rsm->scalarMappings) { if ($this->_rsm->scalarMappings) {
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings."); throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
} }
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
// We only need to add declaring classes if we have inheritance.
if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
return;
}
foreach ($this->_rsm->declaringClasses AS $column => $class) {
$this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
}
} }
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result) /**
* {@inheritdoc}
*/
protected function hydrateRowData(array $sqlResult, array &$cache, array &$result)
{ {
$data = array(); $entityName = $this->class->name;
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) { $data = array();
foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) { // We need to find the correct entity class name if we have inheritance in resultset
if (isset($this->_rsm->fieldMappings[$column])) { if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
$cache[$column]['name'] = $this->_rsm->fieldMappings[$column];
$cache[$column]['field'] = true;
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
}
if (isset($cache[$column]['field'])) {
$value = Type::getType($this->class->fieldMappings[$cache[$column]['name']]['type'])
->convertToPHPValue($value, $this->_platform);
}
$data[$cache[$column]['name']] = $value;
}
$entityName = $this->class->name;
} else {
$discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']); $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
if ($sqlResult[$discrColumnName] === "") {
if ($sqlResult[$discrColumnName] === '') {
throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap)); throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
} }
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]]; $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
unset($sqlResult[$discrColumnName]); unset($sqlResult[$discrColumnName]);
}
foreach ($sqlResult as $column => $value) { foreach ($sqlResult as $column => $value) {
if (!isset($cache[$column])) { // Hydrate column information if not yet present
if (isset($this->_rsm->fieldMappings[$column])) { if ( ! isset($cache[$column])) {
$field = $this->_rsm->fieldMappings[$column]; if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
$class = $this->declaringClasses[$column]; continue;
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
$cache[$column]['name'] = $field;
$cache[$column]['class'] = $class;
}
} else if (isset($this->_rsm->relationMap[$column])) {
if ($this->_rsm->relationMap[$column] == $entityName || is_subclass_of($entityName, $this->_rsm->relationMap[$column])) {
$cache[$column]['name'] = $field;
}
} else {
$cache[$column]['name'] = $this->_rsm->metaMappings[$column];
}
} }
if (isset($cache[$column]['class'])) { $cache[$column] = $info;
$value = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']) }
->convertToPHPValue($value, $this->_platform);
}
// the second and part is to prevent overwrites in case of multiple // Convert field to a valid PHP value
// inheritance classes using the same property name (See AbstractHydrator) if (isset($cache[$column]['field'])) {
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) { $type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
$data[$cache[$column]['name']] = $value; $value = $type->convertToPHPValue($value, $this->_platform);
} }
// 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;
} }
} }
@ -133,4 +127,52 @@ class SimpleObjectHydrator extends AbstractHydrator
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints); $result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
} }
/**
* Retrieve column information form ResultSetMapping.
*
* @param string $entityName
* @param string $column
*
* @return array
*/
protected function hydrateColumnInfo($entityName, $column)
{
switch (true) {
case (isset($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(
'class' => $class,
'name' => $this->_rsm->fieldMappings[$column],
'field' => true,
);
case (isset($this->_rsm->relationMap[$column])):
$class = isset($this->_rsm->relationMap[$column])
? $this->_rsm->relationMap[$column]
: $this->class;
// If class is not self referencing, ignore
if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
return null;
}
// TODO: Decide what to do with associations. It seems original code is incomplete.
// One solution is to load the association, but it might require extra efforts.
return array('name' => $column);
default:
return array(
'name' => $this->_rsm->metaMappings[$column]
);
}
}
} }

View File

@ -19,30 +19,37 @@
namespace Doctrine\ORM\Internal\Hydration; namespace Doctrine\ORM\Internal\Hydration;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection,
Doctrine\ORM\NoResultException,
Doctrine\ORM\NonUniqueResultException;
/** /**
* Hydrator that hydrates a single scalar value from the result set. * Hydrator that hydrates a single scalar value from the result set.
* *
* @since 2.0
* @author Roman Borschel <roman@code-factory.org> * @author Roman Borschel <roman@code-factory.org>
* @since 2.0 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/ */
class SingleScalarHydrator extends AbstractHydrator class SingleScalarHydrator extends AbstractHydrator
{ {
/** @override */ /**
protected function _hydrateAll() * {@inheritdoc}
*/
protected function hydrateAllData()
{ {
$cache = array(); $data = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC);
$result = $this->_stmt->fetchAll(\PDO::FETCH_ASSOC); $numRows = count($data);
$num = count($result);
if ($num == 0) { if ($numRows === 0) {
throw new \Doctrine\ORM\NoResultException; throw new NoResultException();
} else if ($num > 1 || count($result[key($result)]) > 1) {
throw new \Doctrine\ORM\NonUniqueResultException;
} }
$result = $this->_gatherScalarRowData($result[key($result)], $cache); if ($numRows > 1 || count($data[key($data)]) > 1) {
throw new NonUniqueResultException();
}
$cache = array();
$result = $this->gatherScalarRowData($data[key($data)], $cache);
return array_shift($result); return array_shift($result);
} }