Merge branch 'master' of github.com:doctrine/doctrine2
This commit is contained in:
commit
8c8a658dfd
@ -57,6 +57,11 @@ abstract class AbstractQuery
|
||||
*/
|
||||
const HYDRATE_SINGLE_SCALAR = 4;
|
||||
|
||||
/**
|
||||
* Very simple object hydrator (optimized for performance).
|
||||
*/
|
||||
const HYDRATE_SIMPLEOBJECT = 5;
|
||||
|
||||
/**
|
||||
* @var array The parameter map of this query.
|
||||
*/
|
||||
@ -331,6 +336,26 @@ abstract class AbstractQuery
|
||||
return $this->_expireResultCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the default fetch mode of an association for this query.
|
||||
*
|
||||
* $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
|
||||
*
|
||||
* @param string $class
|
||||
* @param string $assocName
|
||||
* @param int $fetchMode
|
||||
* @return AbstractQuery
|
||||
*/
|
||||
public function setFetchMode($class, $assocName, $fetchMode)
|
||||
{
|
||||
if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
|
||||
$fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
|
||||
}
|
||||
|
||||
$this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the processing mode to be used during hydration / result set transformation.
|
||||
*
|
||||
|
@ -679,6 +679,9 @@ class EntityManager implements ObjectManager
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$hydrator = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_SIMPLEOBJECT:
|
||||
$hydrator = new Internal\Hydration\SimpleObjectHydrator($this);
|
||||
break;
|
||||
default:
|
||||
if ($class = $this->config->getCustomHydrationMode($hydrationMode)) {
|
||||
$hydrator = new $class($this);
|
||||
|
@ -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,6 +59,9 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
$this->_resultCounter = 0;
|
||||
if (!isset($this->_hints['deferEagerLoad'])) {
|
||||
$this->_hints['deferEagerLoad'] = true;
|
||||
}
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
@ -108,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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -395,6 +404,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[$key] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $key;
|
||||
}
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateSet($key, $element);
|
||||
}
|
||||
} else {
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array(0 => $element);
|
||||
@ -402,6 +415,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[] = $element;
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateAdd($element);
|
||||
}
|
||||
}
|
||||
|
||||
// Update result pointer
|
||||
|
141
lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php
Normal file
141
lib/Doctrine/ORM/Internal/Hydration/SimpleObjectHydrator.php
Normal file
@ -0,0 +1,141 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use \PDO;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
class SimpleObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
const REFRESH_ENTITY = 'doctrine_refresh_entity';
|
||||
|
||||
/**
|
||||
* @var ClassMetadata
|
||||
*/
|
||||
private $class;
|
||||
|
||||
private $declaringClasses = array();
|
||||
|
||||
protected function _hydrateAll()
|
||||
{
|
||||
$result = array();
|
||||
$cache = array();
|
||||
|
||||
while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$this->_hydrateRow($row, $cache, $result);
|
||||
}
|
||||
|
||||
$this->_em->getUnitOfWork()->triggerEagerLoads();
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function _prepare()
|
||||
{
|
||||
if (count($this->_rsm->aliasMap) == 1) {
|
||||
$this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
|
||||
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) {
|
||||
throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
|
||||
}
|
||||
}
|
||||
|
||||
protected function _hydrateRow(array $sqlResult, array &$cache, array &$result)
|
||||
{
|
||||
$data = array();
|
||||
if ($this->class->inheritanceType == ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
|
||||
if (!isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$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']);
|
||||
$entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
|
||||
unset($sqlResult[$discrColumnName]);
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
if (!isset($cache[$column])) {
|
||||
if (isset($this->_rsm->fieldMappings[$column])) {
|
||||
$field = $this->_rsm->fieldMappings[$column];
|
||||
$class = $this->declaringClasses[$column];
|
||||
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'])) {
|
||||
$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
|
||||
// inheritance classes using the same property name (See AbstractHydrator)
|
||||
if (isset($cache[$column]) && (!isset($data[$cache[$column]['name']]) || $value !== null)) {
|
||||
$data[$cache[$column]['name']] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($this->_hints[self::REFRESH_ENTITY])) {
|
||||
$this->_hints[Query::HINT_REFRESH] = true;
|
||||
$id = array();
|
||||
if ($this->_class->isIdentifierComposite) {
|
||||
foreach ($this->_class->identifier as $fieldName) {
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
} else {
|
||||
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
|
||||
}
|
||||
$this->_em->getUnitOfWork()->registerManaged($this->_hints[self::REFRESH_ENTITY], $id, $data);
|
||||
}
|
||||
|
||||
$result[] = $this->_em->getUnitOfWork()->createEntity($entityName, $data, $this->_hints);
|
||||
}
|
||||
}
|
@ -28,24 +28,11 @@ use Doctrine\ORM\Mapping\ClassMetadata,
|
||||
* types in the hierarchy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
{
|
||||
/**
|
||||
* Map from column names to class metadata instances that declare the field the column is mapped to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $declaringClassMap = array();
|
||||
|
||||
/**
|
||||
* Map from column names to class names that declare the field the association with join column is mapped to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $declaringJoinColumnMap = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -69,49 +56,12 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _processSQLResult(array $sqlResult)
|
||||
{
|
||||
$data = array();
|
||||
$discrColumnName = $this->_platform->getSQLResultCasing($this->_class->discriminatorColumn['name']);
|
||||
$entityName = $this->_class->discriminatorMap[$sqlResult[$discrColumnName]];
|
||||
unset($sqlResult[$discrColumnName]);
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
$realColumnName = $this->_resultColumnNames[$column];
|
||||
if (isset($this->declaringClassMap[$column])) {
|
||||
$class = $this->declaringClassMap[$column];
|
||||
if ($class->name == $entityName || is_subclass_of($entityName, $class->name)) {
|
||||
$field = $class->fieldNames[$realColumnName];
|
||||
if (isset($data[$field])) {
|
||||
$data[$realColumnName] = $value;
|
||||
} else {
|
||||
$data[$field] = Type::getType($class->fieldMappings[$field]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
}
|
||||
} else if (isset($this->declaringJoinColumnMap[$column])) {
|
||||
if ($this->declaringJoinColumnMap[$column] == $entityName || is_subclass_of($entityName, $this->declaringJoinColumnMap[$column])) {
|
||||
$data[$realColumnName] = $value;
|
||||
}
|
||||
} else {
|
||||
$data[$realColumnName] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array($entityName, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class)
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
|
||||
$this->_resultColumnNames[$columnAlias] = $columnName;
|
||||
$this->declaringClassMap[$columnAlias] = $class;
|
||||
}
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field, $class->name);
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
}
|
||||
@ -120,10 +70,7 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
{
|
||||
$columnAlias = $joinColumnName . $this->_sqlAliasCounter++;
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
|
||||
$this->_resultColumnNames[$resultColumnName] = $joinColumnName;
|
||||
$this->declaringJoinColumnMap[$resultColumnName] = $className;
|
||||
}
|
||||
$this->_rsm->addMetaResult('r', $resultColumnName, $joinColumnName);
|
||||
|
||||
return $tableAlias . ".$joinColumnName AS $columnAlias";
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Doctrine\ORM\Persisters;
|
||||
use PDO,
|
||||
Doctrine\DBAL\LockMode,
|
||||
Doctrine\DBAL\Types\Type,
|
||||
Doctrine\DBAL\Connection,
|
||||
Doctrine\ORM\ORMException,
|
||||
Doctrine\ORM\OptimisticLockException,
|
||||
Doctrine\ORM\EntityManager,
|
||||
@ -69,6 +70,7 @@ use PDO,
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Giorgio Sironi <piccoloprincipeazzurro@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
*/
|
||||
class BasicEntityPersister
|
||||
@ -107,15 +109,15 @@ class BasicEntityPersister
|
||||
* @var array
|
||||
*/
|
||||
protected $_queuedInserts = array();
|
||||
|
||||
|
||||
/**
|
||||
* Case-sensitive mappings of column names as they appear in an SQL result set
|
||||
* to column names as they are defined in the mapping. This is necessary because different
|
||||
* RDBMS vendors return column names in result sets in different casings.
|
||||
* ResultSetMapping that is used for all queries. Is generated lazily once per request.
|
||||
*
|
||||
* @var array
|
||||
* TODO: Evaluate Caching in combination with the other cached SQL snippets.
|
||||
*
|
||||
* @var Query\ResultSetMapping
|
||||
*/
|
||||
protected $_resultColumnNames = array();
|
||||
protected $_rsm;
|
||||
|
||||
/**
|
||||
* The map of column names to DBAL mapping types of all prepared columns used
|
||||
@ -142,6 +144,14 @@ class BasicEntityPersister
|
||||
* @var string
|
||||
*/
|
||||
protected $_selectColumnListSql;
|
||||
|
||||
/**
|
||||
* The JOIN SQL fragement used to eagerly load all many-to-one and one-to-one
|
||||
* associations configured as FETCH_EAGER, aswell as all inverse one-to-one associations.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_selectJoinSql;
|
||||
|
||||
/**
|
||||
* Counter for creating unique SQL table and column aliases.
|
||||
@ -557,10 +567,18 @@ 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();
|
||||
|
||||
if ($entity !== null) {
|
||||
$hints[Query::HINT_REFRESH] = true;
|
||||
}
|
||||
|
||||
return $this->_createEntity($result, $entity, $hints);
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
$entities = $hydrator->hydrateAll($stmt, $this->_rsm, $hints);
|
||||
return $entities ? $entities[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -577,6 +595,10 @@ class BasicEntityPersister
|
||||
*/
|
||||
public function loadOneToOneEntity(array $assoc, $sourceEntity, $targetEntity, array $identifier = array())
|
||||
{
|
||||
if ($foundEntity = $this->_em->getUnitOfWork()->tryGetById($identifier, $assoc['targetEntity'])) {
|
||||
return $foundEntity;
|
||||
}
|
||||
|
||||
$targetClass = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
if ($assoc['isOwningSide']) {
|
||||
@ -641,79 +663,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);
|
||||
@ -736,14 +688,13 @@ 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();
|
||||
|
||||
foreach ($result as $row) {
|
||||
$entities[] = $this->_createEntity($row);
|
||||
if ($this->_selectJoinSql) {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
} else {
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_SIMPLEOBJECT);
|
||||
}
|
||||
|
||||
return $entities;
|
||||
return $hydrator->hydrateAll($stmt, $this->_rsm, array('deferEagerLoads' => true));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -770,19 +721,17 @@ class BasicEntityPersister
|
||||
*/
|
||||
private function loadArrayFromStatement($assoc, $stmt)
|
||||
{
|
||||
$entities = array();
|
||||
$hints = array('deferEagerLoads' => true);
|
||||
|
||||
if (isset($assoc['indexBy'])) {
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$entity = $this->_createEntity($result);
|
||||
$entities[$this->_class->reflFields[$assoc['indexBy']]->getValue($entity)] = $entity;
|
||||
}
|
||||
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
|
||||
$rsm->addIndexBy('r', $assoc['indexBy']);
|
||||
} else {
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$entities[] = $this->_createEntity($result);
|
||||
}
|
||||
$rsm = $this->_rsm;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
return $entities;
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
return $hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -793,18 +742,18 @@ class BasicEntityPersister
|
||||
* @param PersistentCollection $coll
|
||||
*/
|
||||
private function loadCollectionFromStatement($assoc, $stmt, $coll)
|
||||
{
|
||||
{
|
||||
$hints = array('deferEagerLoads' => true, 'collection' => $coll);
|
||||
|
||||
if (isset($assoc['indexBy'])) {
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$entity = $this->_createEntity($result);
|
||||
$coll->hydrateSet($this->_class->reflFields[$assoc['indexBy']]->getValue($entity), $entity);
|
||||
}
|
||||
$rsm = clone ($this->_rsm); // this is necessary because the "default rsm" should be changed.
|
||||
$rsm->addIndexBy('r', $assoc['indexBy']);
|
||||
} else {
|
||||
while ($result = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
$coll->hydrateAdd($this->_createEntity($result));
|
||||
}
|
||||
$rsm = $this->_rsm;
|
||||
}
|
||||
$stmt->closeCursor();
|
||||
|
||||
$hydrator = $this->_em->newHydrator(Query::HYDRATE_OBJECT);
|
||||
$hydrator->hydrateAll($stmt, $rsm, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -827,7 +776,6 @@ class BasicEntityPersister
|
||||
{
|
||||
$criteria = array();
|
||||
$sourceClass = $this->_em->getClassMetadata($assoc['sourceEntity']);
|
||||
$joinTableConditions = array();
|
||||
if ($assoc['isOwningSide']) {
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationKeyColumn => $sourceKeyColumn) {
|
||||
if ($sourceClass->containsForeignIdentifier) {
|
||||
@ -873,72 +821,6 @@ class BasicEntityPersister
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or fills a single entity object from an SQL result.
|
||||
*
|
||||
* @param $result The SQL result.
|
||||
* @param object $entity The entity object to fill, if any.
|
||||
* @param array $hints Hints for entity creation.
|
||||
* @return object The filled and managed entity object or NULL, if the SQL result is empty.
|
||||
*/
|
||||
private function _createEntity($result, $entity = null, array $hints = array())
|
||||
{
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
list($entityName, $data) = $this->_processSQLResult($result);
|
||||
|
||||
if ($entity !== null) {
|
||||
$hints[Query::HINT_REFRESH] = true;
|
||||
$id = array();
|
||||
if ($this->_class->isIdentifierComposite) {
|
||||
foreach ($this->_class->identifier as $fieldName) {
|
||||
$id[$fieldName] = $data[$fieldName];
|
||||
}
|
||||
} else {
|
||||
$id = array($this->_class->identifier[0] => $data[$this->_class->identifier[0]]);
|
||||
}
|
||||
$this->_em->getUnitOfWork()->registerManaged($entity, $id, $data);
|
||||
}
|
||||
|
||||
return $this->_em->getUnitOfWork()->createEntity($entityName, $data, $hints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes an SQL result set row that contains data for an entity of the type
|
||||
* this persister is responsible for.
|
||||
*
|
||||
* Subclasses are supposed to override this method if they need to change the
|
||||
* hydration procedure for entities loaded through basic find operations or
|
||||
* lazy-loading (not DQL).
|
||||
*
|
||||
* @param array $sqlResult The SQL result set row to process.
|
||||
* @return array A tuple where the first value is the actual type of the entity and
|
||||
* the second value the prepared data of the entity (a map from field
|
||||
* names to values).
|
||||
*/
|
||||
protected function _processSQLResult(array $sqlResult)
|
||||
{
|
||||
$data = array();
|
||||
foreach ($sqlResult as $column => $value) {
|
||||
$column = $this->_resultColumnNames[$column];
|
||||
if (isset($this->_class->fieldNames[$column])) {
|
||||
$field = $this->_class->fieldNames[$column];
|
||||
if (isset($data[$field])) {
|
||||
$data[$column] = $value;
|
||||
} else {
|
||||
$data[$field] = Type::getType($this->_class->fieldMappings[$field]['type'])
|
||||
->convertToPHPValue($value, $this->_platform);
|
||||
}
|
||||
} else {
|
||||
$data[$column] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return array($this->_class->name, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SELECT SQL to select one or more entities by a set of field criteria.
|
||||
*
|
||||
@ -970,7 +852,7 @@ class BasicEntityPersister
|
||||
return $this->_platform->modifyLimitQuery('SELECT ' . $this->_getSelectColumnListSQL()
|
||||
. $this->_platform->appendLockHint(' FROM ' . $this->_class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->_getSQLTableAlias($this->_class->name), $lockMode)
|
||||
. $joinSql
|
||||
. $this->_selectJoinSql . $joinSql
|
||||
. ($conditionSql ? ' WHERE ' . $conditionSql : '')
|
||||
. $orderBySql, $limit, $offset)
|
||||
. $lockSql;
|
||||
@ -1023,6 +905,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) {
|
||||
@ -1030,16 +914,53 @@ class BasicEntityPersister
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $this->_class);
|
||||
}
|
||||
|
||||
foreach ($this->_class->associationMappings as $assoc) {
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$this->_selectJoinSql = '';
|
||||
$eagerAliasCounter = 0;
|
||||
foreach ($this->_class->associationMappings as $assocField => $assoc) {
|
||||
$assocColumnSQL = $this->_getSelectColumnAssociationSQL($assocField, $assoc, $this->_class);
|
||||
if ($assocColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $assocColumnSQL;
|
||||
}
|
||||
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE && ($assoc['fetch'] == ClassMetadata::FETCH_EAGER || !$assoc['isOwningSide'])) {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
if ($eagerEntity->inheritanceType != ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
continue; // now this is why you shouldn't use inheritance
|
||||
}
|
||||
|
||||
$assocAlias = 'e' . ($eagerAliasCounter++);
|
||||
$this->_rsm->addJoinedEntityResult($assoc['targetEntity'], $assocAlias, 'r', $assocField);
|
||||
|
||||
foreach ($eagerEntity->fieldNames AS $field) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $this->_getSelectColumnSQL($field, $eagerEntity, $assocAlias);
|
||||
}
|
||||
|
||||
foreach ($eagerEntity->associationMappings as $assoc2Field => $assoc2) {
|
||||
$assoc2ColumnSQL = $this->_getSelectColumnAssociationSQL($assoc2Field, $assoc2, $eagerEntity, $assocAlias);
|
||||
if ($assoc2ColumnSQL) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
$columnList .= $assoc2ColumnSQL;
|
||||
}
|
||||
}
|
||||
$this->_selectJoinSql .= ' LEFT JOIN'; // TODO: Inner join when all join columns are NOT nullable.
|
||||
if ($assoc['isOwningSide']) {
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($assoc['sourceEntity']) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($assoc['targetEntity'], $assocAlias) . '.'.$targetCol.' ';
|
||||
}
|
||||
} else {
|
||||
$eagerEntity = $this->_em->getClassMetadata($assoc['targetEntity']);
|
||||
$owningAssoc = $eagerEntity->getAssociationMapping($assoc['mappedBy']);
|
||||
|
||||
$this->_selectJoinSql .= ' ' . $eagerEntity->table['name'] . ' ' . $this->_getSQLTableAlias($eagerEntity->name, $assocAlias) .' ON ';
|
||||
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$columnList .= $this->_getSQLTableAlias($this->_class->name) . ".$srcColumn AS $columnAlias";
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
if ( ! isset($this->_resultColumnNames[$resultColumnName])) {
|
||||
$this->_resultColumnNames[$resultColumnName] = $srcColumn;
|
||||
foreach ($owningAssoc['sourceToTargetKeyColumns'] AS $sourceCol => $targetCol) {
|
||||
$this->_selectJoinSql .= $this->_getSQLTableAlias($owningAssoc['sourceEntity'], $assocAlias) . '.'.$sourceCol.' = ' .
|
||||
$this->_getSQLTableAlias($owningAssoc['targetEntity']) . '.' . $targetCol . ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1049,6 +970,22 @@ class BasicEntityPersister
|
||||
|
||||
return $this->_selectColumnListSql;
|
||||
}
|
||||
|
||||
protected function _getSelectColumnAssociationSQL($field, $assoc, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnList = '';
|
||||
if ($assoc['isOwningSide'] && $assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
if ($columnList) $columnList .= ', ';
|
||||
|
||||
$columnAlias = $srcColumn . $this->_sqlAliasCounter++;
|
||||
$columnList .= $this->_getSQLTableAlias($class->name, ($alias == 'r' ? '' : $alias) ) . ".$srcColumn AS $columnAlias";
|
||||
$resultColumnName = $this->_platform->getSQLResultCasing($columnAlias);
|
||||
$this->_rsm->addMetaResult($alias, $this->_platform->getSQLResultCasing($columnAlias), $srcColumn);
|
||||
}
|
||||
}
|
||||
return $columnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the SQL join fragment used when selecting entities from a
|
||||
@ -1152,15 +1089,14 @@ class BasicEntityPersister
|
||||
* @param string $field The field name.
|
||||
* @param ClassMetadata $class The class that declares this field. The table this class is
|
||||
* mapped to must own the column for the given field.
|
||||
* @param string $alias
|
||||
*/
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class)
|
||||
protected function _getSelectColumnSQL($field, ClassMetadata $class, $alias = 'r')
|
||||
{
|
||||
$columnName = $class->columnNames[$field];
|
||||
$sql = $this->_getSQLTableAlias($class->name) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$sql = $this->_getSQLTableAlias($class->name, $alias == 'r' ? '' : $alias) . '.' . $class->getQuotedColumnName($field, $this->_platform);
|
||||
$columnAlias = $this->_platform->getSQLResultCasing($columnName . $this->_sqlAliasCounter++);
|
||||
if ( ! isset($this->_resultColumnNames[$columnAlias])) {
|
||||
$this->_resultColumnNames[$columnAlias] = $columnName;
|
||||
}
|
||||
$this->_rsm->addFieldResult($alias, $columnAlias, $field);
|
||||
|
||||
return "$sql AS $columnAlias";
|
||||
}
|
||||
@ -1172,14 +1108,18 @@ class BasicEntityPersister
|
||||
* @return string The SQL table alias.
|
||||
* @todo Reconsider. Binding table aliases to class names is not such a good idea.
|
||||
*/
|
||||
protected function _getSQLTableAlias($className)
|
||||
protected function _getSQLTableAlias($className, $assocName = '')
|
||||
{
|
||||
if ($assocName) {
|
||||
$className .= '#'.$assocName;
|
||||
}
|
||||
|
||||
if (isset($this->_sqlTableAliases[$className])) {
|
||||
return $this->_sqlTableAliases[$className];
|
||||
}
|
||||
$tableAlias = 't' . $this->_sqlAliasCounter++;
|
||||
$this->_sqlTableAliases[$className] = $tableAlias;
|
||||
|
||||
$this->_sqlTableAliases[$className] = $tableAlias;
|
||||
return $tableAlias;
|
||||
}
|
||||
|
||||
@ -1265,7 +1205,7 @@ class BasicEntityPersister
|
||||
} else {
|
||||
throw ORMException::unrecognizedField($field);
|
||||
}
|
||||
$conditionSql .= ' = ?';
|
||||
$conditionSql .= (is_array($value)) ? ' IN (?)' : ' = ?';
|
||||
}
|
||||
return $conditionSql;
|
||||
}
|
||||
@ -1330,6 +1270,7 @@ class BasicEntityPersister
|
||||
|
||||
$sql = $this->_getSelectEntitiesSQL($criteria, $assoc, 0, $limit, $offset);
|
||||
list($params, $types) = $this->expandParameters($criteria);
|
||||
|
||||
return $this->_conn->executeQuery($sql, $params, $types);
|
||||
}
|
||||
|
||||
@ -1348,6 +1289,10 @@ class BasicEntityPersister
|
||||
if (isset($this->_class->fieldMappings[$field])) {
|
||||
$type = Type::getType($this->_class->fieldMappings[$field]['type'])->getBindingType();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$type += Connection::ARRAY_PARAM_OFFSET;
|
||||
}
|
||||
|
||||
$params[] = $value;
|
||||
$types[] = $type;
|
||||
}
|
||||
|
@ -21,13 +21,15 @@ 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
|
||||
* database as it is defined by the <tt>Class Table Inheritance</tt> strategy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
* @see http://martinfowler.com/eaaCatalog/classTableInheritance.html
|
||||
*/
|
||||
@ -243,6 +245,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) {
|
||||
@ -278,7 +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
|
||||
|
@ -26,6 +26,7 @@ use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
* SINGLE_TABLE strategy.
|
||||
*
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @since 2.0
|
||||
* @link http://martinfowler.com/eaaCatalog/singleTableInheritance.html
|
||||
*/
|
||||
@ -48,7 +49,8 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
$rootClass = $this->_em->getClassMetadata($this->_class->rootEntityName);
|
||||
$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) {
|
||||
@ -86,9 +88,9 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
protected function _getSQLTableAlias($className)
|
||||
protected function _getSQLTableAlias($className, $assocName = '')
|
||||
{
|
||||
return parent::_getSQLTableAlias($this->_class->rootEntityName);
|
||||
return parent::_getSQLTableAlias($this->_class->rootEntityName, $assocName);
|
||||
}
|
||||
|
||||
/** {@inheritdoc} */
|
||||
|
@ -218,6 +218,13 @@ class UnitOfWork implements PropertyChangedListener
|
||||
|
||||
//private $_readOnlyObjects = array();
|
||||
|
||||
/**
|
||||
* Map of Entity Class-Names and corresponding IDs that should eager loaded when requested.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $eagerLoadingEntities = array();
|
||||
|
||||
/**
|
||||
* Initializes a new UnitOfWork instance, bound to the given EntityManager.
|
||||
*
|
||||
@ -1886,6 +1893,9 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class->reflFields[$field]->setValue($entity, $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Loading the entity right here, if its in the eager loading map get rid of it there.
|
||||
unset($this->eagerLoadingEntities[$class->rootEntityName][$idHash]);
|
||||
|
||||
// Properly initialize any unfetched associations, if partial objects are not allowed.
|
||||
if ( ! isset($hints[Query::HINT_FORCE_PARTIAL_LOAD])) {
|
||||
@ -1900,6 +1910,7 @@ class UnitOfWork implements PropertyChangedListener
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
if ($assoc['isOwningSide']) {
|
||||
$associatedId = array();
|
||||
// TODO: Is this even computed right in all cases of composite keys?
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $targetColumn => $srcColumn) {
|
||||
$joinColumnValue = isset($data[$srcColumn]) ? $data[$srcColumn] : null;
|
||||
if ($joinColumnValue !== null) {
|
||||
@ -1915,6 +1926,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$class->reflFields[$field]->setValue($entity, null);
|
||||
$this->originalEntityData[$oid][$field] = null;
|
||||
} else {
|
||||
if (!isset($hints['fetchMode'][$class->name][$field])) {
|
||||
$hints['fetchMode'][$class->name][$field] = $assoc['fetch'];
|
||||
}
|
||||
|
||||
// Foreign key is set
|
||||
// Check identity map first
|
||||
// FIXME: Can break easily with composite keys if join column values are in
|
||||
@ -1922,16 +1937,38 @@ class UnitOfWork implements PropertyChangedListener
|
||||
$relatedIdHash = implode(' ', $associatedId);
|
||||
if (isset($this->identityMap[$targetClass->rootEntityName][$relatedIdHash])) {
|
||||
$newValue = $this->identityMap[$targetClass->rootEntityName][$relatedIdHash];
|
||||
|
||||
// if this is an uninitialized proxy, we are deferring eager loads,
|
||||
// this association is marked as eager fetch, and its an uninitialized proxy (wtf!)
|
||||
// then we cann append this entity for eager loading!
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER &&
|
||||
isset($hints['deferEagerLoad']) &&
|
||||
!$targetClass->isIdentifierComposite &&
|
||||
$newValue instanceof Proxy &&
|
||||
$newValue->__isInitialized__ === false) {
|
||||
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
}
|
||||
} else {
|
||||
if ($targetClass->subClasses) {
|
||||
// If it might be a subtype, it can not be lazy
|
||||
// If it might be a subtype, it can not be lazy. There isn't even
|
||||
// a way to solve this with deferred eager loading, which means putting
|
||||
// an entity with subclasses at a *-to-one location is really bad! (performance-wise)
|
||||
$newValue = $this->getEntityPersister($assoc['targetEntity'])
|
||||
->loadOneToOneEntity($assoc, $entity, null, $associatedId);
|
||||
} else {
|
||||
if ($assoc['fetch'] == ClassMetadata::FETCH_EAGER) {
|
||||
// TODO: Maybe it could be optimized to do an eager fetch with a JOIN inside
|
||||
// the persister instead of this rather unperformant approach.
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
// Deferred eager load only works for single identifier classes
|
||||
|
||||
if ($hints['fetchMode'][$class->name][$field] == ClassMetadata::FETCH_EAGER) {
|
||||
if (isset($hints['deferEagerLoad']) && !$targetClass->isIdentifierComposite) {
|
||||
// TODO: Is there a faster approach?
|
||||
$this->eagerLoadingEntities[$targetClass->rootEntityName][$relatedIdHash] = current($associatedId);
|
||||
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
} else {
|
||||
// TODO: This is very imperformant, ignore it?
|
||||
$newValue = $this->em->find($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
} else {
|
||||
$newValue = $this->em->getProxyFactory()->getProxy($assoc['targetEntity'], $associatedId);
|
||||
}
|
||||
@ -1945,6 +1982,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
|
||||
@ -1955,10 +1997,10 @@ class UnitOfWork implements PropertyChangedListener
|
||||
// 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();
|
||||
@ -1982,6 +2024,25 @@ class UnitOfWork implements PropertyChangedListener
|
||||
return $entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function triggerEagerLoads()
|
||||
{
|
||||
if (!$this->eagerLoadingEntities) {
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid infinite recursion
|
||||
$eagerLoadingEntities = $this->eagerLoadingEntities;
|
||||
$this->eagerLoadingEntities = array();
|
||||
|
||||
foreach ($eagerLoadingEntities AS $entityName => $ids) {
|
||||
$class = $this->em->getClassMetadata($entityName);
|
||||
$this->getEntityPersister($entityName)->loadAll(array_combine($class->identifier, array(array_values($ids))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes (loads) an uninitialized persistent collection of an entity.
|
||||
*
|
||||
|
2
lib/vendor/doctrine-dbal
vendored
2
lib/vendor/doctrine-dbal
vendored
@ -1 +1 @@
|
||||
Subproject commit 556351d9d6b4a33506f2c1535cccee34faa65d62
|
||||
Subproject commit 9cd6df38d841abb4719286ea35a1b37aa2679f8d
|
@ -35,6 +35,7 @@ class AllTests
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\AdvancedDqlQueryTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToOneUnidirectionalAssociationTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToOneBidirectionalAssociationTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToOneEagerLoadingTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManyBidirectionalAssociationTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\OneToManyUnidirectionalAssociationTest');
|
||||
$suite->addTestSuite('Doctrine\Tests\ORM\Functional\ManyToManyBasicAssociationTest');
|
||||
|
@ -946,4 +946,35 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$this->assertNull($this->_em->find(get_class($ph), $ph->phonenumber)->getUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-952
|
||||
*/
|
||||
public function testManyToOneFetchModeQuery()
|
||||
{
|
||||
$user = new CmsUser();
|
||||
$user->username = "beberlei";
|
||||
$user->name = "Benjamin E.";
|
||||
$user->status = 'active';
|
||||
|
||||
$article = new CmsArticle();
|
||||
$article->topic = "foo";
|
||||
$article->text = "bar";
|
||||
$article->user = $user;
|
||||
|
||||
$this->_em->persist($article);
|
||||
$this->_em->persist($user);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$qc = $this->getCurrentQueryCount();
|
||||
$dql = "SELECT a FROM Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.id = ?1";
|
||||
$article = $this->_em->createQuery($dql)
|
||||
->setParameter(1, $article->id)
|
||||
->setFetchMode('Doctrine\Tests\Models\CMS\CmsArticle', 'user', \Doctrine\ORM\Mapping\ClassMetadata::FETCH_EAGER)
|
||||
->getSingleResult();
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $article->user, "It IS a proxy, ...");
|
||||
$this->assertTrue($article->user->__isInitialized__, "...but its initialized!");
|
||||
$this->assertEquals($qc+2, $this->getCurrentQueryCount());
|
||||
}
|
||||
}
|
||||
|
@ -172,10 +172,10 @@ class EntityRepositoryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$userId = $user->id;
|
||||
|
||||
$this->_em->find('Doctrine\Tests\Models\Cms\CmsUser', $userId);
|
||||
$this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId);
|
||||
|
||||
$this->setExpectedException('Doctrine\ORM\OptimisticLockException');
|
||||
$this->_em->find('Doctrine\Tests\Models\Cms\CmsUser', $userId, \Doctrine\DBAL\LockMode::OPTIMISTIC);
|
||||
$this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $userId, \Doctrine\DBAL\LockMode::OPTIMISTIC);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,8 +196,8 @@ class ExtraLazyCollectionTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertEquals(2, count($someUsers));
|
||||
$this->assertEquals(2, count($otherUsers));
|
||||
|
||||
// +2 queries executed by slice, +4 are executed by EAGER fetching of User Address.
|
||||
$this->assertEquals($queryCount + 2 + 4, $this->getCurrentQueryCount());
|
||||
// +2 queries executed by slice
|
||||
$this->assertEquals($queryCount + 2, $this->getCurrentQueryCount(), "Slicing two parts should only execute two additional queries.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
198
tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php
Normal file
198
tests/Doctrine/Tests/ORM/Functional/OneToOneEagerLoadingTest.php
Normal file
@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\ORM\UnitOfWork;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-952
|
||||
*/
|
||||
class OneToOneEagerLoadingTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$schemaTool = new \Doctrine\ORM\Tools\SchemaTool($this->_em);
|
||||
try {
|
||||
$schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Train'),
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\TrainDriver'),
|
||||
$this->_em->getClassMetadata('Doctrine\Tests\ORM\Functional\Waggon'),
|
||||
));
|
||||
} catch(\Exception $e) {}
|
||||
}
|
||||
|
||||
public function testEagerLoadOneToOneOwningSide()
|
||||
{
|
||||
$train = new Train();
|
||||
$driver = new TrainDriver("Benjamin");
|
||||
$waggon = new Waggon();
|
||||
|
||||
$train->setDriver($driver);
|
||||
$train->addWaggon($waggon);
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
|
||||
$train = $this->_em->find(get_class($train), $train->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $train->driver);
|
||||
$this->assertEquals("Benjamin", $train->driver->name);
|
||||
|
||||
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
|
||||
}
|
||||
|
||||
public function testEagerLoadOneToOneNullOwningSide()
|
||||
{
|
||||
$train = new Train();
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
|
||||
$train = $this->_em->find(get_class($train), $train->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $train->driver);
|
||||
$this->assertNull($train->driver);
|
||||
|
||||
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
|
||||
}
|
||||
|
||||
public function testEagerLoadOneToOneInverseSide()
|
||||
{
|
||||
$train = new Train();
|
||||
$driver = new TrainDriver("Benjamin");
|
||||
$train->setDriver($driver);
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
|
||||
$driver = $this->_em->find(get_class($driver), $driver->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $driver->train);
|
||||
$this->assertNotNull($driver->train);
|
||||
|
||||
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
|
||||
}
|
||||
|
||||
public function testEagerLoadOneToOneNullInverseSide()
|
||||
{
|
||||
$driver = new TrainDriver("Dagny Taggert");
|
||||
|
||||
$this->_em->persist($driver);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->assertNull($driver->train);
|
||||
|
||||
$sqlCount = count($this->_sqlLoggerStack->queries);
|
||||
|
||||
$driver = $this->_em->find(get_class($driver), $driver->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $driver->train);
|
||||
$this->assertNull($driver->train);
|
||||
|
||||
$this->assertEquals($sqlCount + 1, count($this->_sqlLoggerStack->queries));
|
||||
}
|
||||
|
||||
public function testEagerLoadManyToOne()
|
||||
{
|
||||
$train = new Train();
|
||||
$waggon = new Waggon();
|
||||
$train->addWaggon($waggon);
|
||||
|
||||
$this->_em->persist($train); // cascades
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$waggon = $this->_em->find(get_class($waggon), $waggon->id);
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $waggon->train);
|
||||
$this->assertNotNull($waggon->train);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Train
|
||||
{
|
||||
/**
|
||||
* @id @column(type="integer") @generatedValue
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
/**
|
||||
* Owning side
|
||||
* @OneToOne(targetEntity="TrainDriver", inversedBy="train", fetch="EAGER", cascade={"persist"})
|
||||
*/
|
||||
public $driver;
|
||||
/**
|
||||
* @oneToMany(targetEntity="Waggon", mappedBy="train", cascade={"persist"})
|
||||
*/
|
||||
public $waggons;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->waggons = new \Doctrine\Common\Collections\ArrayCollection();
|
||||
}
|
||||
|
||||
public function setDriver(TrainDriver $driver)
|
||||
{
|
||||
$this->driver = $driver;
|
||||
$driver->setTrain($this);
|
||||
}
|
||||
|
||||
public function addWaggon(Waggon $w)
|
||||
{
|
||||
$w->setTrain($this);
|
||||
$this->waggons[] = $w;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class TrainDriver
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
public $id;
|
||||
/** @column(type="string") */
|
||||
public $name;
|
||||
/**
|
||||
* Inverse side
|
||||
* @OneToOne(targetEntity="Train", mappedBy="driver", fetch="EAGER")
|
||||
*/
|
||||
public $train;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setTrain(Train $t)
|
||||
{
|
||||
$this->train = $t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Waggon
|
||||
{
|
||||
/** @id @generatedValue @column(type="integer") */
|
||||
public $id;
|
||||
/** @ManyToOne(targetEntity="Train", inversedBy="waggons", fetch="EAGER") */
|
||||
public $train;
|
||||
|
||||
public function setTrain($train)
|
||||
{
|
||||
$this->train = $train;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser,
|
||||
Doctrine\Tests\Models\CMS\CmsArticle;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
@ -313,4 +314,34 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertTrue($result[0]->user instanceof \Doctrine\ORM\Proxy\Proxy);
|
||||
$this->assertFalse($result[0]->user->__isInitialized__);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-952
|
||||
*/
|
||||
public function testEnableFetchEagerMode()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$article = new CmsArticle;
|
||||
$article->topic = "dr. dolittle";
|
||||
$article->text = "Once upon a time ...";
|
||||
$author = new CmsUser;
|
||||
$author->name = "anonymous";
|
||||
$author->username = "anon".$i;
|
||||
$author->status = "here";
|
||||
$article->user = $author;
|
||||
$this->_em->persist($author);
|
||||
$this->_em->persist($article);
|
||||
}
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$articles = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsArticle a')
|
||||
->setFetchMode('Doctrine\Tests\Models\CMS\CmsArticle', 'user', ClassMetadata::FETCH_EAGER)
|
||||
->getResult();
|
||||
|
||||
$this->assertEquals(10, count($articles));
|
||||
foreach ($articles AS $article) {
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $article);
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
@ -349,4 +351,20 @@ class SingleTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$ref = $this->_em->getReference('Doctrine\Tests\Models\Company\CompanyFixContract', $this->fix->getId());
|
||||
$this->assertInstanceOf('Doctrine\ORM\Proxy\Proxy', $ref, "A proxy can be generated only if no subclasses exists for the requested reference.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-952
|
||||
*/
|
||||
public function testEagerLoadInheritanceHierachy()
|
||||
{
|
||||
$this->loadFullFixture();
|
||||
|
||||
$dql = 'SELECT f FROM Doctrine\Tests\Models\Company\CompanyFixContract f WHERE f.id = ?1';
|
||||
$contract = $this->_em->createQuery($dql)
|
||||
->setFetchMode('Doctrine\Tests\Models\Company\CompanyFixContract', 'salesPerson', ClassMetadata::FETCH_EAGER)
|
||||
->setParameter(1, $this->fix->getId())
|
||||
->getSingleResult();
|
||||
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $contract->getSalesPerson());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
37
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1050Test.php
Normal file
37
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1050Test.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
require_once __DIR__ . '/../../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1050
|
||||
*/
|
||||
class DDC1050Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
$this->markTestSkipped('performance skipped');
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPerformance()
|
||||
{
|
||||
for ($i = 2; $i < 10000; ++$i) {
|
||||
$user = new \Doctrine\Tests\Models\CMS\CmsUser();
|
||||
$user->status = 'developer';
|
||||
$user->username = 'jwage'+$i;
|
||||
$user->name = 'Jonathan';
|
||||
$this->_em->persist($user);
|
||||
}
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$s = microtime(true);
|
||||
$users = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser')->findAll();
|
||||
$e = microtime(true);
|
||||
echo __FUNCTION__ . " - " . ($e - $s) . " seconds" . PHP_EOL;
|
||||
}
|
||||
}
|
@ -21,6 +21,11 @@ class DDC633Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-633
|
||||
* @group DDC-952
|
||||
* @group DDC-914
|
||||
*/
|
||||
public function testOneToOneEager()
|
||||
{
|
||||
$app = new DDC633Appointment();
|
||||
@ -35,7 +40,35 @@ class DDC633Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$eagerAppointment = $this->_em->find(__NAMESPACE__ . '\DDC633Appointment', $app->id);
|
||||
|
||||
$this->assertNotType('Doctrine\ORM\Proxy\Proxy', $eagerAppointment->patient);
|
||||
// Eager loading of one to one leads to fetch-join
|
||||
$this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $eagerAppointment->patient);
|
||||
$this->assertTrue($this->_em->contains($eagerAppointment->patient));
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-633
|
||||
* @group DDC-952
|
||||
*/
|
||||
public function testDQLDeferredEagerLoad()
|
||||
{
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$app = new DDC633Appointment();
|
||||
$pat = new DDC633Patient();
|
||||
$app->patient = $pat;
|
||||
$pat->appointment = $app;
|
||||
|
||||
$this->_em->persist($app);
|
||||
$this->_em->persist($pat);
|
||||
}
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$appointments = $this->_em->createQuery("SELECT a FROM " . __NAMESPACE__ . "\DDC633Appointment a")->getResult();
|
||||
|
||||
foreach ($appointments AS $eagerAppointment) {
|
||||
$this->assertType('Doctrine\ORM\Proxy\Proxy', $eagerAppointment->patient);
|
||||
$this->assertTrue($eagerAppointment->patient->__isInitialized__, "Proxy should already be initialized due to eager loading!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +119,7 @@ class TypeTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
|
||||
$dateTimeDb = $this->_em->find('Doctrine\Tests\Models\Generic\DateTimeModel', $dateTime->id);
|
||||
|
||||
$this->assertInstanceOf('DateTime', $dateTime->datetime);
|
||||
$this->assertInstanceOf('DateTime', $dateTimeDb->datetime);
|
||||
$this->assertEquals('2009-10-02 20:10:52', $dateTimeDb->datetime->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Performance;
|
||||
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class InheritancePersisterPerformanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('company');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testCompanyContract()
|
||||
{
|
||||
$person = new \Doctrine\Tests\Models\Company\CompanyEmployee();
|
||||
$person->setName('Poor Sales Guy');
|
||||
$person->setDepartment('Sales');
|
||||
$person->setSalary(100);
|
||||
$this->_em->persist($person);
|
||||
|
||||
for ($i = 0; $i < 33; $i++) {
|
||||
$fix = new \Doctrine\Tests\Models\Company\CompanyFixContract();
|
||||
$fix->setFixPrice(1000);
|
||||
$fix->setSalesPerson($person);
|
||||
$fix->markCompleted();
|
||||
$this->_em->persist($fix);
|
||||
|
||||
$flex = new \Doctrine\Tests\Models\Company\CompanyFlexContract();
|
||||
$flex->setSalesPerson($person);
|
||||
$flex->setHoursWorked(100);
|
||||
$flex->setPricePerHour(100);
|
||||
$flex->markCompleted();
|
||||
$this->_em->persist($flex);
|
||||
|
||||
$ultra = new \Doctrine\Tests\Models\Company\CompanyFlexUltraContract();
|
||||
$ultra->setSalesPerson($person);
|
||||
$ultra->setHoursWorked(150);
|
||||
$ultra->setPricePerHour(150);
|
||||
$ultra->setMaxPrice(7000);
|
||||
$this->_em->persist($ultra);
|
||||
}
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$contracts = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyContract')->findAll();
|
||||
echo "99 CompanyContract: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
$this->assertEquals(99, count($contracts));
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$contracts = $this->_em->getRepository('Doctrine\Tests\Models\Company\CompanyContract')->findAll();
|
||||
echo "99 CompanyContract: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
$this->assertEquals(99, count($contracts));
|
||||
}
|
||||
}
|
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Performance;
|
||||
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
use Doctrine\Tests\Models\CMS\CmsAddress;
|
||||
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
use Doctrine\Tests\Models\CMS\CmsArticle;
|
||||
use Doctrine\Tests\Models\CMS\CmsComment;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
class PersisterPerformanceTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testFindCmsArticle()
|
||||
{
|
||||
$author = new CmsUser();
|
||||
$author->name = "beberlei";
|
||||
$author->status = "active";
|
||||
$author->username = "beberlei";
|
||||
$this->_em->persist($author);
|
||||
|
||||
$ids = array();
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$article = new CmsArticle();
|
||||
$article->text = "foo";
|
||||
$article->topic = "bar";
|
||||
$article->user = $author;
|
||||
$this->_em->persist($article);
|
||||
$ids[] = $article;
|
||||
}
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsArticle')->findAll();
|
||||
echo "100 CmsArticle findAll(): " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsArticle')->findAll();
|
||||
echo "100 CmsArticle findAll(): " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsArticle')->find($ids[$i]->id);
|
||||
}
|
||||
echo "100 CmsArticle find(): " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsArticle')->find($ids[$i]->id);
|
||||
}
|
||||
echo "100 CmsArticle find(): " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
}
|
||||
|
||||
public function testFindCmsGroup()
|
||||
{
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$group = new CmsGroup();
|
||||
$group->name = "foo" . $i;
|
||||
$this->_em->persist($group);
|
||||
}
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsGroup')->findAll();
|
||||
echo "100 CmsGroup: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsGroup')->findAll();
|
||||
echo "100 CmsGroup: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
}
|
||||
|
||||
public function testFindCmsUser()
|
||||
{
|
||||
for ($i = 0; $i < 100; $i++) {
|
||||
$user = new CmsUser();
|
||||
$user->name = "beberlei";
|
||||
$user->status = "active";
|
||||
$user->username = "beberlei".$i;
|
||||
$this->_em->persist($user);
|
||||
}
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser')->findAll();
|
||||
echo "100 CmsUser: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
|
||||
$this->_em->clear();
|
||||
|
||||
$start = microtime(true);
|
||||
$articles = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser')->findAll();
|
||||
echo "100 CmsUser: " . number_format(microtime(true) - $start, 6) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user