Merge pull request #422 from FabioBatSilva/DDC-1574
DDC-1574 - "new" operator
This commit is contained in:
commit
13762f20c9
@ -234,6 +234,25 @@ abstract class AbstractHydrator
|
||||
// maybe from an additional column that has not been defined in a NativeQuery ResultSetMapping.
|
||||
continue 2;
|
||||
}
|
||||
|
||||
if (isset($this->_rsm->newObjectMappings[$key])) {
|
||||
$mapping = $this->_rsm->newObjectMappings[$key];
|
||||
|
||||
$cache[$key]['isNewObjectParameter'] = true;
|
||||
$cache[$key]['argIndex'] = $mapping['argIndex'];
|
||||
$cache[$key]['objIndex'] = $mapping['objIndex'];
|
||||
$cache[$key]['class'] = new \ReflectionClass($mapping['className']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isNewObjectParameter'])) {
|
||||
$class = $cache[$key]['class'];
|
||||
$argIndex = $cache[$key]['argIndex'];
|
||||
$objIndex = $cache[$key]['objIndex'];
|
||||
$value = $cache[$key]['type']->convertToPHPValue($value, $this->_platform);
|
||||
|
||||
$rowData['newObjects'][$objIndex]['class'] = $class;
|
||||
$rowData['newObjects'][$objIndex]['args'][$argIndex] = $value;
|
||||
}
|
||||
|
||||
if (isset($cache[$key]['isScalar'])) {
|
||||
|
@ -19,15 +19,12 @@
|
||||
|
||||
namespace Doctrine\ORM\Internal\Hydration;
|
||||
|
||||
use PDO,
|
||||
Doctrine\ORM\Mapping\ClassMetadata,
|
||||
Doctrine\ORM\PersistentCollection,
|
||||
Doctrine\ORM\Query,
|
||||
Doctrine\ORM\Event\LifecycleEventArgs,
|
||||
Doctrine\ORM\Events,
|
||||
Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Collection,
|
||||
Doctrine\ORM\Proxy\Proxy;
|
||||
use PDO;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\PersistentCollection;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Proxy\Proxy;
|
||||
|
||||
/**
|
||||
* The ObjectHydrator constructs an object graph out of an SQL result set.
|
||||
@ -35,6 +32,7 @@ use PDO,
|
||||
* @since 2.0
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Guilherme Blanco <guilhermeblanoc@hotmail.com>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @internal Highly performance-sensitive code.
|
||||
*/
|
||||
@ -43,38 +41,67 @@ class ObjectHydrator extends AbstractHydrator
|
||||
/* Local ClassMetadata cache to avoid going to the EntityManager all the time.
|
||||
* This local cache is maintained between hydration runs and not cleared.
|
||||
*/
|
||||
private $_ce = array();
|
||||
private $ce = array();
|
||||
|
||||
/* The following parts are reinitialized on every hydration run. */
|
||||
|
||||
private $_identifierMap;
|
||||
private $_resultPointers;
|
||||
private $_idTemplate;
|
||||
private $_resultCounter;
|
||||
private $_rootAliases = array();
|
||||
private $_initializedCollections = array();
|
||||
private $_existingCollections = array();
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $identifierMap;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $resultPointers;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $idTemplate;
|
||||
|
||||
/**
|
||||
* @var integer
|
||||
*/
|
||||
private $resultCounter;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rootAliases = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $initializedCollections = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $existingCollections = array();
|
||||
|
||||
|
||||
/** @override */
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function prepare()
|
||||
{
|
||||
$this->_identifierMap =
|
||||
$this->_resultPointers =
|
||||
$this->_idTemplate = array();
|
||||
$this->identifierMap =
|
||||
$this->resultPointers =
|
||||
$this->idTemplate = array();
|
||||
|
||||
$this->_resultCounter = 0;
|
||||
$this->resultCounter = 0;
|
||||
|
||||
if ( ! isset($this->_hints['deferEagerLoad'])) {
|
||||
$this->_hints['deferEagerLoad'] = true;
|
||||
}
|
||||
|
||||
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
|
||||
$this->_identifierMap[$dqlAlias] = array();
|
||||
$this->_idTemplate[$dqlAlias] = '';
|
||||
$this->identifierMap[$dqlAlias] = array();
|
||||
$this->idTemplate[$dqlAlias] = '';
|
||||
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
if ( ! isset($this->ce[$className])) {
|
||||
$this->ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
|
||||
// Remember which associations are "fetch joined", so that we know where to inject
|
||||
@ -88,7 +115,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
$sourceClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]];
|
||||
$sourceClass = $this->_getClassMetadata($sourceClassName);
|
||||
$sourceClass = $this->getClassMetadata($sourceClassName);
|
||||
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
|
||||
|
||||
$this->_hints['fetched'][$this->_rsm->parentAliasMap[$dqlAlias]][$assoc['fieldName']] = true;
|
||||
@ -106,7 +133,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
// handle fetch-joined owning side bi-directional one-to-one associations
|
||||
if ($assoc['inversedBy']) {
|
||||
$class = $this->_ce[$className];
|
||||
$class = $this->ce[$className];
|
||||
$inverseAssoc = $class->associationMappings[$assoc['inversedBy']];
|
||||
|
||||
if ( ! ($inverseAssoc['type'] & ClassMetadata::TO_ONE)) {
|
||||
@ -127,10 +154,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
parent::cleanup();
|
||||
|
||||
$this->_identifierMap =
|
||||
$this->_initializedCollections =
|
||||
$this->_existingCollections =
|
||||
$this->_resultPointers = array();
|
||||
$this->identifierMap =
|
||||
$this->initializedCollections =
|
||||
$this->existingCollections =
|
||||
$this->resultPointers = array();
|
||||
|
||||
if ($eagerLoad) {
|
||||
$this->_em->getUnitOfWork()->triggerEagerLoads();
|
||||
@ -150,7 +177,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
// Take snapshots from all newly initialized collections
|
||||
foreach ($this->_initializedCollections as $coll) {
|
||||
foreach ($this->initializedCollections as $coll) {
|
||||
$coll->takeSnapshot();
|
||||
}
|
||||
|
||||
@ -165,7 +192,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param string $fieldName The name of the field on the entity that holds the collection.
|
||||
* @param string $parentDqlAlias Alias of the parent fetch joining this collection.
|
||||
*/
|
||||
private function _initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
|
||||
private function initRelatedCollection($entity, $class, $fieldName, $parentDqlAlias)
|
||||
{
|
||||
$oid = spl_object_hash($entity);
|
||||
$relation = $class->associationMappings[$fieldName];
|
||||
@ -177,14 +204,14 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
if ( ! $value instanceof PersistentCollection) {
|
||||
$value = new PersistentCollection(
|
||||
$this->_em, $this->_ce[$relation['targetEntity']], $value
|
||||
$this->_em, $this->ce[$relation['targetEntity']], $value
|
||||
);
|
||||
$value->setOwner($entity, $relation);
|
||||
|
||||
$class->reflFields[$fieldName]->setValue($entity, $value);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $fieldName, $value);
|
||||
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
$this->initializedCollections[$oid . $fieldName] = $value;
|
||||
} else if (
|
||||
isset($this->_hints[Query::HINT_REFRESH]) ||
|
||||
isset($this->_hints['fetched'][$parentDqlAlias][$fieldName]) &&
|
||||
@ -195,10 +222,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$value->setInitialized(true);
|
||||
$value->unwrap()->clear();
|
||||
|
||||
$this->_initializedCollections[$oid . $fieldName] = $value;
|
||||
$this->initializedCollections[$oid . $fieldName] = $value;
|
||||
} else {
|
||||
// Is already PersistentCollection, and DON'T REFRESH or FETCH-JOIN!
|
||||
$this->_existingCollections[$oid . $fieldName] = $value;
|
||||
$this->existingCollections[$oid . $fieldName] = $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
@ -211,7 +238,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param string $dqlAlias The DQL alias of the entity's class.
|
||||
* @return object The entity.
|
||||
*/
|
||||
private function _getEntity(array $data, $dqlAlias)
|
||||
private function getEntity(array $data, $dqlAlias)
|
||||
{
|
||||
$className = $this->_rsm->aliasMap[$dqlAlias];
|
||||
|
||||
@ -231,13 +258,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
throw HydrationException::emptyDiscriminatorValue($dqlAlias);
|
||||
}
|
||||
|
||||
$className = $this->_ce[$className]->discriminatorMap[$data[$discrColumn]];
|
||||
$className = $this->ce[$className]->discriminatorMap[$data[$discrColumn]];
|
||||
|
||||
unset($data[$discrColumn]);
|
||||
}
|
||||
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->_rootAliases[$dqlAlias])) {
|
||||
$this->registerManaged($this->_ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
if (isset($this->_hints[Query::HINT_REFRESH_ENTITY]) && isset($this->rootAliases[$dqlAlias])) {
|
||||
$this->registerManaged($this->ce[$className], $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
|
||||
}
|
||||
|
||||
$this->_hints['fetchAlias'] = $dqlAlias;
|
||||
@ -250,10 +277,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param array $data
|
||||
* @return mixed
|
||||
*/
|
||||
private function _getEntityFromIdentityMap($className, array $data)
|
||||
private function getEntityFromIdentityMap($className, array $data)
|
||||
{
|
||||
// TODO: Abstract this code and UnitOfWork::createEntity() equivalent?
|
||||
$class = $this->_ce[$className];
|
||||
$class = $this->ce[$className];
|
||||
|
||||
/* @var $class ClassMetadata */
|
||||
if ($class->isIdentifierComposite) {
|
||||
@ -281,13 +308,13 @@ class ObjectHydrator extends AbstractHydrator
|
||||
* @param string $className The name of the class.
|
||||
* @return ClassMetadata
|
||||
*/
|
||||
private function _getClassMetadata($className)
|
||||
private function getClassMetadata($className)
|
||||
{
|
||||
if ( ! isset($this->_ce[$className])) {
|
||||
$this->_ce[$className] = $this->_em->getClassMetadata($className);
|
||||
if ( ! isset($this->ce[$className])) {
|
||||
$this->ce[$className] = $this->_em->getClassMetadata($className);
|
||||
}
|
||||
|
||||
return $this->_ce[$className];
|
||||
return $this->ce[$className];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -314,7 +341,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
protected function hydrateRowData(array $row, array &$cache, array &$result)
|
||||
{
|
||||
// Initialize
|
||||
$id = $this->_idTemplate; // initialize the id-memory
|
||||
$id = $this->idTemplate; // initialize the id-memory
|
||||
$nonemptyComponents = array();
|
||||
// Split the row data into chunks of class data.
|
||||
$rowData = $this->gatherRowData($row, $cache, $id, $nonemptyComponents);
|
||||
@ -326,7 +353,18 @@ class ObjectHydrator extends AbstractHydrator
|
||||
unset($rowData['scalars']);
|
||||
|
||||
if (empty($rowData)) {
|
||||
++$this->_resultCounter;
|
||||
++$this->resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract "new" object constructor arguments. They're appended at the end.
|
||||
if (isset($rowData['newObjects'])) {
|
||||
$newObjects = $rowData['newObjects'];
|
||||
|
||||
unset($rowData['newObjects']);
|
||||
|
||||
if (empty($rowData)) {
|
||||
++$this->resultCounter;
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,17 +387,17 @@ class ObjectHydrator extends AbstractHydrator
|
||||
}
|
||||
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parentAlias])) {
|
||||
$first = reset($this->_resultPointers);
|
||||
if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
|
||||
$first = reset($this->resultPointers);
|
||||
$parentObject = $first[key($first)];
|
||||
} else if (isset($this->_resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->_resultPointers[$parentAlias];
|
||||
} else if (isset($this->resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->resultPointers[$parentAlias];
|
||||
} else {
|
||||
// Parent object of relation not found, so skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentClass = $this->_ce[$this->_rsm->aliasMap[$parentAlias]];
|
||||
$parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]];
|
||||
$oid = spl_object_hash($parentObject);
|
||||
$relationField = $this->_rsm->relationMap[$dqlAlias];
|
||||
$relation = $parentClass->associationMappings[$relationField];
|
||||
@ -371,45 +409,45 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// PATH A: Collection-valued association
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$collKey = $oid . $relationField;
|
||||
if (isset($this->_initializedCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initializedCollections[$collKey];
|
||||
} else if ( ! isset($this->_existingCollections[$collKey])) {
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
|
||||
if (isset($this->initializedCollections[$collKey])) {
|
||||
$reflFieldValue = $this->initializedCollections[$collKey];
|
||||
} else if ( ! isset($this->existingCollections[$collKey])) {
|
||||
$reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
|
||||
}
|
||||
|
||||
$indexExists = isset($this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
|
||||
$indexExists = isset($this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]]);
|
||||
$index = $indexExists ? $this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] : false;
|
||||
$indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : false;
|
||||
|
||||
if ( ! $indexExists || ! $indexIsValid) {
|
||||
if (isset($this->_existingCollections[$collKey])) {
|
||||
if (isset($this->existingCollections[$collKey])) {
|
||||
// Collection exists, only look for the element in the identity map.
|
||||
if ($element = $this->_getEntityFromIdentityMap($entityName, $data)) {
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
if ($element = $this->getEntityFromIdentityMap($entityName, $data)) {
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
} else {
|
||||
unset($this->_resultPointers[$dqlAlias]);
|
||||
unset($this->resultPointers[$dqlAlias]);
|
||||
}
|
||||
} else {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
$element = $this->getEntity($data, $dqlAlias);
|
||||
|
||||
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
|
||||
$indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
|
||||
$reflFieldValue->hydrateSet($indexValue, $element);
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
|
||||
$this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
|
||||
} else {
|
||||
$reflFieldValue->hydrateAdd($element);
|
||||
$reflFieldValue->last();
|
||||
$this->_identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
|
||||
$this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $reflFieldValue->key();
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
}
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue[$index];
|
||||
$this->resultPointers[$dqlAlias] = $reflFieldValue[$index];
|
||||
}
|
||||
} else if ( ! $reflFieldValue) {
|
||||
$reflFieldValue = $this->_initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
|
||||
$reflFieldValue = $this->initRelatedCollection($parentObject, $parentClass, $relationField, $parentAlias);
|
||||
} else if ($reflFieldValue instanceof PersistentCollection && $reflFieldValue->isInitialized() === false) {
|
||||
$reflFieldValue->setInitialized(true);
|
||||
}
|
||||
@ -421,10 +459,10 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// we only need to take action if this value is null,
|
||||
// we refresh the entity or its an unitialized proxy.
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$element = $this->_getEntity($data, $dqlAlias);
|
||||
$element = $this->getEntity($data, $dqlAlias);
|
||||
$reflField->setValue($parentObject, $element);
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, $element);
|
||||
$targetClass = $this->_ce[$relation['targetEntity']];
|
||||
$targetClass = $this->ce[$relation['targetEntity']];
|
||||
|
||||
if ($relation['isOwningSide']) {
|
||||
//TODO: Just check hints['fetched'] here?
|
||||
@ -445,7 +483,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$this->_uow->setOriginalEntityProperty(spl_object_hash($element), $relation['mappedBy'], $parentObject);
|
||||
}
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
} else {
|
||||
$this->_uow->setOriginalEntityProperty($oid, $relationField, null);
|
||||
$reflField->setValue($parentObject, null);
|
||||
@ -453,12 +491,12 @@ class ObjectHydrator extends AbstractHydrator
|
||||
// else leave $reflFieldValue null for single-valued associations
|
||||
} else {
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $reflFieldValue;
|
||||
$this->resultPointers[$dqlAlias] = $reflFieldValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// PATH C: Its a root result element
|
||||
$this->_rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
$this->rootAliases[$dqlAlias] = true; // Mark as root alias
|
||||
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
|
||||
|
||||
// if this row has a NULL value for the root result id then make it a null result.
|
||||
@ -468,14 +506,15 @@ class ObjectHydrator extends AbstractHydrator
|
||||
} else {
|
||||
$result[] = null;
|
||||
}
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
$resultKey = $this->resultCounter;
|
||||
++$this->resultCounter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for existing result from the iterations before
|
||||
if ( ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->_getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
if ( ! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {
|
||||
$element = $this->getEntity($rowData[$dqlAlias], $dqlAlias);
|
||||
|
||||
if ($this->_rsm->isMixed) {
|
||||
$element = array($entityKey => $element);
|
||||
}
|
||||
@ -489,8 +528,8 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
$result[$resultKey] = $element;
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter;
|
||||
++$this->_resultCounter;
|
||||
$resultKey = $this->resultCounter;
|
||||
++$this->resultCounter;
|
||||
|
||||
if (isset($this->_hints['collection'])) {
|
||||
$this->_hints['collection']->hydrateAdd($element);
|
||||
@ -499,15 +538,15 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[] = $element;
|
||||
}
|
||||
|
||||
$this->_identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
|
||||
$this->identifierMap[$dqlAlias][$id[$dqlAlias]] = $resultKey;
|
||||
|
||||
// Update result pointer
|
||||
$this->_resultPointers[$dqlAlias] = $element;
|
||||
$this->resultPointers[$dqlAlias] = $element;
|
||||
|
||||
} else {
|
||||
// Update result pointer
|
||||
$index = $this->_identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$this->_resultPointers[$dqlAlias] = $result[$index];
|
||||
$index = $this->identifierMap[$dqlAlias][$id[$dqlAlias]];
|
||||
$this->resultPointers[$dqlAlias] = $result[$index];
|
||||
$resultKey = $index;
|
||||
/*if ($this->_rsm->isMixed) {
|
||||
$result[] = $result[$index];
|
||||
@ -523,7 +562,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
if (isset($this->_rsm->indexByMap['scalars'])) {
|
||||
$resultKey = $row[$this->_rsm->indexByMap['scalars']];
|
||||
} else {
|
||||
$resultKey = $this->_resultCounter - 1;
|
||||
$resultKey = $this->resultCounter - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -531,6 +570,30 @@ class ObjectHydrator extends AbstractHydrator
|
||||
$result[$resultKey][$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Append new object to mixed result sets
|
||||
if (isset($newObjects)) {
|
||||
if ( ! isset($resultKey) ) {
|
||||
$resultKey = $this->resultCounter - 1;
|
||||
}
|
||||
|
||||
$count = count($newObjects);
|
||||
|
||||
foreach ($newObjects as $objIndex => $newObject) {
|
||||
$class = $newObject['class'];
|
||||
$args = $newObject['args'];
|
||||
$obj = $class->newInstanceArgs($args);
|
||||
|
||||
if ($count === 1) {
|
||||
$result[$resultKey] = $obj;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$resultKey][$objIndex] = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -541,11 +604,11 @@ class ObjectHydrator extends AbstractHydrator
|
||||
{
|
||||
parent::onClear($eventArgs);
|
||||
|
||||
$aliases = array_keys($this->_identifierMap);
|
||||
$this->_identifierMap = array();
|
||||
$aliases = array_keys($this->identifierMap);
|
||||
$this->identifierMap = array();
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
$this->_identifierMap[$alias] = array();
|
||||
$this->identifierMap[$alias] = array();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
60
lib/Doctrine/ORM/Query/AST/NewObjectExpression.php
Normal file
60
lib/Doctrine/ORM/Query/AST/NewObjectExpression.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?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\Query\AST;
|
||||
|
||||
/**
|
||||
* NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")"
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.3
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
class NewObjectExpression extends Node
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $className;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $args;
|
||||
|
||||
/**
|
||||
* @param type $className
|
||||
* @param array $args
|
||||
*/
|
||||
public function __construct($className, array $args)
|
||||
{
|
||||
$this->className = $className;
|
||||
$this->args = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\ORM\Query\SqlWalker $sqlWalker
|
||||
* @return string
|
||||
*/
|
||||
public function dispatch($sqlWalker)
|
||||
{
|
||||
return $sqlWalker->walkNewObject($this);
|
||||
}
|
||||
}
|
@ -108,6 +108,7 @@ class Lexer extends \Doctrine\Common\Lexer
|
||||
const T_WHERE = 154;
|
||||
const T_WITH = 155;
|
||||
const T_PARTIAL = 156;
|
||||
const T_NEW = 157;
|
||||
|
||||
/**
|
||||
* Creates a new query scanner object.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -118,6 +118,11 @@ class ResultSetMapping
|
||||
*/
|
||||
public $isIdentifierColumn = array();
|
||||
|
||||
/**
|
||||
* @var array Maps column names in the result set to field names for each new object expression.
|
||||
*/
|
||||
public $newObjectMappings = array();
|
||||
|
||||
/**
|
||||
* Adds an entity result to this ResultSetMapping.
|
||||
*
|
||||
|
@ -34,6 +34,7 @@ use Doctrine\DBAL\LockMode,
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
* @since 2.0
|
||||
* @todo Rename: SQLWalker
|
||||
*/
|
||||
@ -77,6 +78,13 @@ class SqlWalker implements TreeWalker
|
||||
*/
|
||||
private $sqlParamIndex = 0;
|
||||
|
||||
/**
|
||||
* Counters for generating indexes.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
private $newObjectCounter = 0;
|
||||
|
||||
/**
|
||||
* @var ParserResult
|
||||
*/
|
||||
@ -1221,6 +1229,10 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\NewObjectExpression):
|
||||
$sql .= $this->walkNewObject($expr);
|
||||
break;
|
||||
|
||||
default:
|
||||
// IdentificationVariable or PartialObjectExpression
|
||||
if ($expr instanceof AST\PartialObjectExpression) {
|
||||
@ -1387,6 +1399,68 @@ class SqlWalker implements TreeWalker
|
||||
. $this->walkSimpleSelectExpression($simpleSelectClause->simpleSelectExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AST\NewObjectExpression
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkNewObject($newObjectExpression)
|
||||
{
|
||||
$sqlSelectExpressions = array();
|
||||
$objIndex = $this->newObjectCounter++;
|
||||
|
||||
foreach ($newObjectExpression->args as $argIndex => $e) {
|
||||
$resultAlias = $this->scalarResultCounter++;
|
||||
$columnAlias = $this->getSQLColumnAlias('sclr');
|
||||
|
||||
switch (true) {
|
||||
case ($e instanceof AST\NewObjectExpression):
|
||||
$sqlSelectExpressions[] = $e->dispatch($this);
|
||||
break;
|
||||
|
||||
default:
|
||||
$sqlSelectExpressions[] = trim($e->dispatch($this)) . ' AS ' . $columnAlias;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case ($e instanceof AST\PathExpression):
|
||||
$fieldName = $e->field;
|
||||
$dqlAlias = $e->identificationVariable;
|
||||
$qComp = $this->queryComponents[$dqlAlias];
|
||||
$class = $qComp['metadata'];
|
||||
$fieldType = $class->getTypeOfField($fieldName);
|
||||
break;
|
||||
|
||||
case ($e instanceof AST\Literal):
|
||||
switch ($e->type) {
|
||||
case AST\Literal::BOOLEAN:
|
||||
$fieldType = 'boolean';
|
||||
break;
|
||||
|
||||
case AST\Literal::NUMERIC:
|
||||
$fieldType = is_float($e->value) ? 'float' : 'integer';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$fieldType = 'string';
|
||||
break;
|
||||
}
|
||||
|
||||
$this->scalarResultAliasMap[$resultAlias] = $columnAlias;
|
||||
$this->rsm->addScalarResult($columnAlias, $resultAlias, $fieldType);
|
||||
|
||||
$this->rsm->newObjectMappings[$columnAlias] = array(
|
||||
'className' => $newObjectExpression->className,
|
||||
'objIndex' => $objIndex,
|
||||
'argIndex' => $argIndex
|
||||
);
|
||||
}
|
||||
|
||||
return implode(', ', $sqlSelectExpressions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
|
17
tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php
Normal file
17
tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
class CmsAddressDTO
|
||||
{
|
||||
public $country;
|
||||
public $city;
|
||||
public $zip;
|
||||
|
||||
public function __construct($country = null, $city = null, $zip = null)
|
||||
{
|
||||
$this->country = $country;
|
||||
$this->city = $city;
|
||||
$this->zip = $zip;
|
||||
}
|
||||
}
|
19
tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php
Normal file
19
tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
class CmsUserDTO
|
||||
{
|
||||
public $name;
|
||||
public $email;
|
||||
public $address;
|
||||
public $phonenumbers;
|
||||
|
||||
public function __construct($name = null, $email = null, $address = null, $phonenumbers = null)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->email = $email;
|
||||
$this->address = $address;
|
||||
$this->phonenumbers = $phonenumbers;
|
||||
}
|
||||
}
|
590
tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php
Normal file
590
tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php
Normal file
@ -0,0 +1,590 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\CMS\CmsEmail;
|
||||
use Doctrine\Tests\Models\CMS\CmsAddress;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
* @group DDC-1574
|
||||
*/
|
||||
class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $fixtures;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
|
||||
$this->loadFixtures();
|
||||
}
|
||||
|
||||
private function loadFixtures()
|
||||
{
|
||||
$u1 = new CmsUser;
|
||||
$u2 = new CmsUser;
|
||||
$u3 = new CmsUser;
|
||||
|
||||
$u1->setEmail(new CmsEmail());
|
||||
$u1->setAddress(new CmsAddress());
|
||||
$u1->addPhonenumber(new CmsPhonenumber());
|
||||
|
||||
$u2->setEmail(new CmsEmail());
|
||||
$u2->setAddress(new CmsAddress());
|
||||
$u2->addPhonenumber(new CmsPhonenumber());
|
||||
$u2->addPhonenumber(new CmsPhonenumber());
|
||||
|
||||
$u3->setEmail(new CmsEmail());
|
||||
$u3->setAddress(new CmsAddress());
|
||||
$u3->addPhonenumber(new CmsPhonenumber());
|
||||
$u3->addPhonenumber(new CmsPhonenumber());
|
||||
$u3->addPhonenumber(new CmsPhonenumber());
|
||||
|
||||
$u1->name = 'Test 1';
|
||||
$u1->username = '1test';
|
||||
$u1->status = 'developer';
|
||||
$u1->email->email = 'email@test1.com';
|
||||
$u1->address->zip = '111111111';
|
||||
$u1->address->city = 'Some City 1';
|
||||
$u1->address->country = 'Some Country 2';
|
||||
$u1->phonenumbers[0]->phonenumber = "(11) 1111-1111";
|
||||
|
||||
$u2->name = 'Test 2';
|
||||
$u2->username = '2test';
|
||||
$u2->status = 'developer';
|
||||
$u2->email->email = 'email@test2.com';
|
||||
$u2->address->zip = '222222222';
|
||||
$u2->address->city = 'Some City 2';
|
||||
$u2->address->country = 'Some Country 2';
|
||||
$u2->phonenumbers[0]->phonenumber = "(22) 1111-1111";
|
||||
$u2->phonenumbers[1]->phonenumber = "(22) 2222-2222";
|
||||
|
||||
$u3->name = 'Test 3';
|
||||
$u3->username = '3test';
|
||||
$u3->status = 'developer';
|
||||
$u3->email->email = 'email@test3.com';
|
||||
$u3->address->zip = '33333333';
|
||||
$u3->address->city = 'Some City 3';
|
||||
$u3->address->country = 'Some Country 3';
|
||||
$u3->phonenumbers[0]->phonenumber = "(33) 1111-1111";
|
||||
$u3->phonenumbers[1]->phonenumber = "(33) 2222-2222";
|
||||
$u3->phonenumbers[2]->phonenumber = "(33) 3333-3333";
|
||||
|
||||
$this->_em->persist($u1);
|
||||
$this->_em->persist($u2);
|
||||
$this->_em->persist($u3);
|
||||
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$this->fixtures = array($u1, $u2, $u3);
|
||||
}
|
||||
|
||||
public function testShouldSupportsBasicUsage()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
e.email,
|
||||
a.city
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->email->email, $result[0]->email);
|
||||
$this->assertEquals($this->fixtures[1]->email->email, $result[1]->email);
|
||||
$this->assertEquals($this->fixtures[2]->email->email, $result[2]->email);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->city, $result[0]->address);
|
||||
$this->assertEquals($this->fixtures[1]->address->city, $result[1]->address);
|
||||
$this->assertEquals($this->fixtures[2]->address->city, $result[2]->address);
|
||||
}
|
||||
|
||||
public function testShouldAssumeFromEntityNamespaceWhenNotGiven()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new CmsUserDTO(u.name, e.email, a.city)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
}
|
||||
|
||||
public function testShouldSupportFromEntityNamespaceAlias()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new CmsUserDTO(u.name, e.email, a.city)
|
||||
FROM
|
||||
cms:CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
|
||||
$this->_em->getConfiguration()
|
||||
->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS');
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
}
|
||||
|
||||
public function testShouldSupportValueObjectNamespaceAlias()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new cms:CmsUserDTO(u.name, e.email, a.city)
|
||||
FROM
|
||||
cms:CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
|
||||
$this->_em->getConfiguration()
|
||||
->addEntityNamespace('cms', 'Doctrine\Tests\Models\CMS');
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
}
|
||||
|
||||
public function testShouldSupportLiteralExpression()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
'fabio.bat.silva@gmail.com',
|
||||
FALSE,
|
||||
123
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
JOIN
|
||||
u.phonenumbers p
|
||||
GROUP BY
|
||||
u, e, a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals('fabio.bat.silva@gmail.com', $result[0]->email);
|
||||
$this->assertEquals('fabio.bat.silva@gmail.com', $result[1]->email);
|
||||
$this->assertEquals('fabio.bat.silva@gmail.com', $result[2]->email);
|
||||
|
||||
$this->assertEquals(false, $result[0]->address);
|
||||
$this->assertEquals(false, $result[1]->address);
|
||||
$this->assertEquals(false, $result[2]->address);
|
||||
|
||||
$this->assertEquals(123, $result[0]->phonenumbers);
|
||||
$this->assertEquals(123, $result[1]->phonenumbers);
|
||||
$this->assertEquals(123, $result[2]->phonenumbers);
|
||||
}
|
||||
|
||||
public function testShouldSupportCaseExpression()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
CASE WHEN (e.email = 'email@test1.com') THEN 'TEST1' ELSE 'OTHER_TEST' END
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
JOIN
|
||||
u.phonenumbers p
|
||||
GROUP BY
|
||||
u, e, a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals('TEST1', $result[0]->email);
|
||||
$this->assertEquals('OTHER_TEST', $result[1]->email);
|
||||
$this->assertEquals('OTHER_TEST', $result[2]->email);
|
||||
}
|
||||
|
||||
public function testShouldSupportSimpleArithmeticExpression()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
e.email,
|
||||
a.city,
|
||||
a.id + u.id
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
JOIN
|
||||
u.phonenumbers p
|
||||
GROUP BY
|
||||
u, e, a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->email->email, $result[0]->email);
|
||||
$this->assertEquals($this->fixtures[1]->email->email, $result[1]->email);
|
||||
$this->assertEquals($this->fixtures[2]->email->email, $result[2]->email);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->city, $result[0]->address);
|
||||
$this->assertEquals($this->fixtures[1]->address->city, $result[1]->address);
|
||||
$this->assertEquals($this->fixtures[2]->address->city, $result[2]->address);
|
||||
|
||||
$this->assertEquals(
|
||||
($this->fixtures[0]->address->id + $this->fixtures[0]->id),
|
||||
$result[0]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
($this->fixtures[1]->address->id + $this->fixtures[1]->id),
|
||||
$result[1]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
($this->fixtures[2]->address->id + $this->fixtures[2]->id),
|
||||
$result[2]->phonenumbers
|
||||
);
|
||||
}
|
||||
|
||||
public function testShouldSupportAggregateFunctions()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
e.email,
|
||||
a.city,
|
||||
COUNT(p)
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
JOIN
|
||||
u.phonenumbers p
|
||||
GROUP BY
|
||||
u, e, a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->email->email, $result[0]->email);
|
||||
$this->assertEquals($this->fixtures[1]->email->email, $result[1]->email);
|
||||
$this->assertEquals($this->fixtures[2]->email->email, $result[2]->email);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->city, $result[0]->address);
|
||||
$this->assertEquals($this->fixtures[1]->address->city, $result[1]->address);
|
||||
$this->assertEquals($this->fixtures[2]->address->city, $result[2]->address);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[0]->phonenumbers)),
|
||||
$result[0]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[1]->phonenumbers)),
|
||||
$result[1]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[2]->phonenumbers)),
|
||||
$result[2]->phonenumbers
|
||||
);
|
||||
}
|
||||
|
||||
public function testShouldSupportArithmeticExpression()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new Doctrine\Tests\Models\CMS\CmsUserDTO(
|
||||
u.name,
|
||||
e.email,
|
||||
a.city,
|
||||
COUNT(p) + u.id
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
JOIN
|
||||
u.phonenumbers p
|
||||
GROUP BY
|
||||
u, e, a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2]->name);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->email->email, $result[0]->email);
|
||||
$this->assertEquals($this->fixtures[1]->email->email, $result[1]->email);
|
||||
$this->assertEquals($this->fixtures[2]->email->email, $result[2]->email);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->city, $result[0]->address);
|
||||
$this->assertEquals($this->fixtures[1]->address->city, $result[1]->address);
|
||||
$this->assertEquals($this->fixtures[2]->address->city, $result[2]->address);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[0]->phonenumbers) + $this->fixtures[0]->id),
|
||||
$result[0]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[1]->phonenumbers) + $this->fixtures[1]->id),
|
||||
$result[1]->phonenumbers
|
||||
);
|
||||
|
||||
$this->assertEquals(
|
||||
(count($this->fixtures[2]->phonenumbers) + $this->fixtures[2]->id),
|
||||
$result[2]->phonenumbers
|
||||
);
|
||||
}
|
||||
|
||||
public function testShouldSupportMultipleNewOperators()
|
||||
{
|
||||
$dql = "
|
||||
SELECT
|
||||
new CmsUserDTO(
|
||||
u.name,
|
||||
e.email
|
||||
),
|
||||
new CmsAddressDTO(
|
||||
a.country,
|
||||
a.city
|
||||
)
|
||||
FROM
|
||||
Doctrine\Tests\Models\CMS\CmsUser u
|
||||
JOIN
|
||||
u.email e
|
||||
JOIN
|
||||
u.address a
|
||||
ORDER BY
|
||||
u.name";
|
||||
|
||||
$query = $this->_em->createQuery($dql);
|
||||
$result = $query->getResult();
|
||||
|
||||
$this->assertCount(3, $result);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0][0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1][0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2][0]);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[0][1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[1][1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddressDTO', $result[2][1]);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->name, $result[0][0]->name);
|
||||
$this->assertEquals($this->fixtures[1]->name, $result[1][0]->name);
|
||||
$this->assertEquals($this->fixtures[2]->name, $result[2][0]->name);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->email->email, $result[0][0]->email);
|
||||
$this->assertEquals($this->fixtures[1]->email->email, $result[1][0]->email);
|
||||
$this->assertEquals($this->fixtures[2]->email->email, $result[2][0]->email);
|
||||
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->city, $result[0][1]->city);
|
||||
$this->assertEquals($this->fixtures[1]->address->city, $result[1][1]->city);
|
||||
$this->assertEquals($this->fixtures[2]->address->city, $result[2][1]->city);
|
||||
|
||||
$this->assertEquals($this->fixtures[0]->address->country, $result[0][1]->country);
|
||||
$this->assertEquals($this->fixtures[1]->address->country, $result[1][1]->country);
|
||||
$this->assertEquals($this->fixtures[2]->address->country, $result[2][1]->country);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\InvalidClass(u.name)': Error: Class "\InvalidClass" is not defined.
|
||||
*/
|
||||
public function testInvalidClassException()
|
||||
{
|
||||
$dql = "SELECT new \InvalidClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near '\stdClass(u.name)': Error: Class "\stdClass" has not a valid contructor.
|
||||
*/
|
||||
public function testInvalidClassConstructorException()
|
||||
{
|
||||
$dql = "SELECT new \stdClass(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name)': Error: Number of arguments does not match with "Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs" constructor declaration.
|
||||
*/
|
||||
public function testInvalidClassWithoutConstructorException()
|
||||
{
|
||||
$dql = "SELECT new Doctrine\Tests\ORM\Functional\ClassWithTooMuchArgs(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Doctrine\ORM\Query\QueryException
|
||||
* @expectedExceptionMessage [Semantical Error] line 0, col 11 near 'Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name)': Error: Class "Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor" can not be instantiated.
|
||||
*/
|
||||
public function testClassCantBeInstantiatedException()
|
||||
{
|
||||
$dql = "SELECT new Doctrine\Tests\ORM\Functional\ClassWithPrivateConstructor(u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$this->_em->createQuery($dql)->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithTooMuchArgs
|
||||
{
|
||||
public function __construct($foo, $bar)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
$this->bor = $bar;
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithPrivateConstructor
|
||||
{
|
||||
private function __construct($foo)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
}
|
@ -1555,6 +1555,38 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group DDC-1574
|
||||
*/
|
||||
public function testSupportsNewOperator()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a",
|
||||
"SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id"
|
||||
);
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.id + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a",
|
||||
"SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.id + c0_.id AS sclr2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id"
|
||||
);
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p)) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p",
|
||||
"SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id"
|
||||
);
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city, COUNT(p) + u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a JOIN u.phonenumbers p",
|
||||
"SELECT c0_.name AS sclr0, c1_.email AS sclr1, c2_.city AS sclr2, COUNT(c3_.phonenumber) + c0_.id AS sclr3 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id INNER JOIN cms_phonenumbers c3_ ON c0_.id = c3_.user_id"
|
||||
);
|
||||
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(a.id, a.country, a.city), new Doctrine\Tests\Models\CMS\CmsAddressDTO(u.name, e.email) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name",
|
||||
"SELECT c0_.id AS sclr0, c0_.country AS sclr1, c0_.city AS sclr2, c1_.name AS sclr3, c2_.email AS sclr4 FROM cms_users c1_ INNER JOIN cms_emails c2_ ON c1_.email_id = c2_.id INNER JOIN cms_addresses c0_ ON c1_.id = c0_.user_id ORDER BY c1_.name ASC"
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function testCustomTypeValueSql()
|
||||
{
|
||||
if (DBALType::hasType('negative_to_positive')) {
|
||||
|
Loading…
Reference in New Issue
Block a user