1
0
mirror of synced 2024-12-14 07:06:04 +03:00

Merge pull request #422 from FabioBatSilva/DDC-1574

DDC-1574 - "new" operator
This commit is contained in:
Benjamin Eberlei 2012-10-05 07:31:18 -07:00
commit 13762f20c9
11 changed files with 1441 additions and 426 deletions

View File

@ -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'])) {

View File

@ -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,21 +387,21 @@ 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]];
$oid = spl_object_hash($parentObject);
$relationField = $this->_rsm->relationMap[$dqlAlias];
$relation = $parentClass->associationMappings[$relationField];
$reflField = $parentClass->reflFields[$relationField];
$parentClass = $this->ce[$this->_rsm->aliasMap[$parentAlias]];
$oid = spl_object_hash($parentObject);
$relationField = $this->_rsm->relationMap[$dqlAlias];
$relation = $parentClass->associationMappings[$relationField];
$reflField = $parentClass->reflFields[$relationField];
// Check the type of the relation (many or single-valued)
if ( ! ($relation['type'] & ClassMetadata::TO_ONE)) {
@ -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;
$indexIsValid = $index !== false ? isset($reflFieldValue[$index]) : 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();
}
}
}

View 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);
}
}

View File

@ -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

View File

@ -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.
*

View File

@ -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.
*

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@ -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')) {