1
0
Fork 0
mirror of synced 2025-04-01 12:26:11 +03:00

[DDC-952] [DDC-1050] Use ObjectHydrator inside Persisters, removing a bunch of duplicate code (step1, more necessary)

This commit is contained in:
Benjamin Eberlei 2011-03-06 21:26:54 +01:00
parent d9c8a9eecb
commit 851f44a066
9 changed files with 84 additions and 106 deletions

View file

@ -211,7 +211,16 @@ abstract class AbstractHydrator
}
if (isset($cache[$key]['isMetaColumn'])) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
if (!isset($rowData[$dqlAlias][$cache[$key]['fieldName']]) || $value !== null) {
$rowData[$dqlAlias][$cache[$key]['fieldName']] = $value;
}
continue;
}
// in an inheritance hierachy 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.
// 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) {
continue;
}

View file

@ -59,7 +59,9 @@ class ObjectHydrator extends AbstractHydrator
$this->_resultPointers =
$this->_idTemplate = array();
$this->_resultCounter = 0;
$this->_hints['deferEagerLoad'] = true;
if (!isset($this->_hints['deferEagerLoad'])) {
$this->_hints['deferEagerLoad'] = true;
}
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
$this->_identifierMap[$dqlAlias] = array();
@ -109,11 +111,17 @@ class ObjectHydrator extends AbstractHydrator
*/
protected function _cleanup()
{
$eagerLoad = (isset($this->_hints['deferEagerLoad'])) && $this->_hints['deferEagerLoad'] == true;
parent::_cleanup();
$this->_identifierMap =
$this->_initializedCollections =
$this->_existingCollections =
$this->_resultPointers = array();
if ($eagerLoad) {
$this->_em->getUnitOfWork()->triggerEagerLoads();
}
}
/**
@ -133,8 +141,6 @@ class ObjectHydrator extends AbstractHydrator
$coll->takeSnapshot();
}
$this->_em->getUnitOfWork()->triggerEagerLoads();
return $result;
}

View file

@ -112,6 +112,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$this->_resultColumnNames[$columnAlias] = $columnName;
$this->declaringClassMap[$columnAlias] = $class;
}
$this->_rsm->addFieldResult('r', $columnAlias, $field, $class->name);
return "$sql AS $columnAlias";
}
@ -124,6 +125,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
$this->_resultColumnNames[$resultColumnName] = $joinColumnName;
$this->declaringJoinColumnMap[$resultColumnName] = $className;
}
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
return $tableAlias . ".$joinColumnName AS $columnAlias";
}

View file

@ -117,6 +117,15 @@ class BasicEntityPersister
* @var array
*/
protected $_resultColumnNames = array();
/**
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
*
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
*
* @var Query\ResultSetMapping
*/
protected $_rsm;
/**
* The map of column names to DBAL mapping types of all prepared columns used
@ -558,13 +567,14 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, $lockMode);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$hints['deferEagerLoad'] = true;
$entity = $this->_createEntity($result, $entity, $hints);
$this->_em->getUnitOfWork()->triggerEagerLoads();
return $entity;
if ($entity !== null) {
$hints[Query::HINT_REFRESH] = true;
}
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
return $entities ? $entities[0] : null;
}
/**
@ -649,79 +659,9 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($id);
list($params, $types) = $this->expandParameters($id);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$metaColumns = array();
$newData = array();
// Refresh simple state
foreach ($result as $column => $value) {
$column = $this->_resultColumnNames[$column];
if (isset($this->_class->fieldNames[$column])) {
$fieldName = $this->_class->fieldNames[$column];
$newValue = $this->_conn->convertToPHPValue($value, $this->_class->fieldMappings[$fieldName]['type']);
$this->_class->reflFields[$fieldName]->setValue($entity, $newValue);
$newData[$fieldName] = $newValue;
} else {
$metaColumns[$column] = $value;
}
}
// Refresh associations
foreach ($this->_class->associationMappings as $field => $assoc) {
$value = $this->_class->reflFields[$field]->getValue($entity);
if ($assoc['type'] & ClassMetadata::TO_ONE) {
if ($value instanceof Proxy && ! $value->__isInitialized__) {
continue; // skip uninitialized proxies
}
if ($assoc['isOwningSide']) {
$joinColumnValues = array();
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
if ($metaColumns[$srcColumn] !== null) {
$joinColumnValues[$targetColumn] = $metaColumns[$srcColumn];
}
}
if ( ! $joinColumnValues && $value !== null) {
$this->_class->reflFields[$field]->setValue($entity, null);
$newData[$field] = null;
} else if ($value !== null) {
// Check identity map first, if the entity is not there,
// place a proxy in there instead.
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
if ($found = $this->_em->getUnitOfWork()->tryGetById($joinColumnValues, $targetClass->rootEntityName)) {
$this->_class->reflFields[$field]->setValue($entity, $found);
// Complete inverse side, if necessary.
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($found, $entity);
}
$newData[$field] = $found;
} else {
// FIXME: What is happening with subClassees here?
$proxy = $this->_em->getProxyFactory()->getProxy($assoc['targetEntity'], $joinColumnValues);
$this->_class->reflFields[$field]->setValue($entity, $proxy);
$newData[$field] = $proxy;
$this->_em->getUnitOfWork()->registerManaged($proxy, $joinColumnValues, array());
}
}
} else {
// Inverse side of 1-1/1-x can never be lazy.
//$newData[$field] = $assoc->load($entity, null, $this->_em);
$newData[$field] = $this->_em->getUnitOfWork()->getEntityPersister($assoc['targetEntity'])
->loadOneToOneEntity($assoc, $entity, null);
}
} else if ($value instanceof PersistentCollection && $value->isInitialized()) {
$value->setInitialized(false);
// no matter if dirty or non-dirty entities are already loaded, smoke them out!
// the beauty of it being, they are still in the identity map
$value->unwrap()->clear();
$newData[$field] = $value;
}
}
$this->_em->getUnitOfWork()->setOriginalEntityData($entity, $newData);
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
$hydrator->hydrateAll($stmt, $this->_rsm, array(Query::HINT_REFRESH => true));
if (isset($this->_class->lifecycleCallbacks[Events::postLoad])) {
$this->_class->invokeLifecycleCallbacks(Events::postLoad, $entity);
@ -744,17 +684,9 @@ class BasicEntityPersister
$sql = $this->_getSelectEntitiesSQL($criteria);
list($params, $types) = $this->expandParameters($criteria);
$stmt = $this->_conn->executeQuery($sql, $params, $types);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
$hints = array('deferEagerLoads' => true);
foreach ($result as $row) {
$entities[] = $this->_createEntity($row, null, $hints);
}
$this->_em->getUnitOfWork()->triggerEagerLoads();
return $entities;
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
}
/**
@ -1036,6 +968,8 @@ class BasicEntityPersister
}
$columnList = '';
$this->_rsm = new Query\ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r'); // r for root
// Add regular columns to select list
foreach ($this->_class->fieldNames as $field) {
@ -1054,6 +988,7 @@ class BasicEntityPersister
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
}
$this->_rsm->addMetaResult('r', $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
}
}
}
@ -1174,6 +1109,7 @@ class BasicEntityPersister
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
$this->_resultColumnNames[$columnAlias] = $columnName;
}
$this->_rsm->addFieldResult('r', $columnAlias, $field);
return "$sql AS $columnAlias";
}

View file

@ -21,7 +21,8 @@ namespace Doctrine\ORM\Persisters;
use Doctrine\ORM\ORMException,
Doctrine\ORM\Mapping\ClassMetadata,
Doctrine\DBAL\LockMode;
Doctrine\DBAL\LockMode,
Doctrine\ORM\Query\ResultSetMapping;
/**
* The joined subclass persister maps a single entity instance to several tables in the
@ -243,6 +244,10 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
// Create the column list fragment only once
if ($this->_selectColumnListSql === null) {
$this->_rsm = new ResultSetMapping();
$this->_rsm->addEntityResult($this->_class->name, 'r');
// Add regular columns
$columnList = '';
foreach ($this->_class->fieldMappings as $fieldName => $mapping) {
@ -279,6 +284,8 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
$this->_rsm->setDiscriminatorColumn('r', $discrColumn);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
}
// INNER JOIN parent tables

View file

@ -49,6 +49,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
$tableAlias = $this->_getSQLTableAlias($rootClass->name);
$resultColumnName = $this->_platform->getSQLResultCasing($discrColumn);
$this->_resultColumnNames[$resultColumnName] = $discrColumn;
$this->_rsm->setDiscriminatorColumn('r', $discrColumn);
$this->_rsm->addMetaResult('r', $resultColumnName, $discrColumn);
// Append subclass columns
foreach ($this->_class->subClasses as $subClassName) {

View file

@ -1966,6 +1966,11 @@ class UnitOfWork implements PropertyChangedListener
}
$this->originalEntityData[$oid][$field] = $newValue;
$class->reflFields[$field]->setValue($entity, $newValue);
if ($assoc['inversedBy'] && $assoc['type'] & ClassMetadata::ONE_TO_ONE) {
$inverseAssoc = $targetClass->associationMappings[$assoc['inversedBy']];
$targetClass->reflFields[$inverseAssoc['fieldName']]->setValue($newValue, $entity);
}
}
} else {
// Inverse side of x-to-one can never be lazy
@ -1973,18 +1978,27 @@ class UnitOfWork implements PropertyChangedListener
->loadOneToOneEntity($assoc, $entity, null));
}
} else {
// Inject collection
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
$pColl->setOwner($entity, $assoc);
$reflField = $class->reflFields[$field];
$reflField->setValue($entity, $pColl);
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
} else {
if (isset($hints[Query::HINT_REFRESH])) {
$pColl = $this->_class->reflFields[$field]->getValue($entity);
$pColl->setInitialized(false);
// no matter if dirty or non-dirty entities are already loaded, smoke them out!
// the beauty of it being, they are still in the identity map
$pColl->unwrap()->clear();
} else {
// Inject collection
$pColl = new PersistentCollection($this->em, $targetClass, new ArrayCollection);
$pColl->setOwner($entity, $assoc);
$reflField = $class->reflFields[$field];
$reflField->setValue($entity, $pColl);
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
$this->loadCollection($pColl);
$pColl->takeSnapshot();
} else {
$pColl->setInitialized(false);
}
}
$this->originalEntityData[$oid][$field] = $pColl;
}

View file

@ -42,7 +42,7 @@ class ReferenceProxyTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
$id = $this->createProduct();
$productProxy = $this->_factory->getProxy('Doctrine\Tests\Models\ECommerce\ECommerceProduct', array('id' => $id));
$productProxy = $this->_em->getReference('Doctrine\Tests\Models\ECommerce\ECommerceProduct', array('id' => $id));
$this->assertEquals('Doctrine Cookbook', $productProxy->getName());
}

View file

@ -33,12 +33,14 @@ class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->persist($customer);
$this->_em->flush();
$this->_em->clear();
$cardId = $cart->getId();
unset($cart);
$class = $this->_em->getClassMetadata('Doctrine\Tests\Models\ECommerce\ECommerceCart');
$persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceCart');
$newCart = new ECommerceCart();
$this->_em->getUnitOfWork()->registerManaged($newCart, array('id' => $cardId), array());
$persister->load(array('customer_id' => $customer->getId()), $newCart, $class->associationMappings['customer']);
$this->assertEquals('Credit card', $newCart->getPayment());
}