[DDC-952] [DDC-1050] Use ObjectHydrator inside Persisters, removing a bunch of duplicate code (step1, more necessary)
This commit is contained in:
parent
d9c8a9eecb
commit
851f44a066
9 changed files with 84 additions and 106 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue