commit
a3210e78aa
@ -24,4 +24,9 @@ Also, related functions were affected:
|
||||
* iterate($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance
|
||||
* setParameters($parameters) the argument $parameters can be either an key=>value array or an ArrayCollection instance
|
||||
* getParameters() now returns ArrayCollection instead of array
|
||||
* getParameter($key) now returns Parameter instance instead of parameter value
|
||||
* getParameter($key) now returns Parameter instance instead of parameter value
|
||||
|
||||
# Query TreeWalker method renamed
|
||||
|
||||
Internal changes were made to DQL and SQL generation. If you have implemented your own TreeWalker,
|
||||
you probably need to update it. The method walkJoinVariableDeclaration is now named walkJoin.
|
||||
|
@ -36,13 +36,13 @@ class IdentificationVariableDeclaration extends Node
|
||||
{
|
||||
public $rangeVariableDeclaration = null;
|
||||
public $indexBy = null;
|
||||
public $joinVariableDeclarations = array();
|
||||
public $joins = array();
|
||||
|
||||
public function __construct($rangeVariableDecl, $indexBy, array $joinVariableDecls)
|
||||
public function __construct($rangeVariableDecl, $indexBy, array $joins)
|
||||
{
|
||||
$this->rangeVariableDeclaration = $rangeVariableDecl;
|
||||
$this->indexBy = $indexBy;
|
||||
$this->joinVariableDeclarations = $joinVariableDecls;
|
||||
$this->joins = $joins;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
|
@ -25,30 +25,26 @@ namespace Doctrine\ORM\Query\AST;
|
||||
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression
|
||||
* ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]
|
||||
*
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision: 3938 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class Join extends Node
|
||||
{
|
||||
const JOIN_TYPE_LEFT = 1;
|
||||
const JOIN_TYPE_LEFT = 1;
|
||||
const JOIN_TYPE_LEFTOUTER = 2;
|
||||
const JOIN_TYPE_INNER = 3;
|
||||
const JOIN_TYPE_INNER = 3;
|
||||
|
||||
public $joinType = self::JOIN_TYPE_INNER;
|
||||
public $joinAssociationPathExpression = null;
|
||||
public $aliasIdentificationVariable = null;
|
||||
public $joinAssociationDeclaration = null;
|
||||
public $conditionalExpression = null;
|
||||
|
||||
public function __construct($joinType, $joinAssocPathExpr, $aliasIdentVar)
|
||||
public function __construct($joinType, $joinAssociationDeclaration)
|
||||
{
|
||||
$this->joinType = $joinType;
|
||||
$this->joinAssociationPathExpression = $joinAssocPathExpr;
|
||||
$this->aliasIdentificationVariable = $aliasIdentVar;
|
||||
$this->joinAssociationDeclaration = $joinAssociationDeclaration;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
|
53
lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php
Normal file
53
lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* 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
|
||||
<<<<<<< HEAD:lib/Doctrine/ORM/Query/AST/JoinVariableDeclaration.php
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
=======
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
>>>>>>> Updated PoC for multiple components DQL support.:lib/Doctrine/ORM/Query/AST/JoinAssociationDeclaration.php
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.3
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
*/
|
||||
class JoinAssociationDeclaration extends Node
|
||||
{
|
||||
public $joinAssociationPathExpression;
|
||||
public $aliasIdentificationVariable;
|
||||
public $indexBy;
|
||||
|
||||
public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy)
|
||||
{
|
||||
$this->joinAssociationPathExpression = $joinAssociationPathExpression;
|
||||
$this->aliasIdentificationVariable = $aliasIdentificationVariable;
|
||||
$this->indexBy = $indexBy;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
{
|
||||
return $sqlWalker->walkJoinAssociationDeclaration($this);
|
||||
}
|
||||
}
|
@ -24,10 +24,8 @@ namespace Doctrine\ORM\Query\AST;
|
||||
/**
|
||||
* JoinAssociationPathExpression ::= IdentificationVariable "." (SingleValuedAssociationField | CollectionValuedAssociationField)
|
||||
*
|
||||
*
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision: 3938 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
@ -40,11 +38,11 @@ class JoinAssociationPathExpression extends Node
|
||||
public function __construct($identificationVariable, $associationField)
|
||||
{
|
||||
$this->identificationVariable = $identificationVariable;
|
||||
$this->associationField = $associationField;
|
||||
$this->associationField = $associationField;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
{
|
||||
return $sqlWalker->walkJoinPathExpression($this);
|
||||
return $sqlWalker->walkPathExpression($this);
|
||||
}
|
||||
}
|
||||
|
@ -15,36 +15,33 @@
|
||||
* 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 MIT license. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST;
|
||||
|
||||
/**
|
||||
* JoinVariableDeclaration ::= Join [IndexBy]
|
||||
* JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
*
|
||||
*
|
||||
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
||||
* @link www.doctrine-project.org
|
||||
* @since 2.0
|
||||
* @version $Revision: 3938 $
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @since 2.3
|
||||
* @author Alexander <iam.asm89@gmail.com>
|
||||
*/
|
||||
class JoinVariableDeclaration extends Node
|
||||
class JoinClassPathExpression extends Node
|
||||
{
|
||||
public $join = null;
|
||||
public $indexBy = null;
|
||||
public $abstractSchemaName;
|
||||
public $aliasIdentificationVariable;
|
||||
|
||||
public function __construct($join, $indexBy)
|
||||
public function __construct($abstractSchemaName, $aliasIdentificationVar)
|
||||
{
|
||||
$this->join = $join;
|
||||
$this->indexBy = $indexBy;
|
||||
$this->abstractSchemaName = $abstractSchemaName;
|
||||
$this->aliasIdentificationVariable = $aliasIdentificationVar;
|
||||
}
|
||||
|
||||
public function dispatch($sqlWalker)
|
||||
public function dispatch($walker)
|
||||
{
|
||||
return $sqlWalker->walkJoinVariableDeclaration($this);
|
||||
return $sqlWalker->walkJoinPathExpression($this);
|
||||
}
|
||||
}
|
@ -905,7 +905,7 @@ class Parser
|
||||
$qComp = $this->_queryComponents[$identVariable];
|
||||
$class = $qComp['metadata'];
|
||||
|
||||
if ( ! isset($class->associationMappings[$field])) {
|
||||
if ( ! $class->hasAssociation($field)) {
|
||||
$this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
|
||||
}
|
||||
|
||||
@ -1401,7 +1401,7 @@ class Parser
|
||||
}
|
||||
|
||||
/**
|
||||
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
|
||||
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
|
||||
*/
|
||||
@ -1409,18 +1409,18 @@ class Parser
|
||||
{
|
||||
$rangeVariableDeclaration = $this->RangeVariableDeclaration();
|
||||
$indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
|
||||
$joinVariableDeclarations = array();
|
||||
$joins = array();
|
||||
|
||||
while (
|
||||
$this->_lexer->isNextToken(Lexer::T_LEFT) ||
|
||||
$this->_lexer->isNextToken(Lexer::T_INNER) ||
|
||||
$this->_lexer->isNextToken(Lexer::T_JOIN)
|
||||
) {
|
||||
$joinVariableDeclarations[] = $this->JoinVariableDeclaration();
|
||||
$joins[] = $this->Join();
|
||||
}
|
||||
|
||||
return new AST\IdentificationVariableDeclaration(
|
||||
$rangeVariableDeclaration, $indexBy, $joinVariableDeclarations
|
||||
$rangeVariableDeclaration, $indexBy, $joins
|
||||
);
|
||||
}
|
||||
|
||||
@ -1450,16 +1450,57 @@ class Parser
|
||||
}
|
||||
|
||||
/**
|
||||
* JoinVariableDeclaration ::= Join [IndexBy]
|
||||
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN"
|
||||
* (JoinAssociationDeclaration | RangeVariableDeclaration)
|
||||
* ["WITH" ConditionalExpression]
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\AST\JoinVariableDeclaration
|
||||
* @return \Doctrine\ORM\Query\AST\Join
|
||||
*/
|
||||
public function JoinVariableDeclaration()
|
||||
public function Join()
|
||||
{
|
||||
$join = $this->Join();
|
||||
$indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
|
||||
// Check Join type
|
||||
$joinType = AST\Join::JOIN_TYPE_INNER;
|
||||
|
||||
return new AST\JoinVariableDeclaration($join, $indexBy);
|
||||
switch (true) {
|
||||
case ($this->_lexer->isNextToken(Lexer::T_LEFT)):
|
||||
$this->match(Lexer::T_LEFT);
|
||||
|
||||
$joinType = AST\Join::JOIN_TYPE_LEFT;
|
||||
|
||||
// Possible LEFT OUTER join
|
||||
if ($this->_lexer->isNextToken(Lexer::T_OUTER)) {
|
||||
$this->match(Lexer::T_OUTER);
|
||||
|
||||
$joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($this->_lexer->isNextToken(Lexer::T_INNER)):
|
||||
$this->match(Lexer::T_INNER);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_JOIN);
|
||||
|
||||
$next = $this->_lexer->glimpse();
|
||||
$joinDeclaration = ($next['type'] === Lexer::T_DOT)
|
||||
? $this->JoinAssociationDeclaration()
|
||||
: $this->RangeVariableDeclaration();
|
||||
|
||||
// Create AST node
|
||||
$join = new AST\Join($joinType, $joinDeclaration);
|
||||
|
||||
// Check for ad-hoc Join conditions
|
||||
if ($this->_lexer->isNextToken(Lexer::T_WITH) || $joinDeclaration instanceof AST\RangeVariableDeclaration) {
|
||||
$this->match(Lexer::T_WITH);
|
||||
|
||||
$join->conditionalExpression = $this->ConditionalExpression();
|
||||
}
|
||||
|
||||
return $join;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1494,6 +1535,43 @@ class Parser
|
||||
return new AST\RangeVariableDeclaration($abstractSchemaName, $aliasIdentificationVariable);
|
||||
}
|
||||
|
||||
/**
|
||||
* JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable [IndexBy]
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\AST\JoinAssociationPathExpression
|
||||
*/
|
||||
public function JoinAssociationDeclaration()
|
||||
{
|
||||
$joinAssociationPathExpression = $this->JoinAssociationPathExpression();
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
|
||||
$this->match(Lexer::T_AS);
|
||||
}
|
||||
|
||||
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
|
||||
$indexBy = $this->_lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
|
||||
|
||||
$identificationVariable = $joinAssociationPathExpression->identificationVariable;
|
||||
$field = $joinAssociationPathExpression->associationField;
|
||||
|
||||
$class = $this->_queryComponents[$identificationVariable]['metadata'];
|
||||
$targetClass = $this->_em->getClassMetadata($class->associationMappings[$field]['targetEntity']);
|
||||
|
||||
// Building queryComponent
|
||||
$joinQueryComponent = array(
|
||||
'metadata' => $targetClass,
|
||||
'parent' => $joinAssociationPathExpression->identificationVariable,
|
||||
'relation' => $class->getAssociationMapping($field),
|
||||
'map' => null,
|
||||
'nestingLevel' => $this->_nestingLevel,
|
||||
'token' => $this->_lexer->lookahead
|
||||
);
|
||||
|
||||
$this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
|
||||
|
||||
return new AST\JoinAssociationDeclaration($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
* PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
@ -1535,87 +1613,6 @@ class Parser
|
||||
return $partialObjectExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join ::= ["LEFT" ["OUTER"] | "INNER"] "JOIN" JoinAssociationPathExpression
|
||||
* ["AS"] AliasIdentificationVariable ["WITH" ConditionalExpression]
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\AST\Join
|
||||
*/
|
||||
public function Join()
|
||||
{
|
||||
// Check Join type
|
||||
$joinType = AST\Join::JOIN_TYPE_INNER;
|
||||
|
||||
switch (true) {
|
||||
case ($this->_lexer->isNextToken(Lexer::T_LEFT)):
|
||||
$this->match(Lexer::T_LEFT);
|
||||
|
||||
$joinType = AST\Join::JOIN_TYPE_LEFT;
|
||||
|
||||
// Possible LEFT OUTER join
|
||||
if ($this->_lexer->isNextToken(Lexer::T_OUTER)) {
|
||||
$this->match(Lexer::T_OUTER);
|
||||
|
||||
$joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
|
||||
}
|
||||
break;
|
||||
|
||||
case ($this->_lexer->isNextToken(Lexer::T_INNER)):
|
||||
$this->match(Lexer::T_INNER);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
$this->match(Lexer::T_JOIN);
|
||||
|
||||
$joinPathExpression = $this->JoinAssociationPathExpression();
|
||||
|
||||
if ($this->_lexer->isNextToken(Lexer::T_AS)) {
|
||||
$this->match(Lexer::T_AS);
|
||||
}
|
||||
|
||||
$token = $this->_lexer->lookahead;
|
||||
$aliasIdentificationVariable = $this->AliasIdentificationVariable();
|
||||
|
||||
// Verify that the association exists.
|
||||
$parentClass = $this->_queryComponents[$joinPathExpression->identificationVariable]['metadata'];
|
||||
$assocField = $joinPathExpression->associationField;
|
||||
|
||||
if ( ! $parentClass->hasAssociation($assocField)) {
|
||||
$this->semanticalError(
|
||||
"Class " . $parentClass->name . " has no association named '$assocField'."
|
||||
);
|
||||
}
|
||||
|
||||
$targetClassName = $parentClass->associationMappings[$assocField]['targetEntity'];
|
||||
|
||||
// Building queryComponent
|
||||
$joinQueryComponent = array(
|
||||
'metadata' => $this->_em->getClassMetadata($targetClassName),
|
||||
'parent' => $joinPathExpression->identificationVariable,
|
||||
'relation' => $parentClass->getAssociationMapping($assocField),
|
||||
'map' => null,
|
||||
'nestingLevel' => $this->_nestingLevel,
|
||||
'token' => $token
|
||||
);
|
||||
|
||||
$this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
|
||||
|
||||
// Create AST node
|
||||
$join = new AST\Join($joinType, $joinPathExpression, $aliasIdentificationVariable);
|
||||
|
||||
// Check for ad-hoc Join conditions
|
||||
if ($this->_lexer->isNextToken(Lexer::T_WITH)) {
|
||||
$this->match(Lexer::T_WITH);
|
||||
|
||||
$join->conditionalExpression = $this->ConditionalExpression();
|
||||
}
|
||||
|
||||
return $join;
|
||||
}
|
||||
|
||||
/**
|
||||
* IndexBy ::= "INDEX" "BY" StateFieldPathExpression
|
||||
*
|
||||
|
@ -705,23 +705,10 @@ class SqlWalker implements TreeWalker
|
||||
$sqlParts = array();
|
||||
|
||||
foreach ($identificationVarDecls as $identificationVariableDecl) {
|
||||
$sql = '';
|
||||
$sql = $this->walkRangeVariableDeclaration($identificationVariableDecl->rangeVariableDeclaration);
|
||||
|
||||
$rangeDecl = $identificationVariableDecl->rangeVariableDeclaration;
|
||||
$dqlAlias = $rangeDecl->aliasIdentificationVariable;
|
||||
|
||||
$this->_rootAliases[] = $dqlAlias;
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
}
|
||||
|
||||
foreach ($identificationVariableDecl->joinVariableDeclarations as $joinVarDecl) {
|
||||
$sql .= $this->walkJoinVariableDeclaration($joinVarDecl);
|
||||
foreach ($identificationVariableDecl->joins as $join) {
|
||||
$sql .= $this->walkJoin($join);
|
||||
}
|
||||
|
||||
if ($identificationVariableDecl->indexBy) {
|
||||
@ -744,6 +731,174 @@ class SqlWalker implements TreeWalker
|
||||
return ' FROM ' . implode(', ', $sqlParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a RangeVariableDeclaration AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function walkRangeVariableDeclaration($rangeVariableDeclaration)
|
||||
{
|
||||
$class = $this->_em->getClassMetadata($rangeVariableDeclaration->abstractSchemaName);
|
||||
$dqlAlias = $rangeVariableDeclaration->aliasIdentificationVariable;
|
||||
|
||||
$this->_rootAliases[] = $dqlAlias;
|
||||
|
||||
$sql = $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a JoinAssociationDeclaration AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function walkJoinAssociationDeclaration($joinAssociationDeclaration, $joinType = AST\Join::JOIN_TYPE_INNER)
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
$associationPathExpression = $joinAssociationDeclaration->joinAssociationPathExpression;
|
||||
$joinedDqlAlias = $joinAssociationDeclaration->aliasIdentificationVariable;
|
||||
$indexBy = $joinAssociationDeclaration->indexBy;
|
||||
|
||||
$relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
|
||||
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
|
||||
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
|
||||
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $associationPathExpression->identificationVariable);
|
||||
|
||||
// Ensure we got the owning side, since it has all mapping info
|
||||
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
|
||||
|
||||
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) {
|
||||
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
|
||||
}
|
||||
}
|
||||
|
||||
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
|
||||
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
|
||||
// The owning side is necessary at this point because only it contains the JoinColumn information.
|
||||
switch (true) {
|
||||
case ($assoc['type'] & ClassMetadata::TO_ONE):
|
||||
$conditions = array();
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
|
||||
if ($relation['isOwningSide']) {
|
||||
$quotedTargetColumn = ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn]))
|
||||
? $targetColumn // Join columns cannot be quoted.
|
||||
: $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
|
||||
$conditions[] = $sourceTableAlias . '.' . $sourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$quotedTargetColumn = ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn]))
|
||||
? $targetColumn // Join columns cannot be quoted.
|
||||
: $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
|
||||
$conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
|
||||
}
|
||||
|
||||
// Apply remaining inheritance restrictions
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
|
||||
|
||||
if ($discrSql) {
|
||||
$conditions[] = $discrSql;
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
$filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
|
||||
|
||||
if ($filterExpr) {
|
||||
$conditions[] = $filterExpr;
|
||||
}
|
||||
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
|
||||
break;
|
||||
|
||||
case ($assoc['type'] == ClassMetadata::MANY_TO_MANY):
|
||||
// Join relation table
|
||||
$joinTable = $assoc['joinTable'];
|
||||
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
|
||||
$joinTableName = $sourceClass->getQuotedJoinTableName($assoc, $this->_platform);
|
||||
|
||||
$conditions = array();
|
||||
$relationColumns = ($relation['isOwningSide'])
|
||||
? $assoc['relationToSourceKeyColumns']
|
||||
: $assoc['relationToTargetKeyColumns'];
|
||||
|
||||
foreach ($relationColumns as $relationColumn => $sourceColumn) {
|
||||
$quotedTargetColumn = ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn]))
|
||||
? $sourceColumn // Join columns cannot be quoted.
|
||||
: $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform);
|
||||
|
||||
$conditions[] = $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
|
||||
$sql .= $joinTableName . ' ' . $joinTableAlias . ' ON ' . implode(' AND ', $conditions);
|
||||
|
||||
// Join target table
|
||||
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
|
||||
|
||||
$conditions = array();
|
||||
$relationColumns = ($relation['isOwningSide'])
|
||||
? $assoc['relationToTargetKeyColumns']
|
||||
: $assoc['relationToSourceKeyColumns'];
|
||||
|
||||
foreach ($relationColumns as $relationColumn => $targetColumn) {
|
||||
$quotedTargetColumn = ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn]))
|
||||
? $targetColumn // Join columns cannot be quoted.
|
||||
: $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
|
||||
$conditions[] = $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
|
||||
// Apply remaining inheritance restrictions
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
|
||||
|
||||
if ($discrSql) {
|
||||
$conditions[] = $discrSql;
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
$filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias);
|
||||
|
||||
if ($filterExpr) {
|
||||
$conditions[] = $filterExpr;
|
||||
}
|
||||
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ' . implode(' AND ', $conditions);
|
||||
break;
|
||||
}
|
||||
|
||||
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
|
||||
if ($targetClass->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
|
||||
}
|
||||
|
||||
// Apply the indexes
|
||||
if ($indexBy) {
|
||||
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
|
||||
$this->_rsm->addIndexBy(
|
||||
$indexBy->simpleStateFieldPathExpression->identificationVariable,
|
||||
$indexBy->simpleStateFieldPathExpression->field
|
||||
);
|
||||
} else if (isset($relation['indexBy'])) {
|
||||
$this->_rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a FunctionNode AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
@ -799,169 +954,35 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
|
||||
* Walks down a Join AST node and creates the corresponding SQL.
|
||||
*
|
||||
* @param JoinVariableDeclaration $joinVarDecl
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkJoinVariableDeclaration($joinVarDecl)
|
||||
public function walkJoin($join)
|
||||
{
|
||||
$join = $joinVarDecl->join;
|
||||
$joinType = $join->joinType;
|
||||
$sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
|
||||
$joinType = $join->joinType;
|
||||
$joinDeclaration = $join->joinAssociationDeclaration;
|
||||
|
||||
$sql = ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER)
|
||||
? ' LEFT JOIN '
|
||||
: ' INNER JOIN ';
|
||||
|
||||
if ($joinVarDecl->indexBy) {
|
||||
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
|
||||
$this->_rsm->addIndexBy(
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->field
|
||||
);
|
||||
}
|
||||
switch (true) {
|
||||
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
|
||||
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration)
|
||||
. ' ON (' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
|
||||
break;
|
||||
|
||||
$joinAssocPathExpr = $join->joinAssociationPathExpression;
|
||||
$joinedDqlAlias = $join->aliasIdentificationVariable;
|
||||
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
|
||||
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
|
||||
|
||||
$relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
|
||||
$targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
|
||||
$sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
|
||||
$targetTableName = $targetClass->getQuotedTableName($this->_platform);
|
||||
|
||||
$targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
|
||||
$sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable);
|
||||
|
||||
// Ensure we got the owning side, since it has all mapping info
|
||||
$assoc = ( ! $relation['isOwningSide']) ? $targetClass->associationMappings[$relation['mappedBy']] : $relation;
|
||||
if ($this->_query->getHint(Query::HINT_INTERNAL_ITERATION) == true && (!$this->_query->getHint(self::HINT_DISTINCT) || isset($this->_selectedClasses[$joinedDqlAlias]))) {
|
||||
if ($relation['type'] == ClassMetadata::ONE_TO_MANY || $relation['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
throw QueryException::iterateWithFetchJoinNotAllowed($assoc);
|
||||
}
|
||||
}
|
||||
|
||||
if ($joinVarDecl->indexBy) {
|
||||
// For Many-To-One or One-To-One associations this obviously makes no sense, but is ignored silently.
|
||||
$this->_rsm->addIndexBy(
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable,
|
||||
$joinVarDecl->indexBy->simpleStateFieldPathExpression->field
|
||||
);
|
||||
} else if (isset($relation['indexBy'])) {
|
||||
$this->_rsm->addIndexBy($joinedDqlAlias, $relation['indexBy']);
|
||||
}
|
||||
|
||||
// This condition is not checking ClassMetadata::MANY_TO_ONE, because by definition it cannot
|
||||
// be the owning side and previously we ensured that $assoc is always the owning side of the associations.
|
||||
// The owning side is necessary at this point because only it contains the JoinColumn information.
|
||||
if ($assoc['type'] & ClassMetadata::TO_ONE) {
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
|
||||
$first = true;
|
||||
|
||||
foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($relation['isOwningSide']) {
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
$sql .= $sourceTableAlias . '.' . $sourceColumn . ' = ' . $targetTableAlias . '.' . $quotedTargetColumn;
|
||||
} else {
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $targetTableAlias . '.' . $sourceColumn;
|
||||
// Handle WITH clause
|
||||
if (($condExpr = $join->conditionalExpression) !== null) {
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
}
|
||||
|
||||
} else if ($assoc['type'] == ClassMetadata::MANY_TO_MANY) {
|
||||
// Join relation table
|
||||
$joinTable = $assoc['joinTable'];
|
||||
$joinTableAlias = $this->getSQLTableAlias($joinTable['name'], $joinedDqlAlias);
|
||||
$sql .= $sourceClass->getQuotedJoinTableName($assoc, $this->_platform) . ' ' . $joinTableAlias . ' ON ';
|
||||
|
||||
$first = true;
|
||||
if ($relation['isOwningSide']) {
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn])) {
|
||||
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
} else {
|
||||
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
}
|
||||
|
||||
// Join target table
|
||||
$sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN ';
|
||||
$sql .= $targetTableName . ' ' . $targetTableAlias . ' ON ';
|
||||
|
||||
$first = true;
|
||||
if ($relation['isOwningSide']) {
|
||||
foreach ($assoc['relationToTargetKeyColumns'] as $relationColumn => $targetColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$targetColumn])) {
|
||||
$quotedTargetColumn = $targetColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$targetColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
} else {
|
||||
foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
|
||||
if ( ! $first) $sql .= ' AND '; else $first = false;
|
||||
|
||||
if ($targetClass->containsForeignIdentifier && !isset($targetClass->fieldNames[$sourceColumn])) {
|
||||
$quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
|
||||
} else {
|
||||
$quotedTargetColumn = $targetClass->getQuotedColumnName($targetClass->fieldNames[$sourceColumn], $this->_platform);
|
||||
}
|
||||
|
||||
$sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
if ($filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias)) {
|
||||
$sql .= ' AND ' . $filterExpr;
|
||||
}
|
||||
|
||||
// Handle WITH clause
|
||||
if (($condExpr = $join->conditionalExpression) !== null) {
|
||||
// Phase 2 AST optimization: Skip processment of ConditionalExpression
|
||||
// if only one ConditionalTerm is defined
|
||||
$sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')';
|
||||
}
|
||||
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias));
|
||||
|
||||
if ($discrSql) {
|
||||
$sql .= ' AND ' . $discrSql;
|
||||
}
|
||||
|
||||
// FIXME: these should either be nested or all forced to be left joins (DDC-XXX)
|
||||
if ($targetClass->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias);
|
||||
break;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
@ -1304,23 +1325,10 @@ class SqlWalker implements TreeWalker
|
||||
$sqlParts = array ();
|
||||
|
||||
foreach ($identificationVarDecls as $subselectIdVarDecl) {
|
||||
$sql = '';
|
||||
$sql = $this->walkRangeVariableDeclaration($subselectIdVarDecl->rangeVariableDeclaration);
|
||||
|
||||
$rangeDecl = $subselectIdVarDecl->rangeVariableDeclaration;
|
||||
$dqlAlias = $rangeDecl->aliasIdentificationVariable;
|
||||
|
||||
$class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName);
|
||||
$sql .= $class->getQuotedTableName($this->_platform) . ' '
|
||||
. $this->getSQLTableAlias($class->getTableName(), $dqlAlias);
|
||||
|
||||
$this->_rootAliases[] = $dqlAlias;
|
||||
|
||||
if ($class->isInheritanceTypeJoined()) {
|
||||
$sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias);
|
||||
}
|
||||
|
||||
foreach ($subselectIdVarDecl->joinVariableDeclarations as $joinVarDecl) {
|
||||
$sql .= $this->walkJoinVariableDeclaration($joinVarDecl);
|
||||
foreach ($subselectIdVarDecl->joins as $join) {
|
||||
$sql .= $this->walkJoin($join);
|
||||
}
|
||||
|
||||
$sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE));
|
||||
|
@ -91,12 +91,12 @@ interface TreeWalker
|
||||
function walkHavingClause($havingClause);
|
||||
|
||||
/**
|
||||
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
|
||||
* Walks down a Join AST node and creates the corresponding SQL.
|
||||
*
|
||||
* @param JoinVariableDeclaration $joinVarDecl
|
||||
* @param Join $joinVarDecl
|
||||
* @return string The SQL.
|
||||
*/
|
||||
function walkJoinVariableDeclaration($joinVarDecl);
|
||||
function walkJoin($join);
|
||||
|
||||
/**
|
||||
* Walks down a SelectExpression AST node and generates the corresponding SQL.
|
||||
|
@ -125,12 +125,12 @@ abstract class TreeWalkerAdapter implements TreeWalker
|
||||
public function walkHavingClause($havingClause) {}
|
||||
|
||||
/**
|
||||
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
|
||||
* Walks down a Join AST node and creates the corresponding SQL.
|
||||
*
|
||||
* @param JoinVariableDeclaration $joinVarDecl
|
||||
* @param Join $join
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkJoinVariableDeclaration($joinVarDecl) {}
|
||||
public function walkJoin($join) {}
|
||||
|
||||
/**
|
||||
* Walks down a SelectExpression AST node and generates the corresponding SQL.
|
||||
|
@ -148,15 +148,15 @@ class TreeWalkerChain implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL.
|
||||
* Walks down a Join AST node and creates the corresponding SQL.
|
||||
*
|
||||
* @param JoinVariableDeclaration $joinVarDecl
|
||||
* @param Join $join
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkJoinVariableDeclaration($joinVarDecl)
|
||||
public function walkJoin($join)
|
||||
{
|
||||
foreach ($this->_walkers as $walker) {
|
||||
$walker->walkJoinVariableDeclaration($joinVarDecl);
|
||||
$walker->walkJoin($join);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,13 @@ namespace Doctrine\Tests\ORM\Functional;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser,
|
||||
Doctrine\Tests\Models\CMS\CmsArticle,
|
||||
Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\CMS\CmsArticle;
|
||||
|
||||
require_once __DIR__ . '/../../TestInit.php';
|
||||
|
||||
/**
|
||||
@ -713,4 +712,72 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
$this->assertInstanceOf('\Doctrine\ORM\NonUniqueResultException', $exc);
|
||||
}
|
||||
}
|
||||
|
||||
public function testMultipleJoinComponentsUsingInnerJoin()
|
||||
{
|
||||
$userA = new CmsUser;
|
||||
$userA->name = 'Benjamin';
|
||||
$userA->username = 'beberlei';
|
||||
$userA->status = 'developer';
|
||||
|
||||
$phonenumberA = new CmsPhonenumber;
|
||||
$phonenumberA->phonenumber = '111111';
|
||||
$userA->addPhonenumber($phonenumberA);
|
||||
|
||||
$userB = new CmsUser;
|
||||
$userB->name = 'Alexander';
|
||||
$userB->username = 'asm89';
|
||||
$userB->status = 'developer';
|
||||
|
||||
$this->_em->persist($userA);
|
||||
$this->_em->persist($userB);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery("
|
||||
SELECT u, p
|
||||
FROM Doctrine\Tests\Models\CMS\CmsUser u
|
||||
INNER JOIN Doctrine\Tests\Models\CMS\CmsPhonenumber p WITH u = p.user
|
||||
");
|
||||
$users = $query->execute();
|
||||
|
||||
$this->assertEquals(2, count($users));
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $users[1]);
|
||||
}
|
||||
|
||||
public function testMultipleJoinComponentsUsingLeftJoin()
|
||||
{
|
||||
$userA = new CmsUser;
|
||||
$userA->name = 'Benjamin';
|
||||
$userA->username = 'beberlei';
|
||||
$userA->status = 'developer';
|
||||
|
||||
$phonenumberA = new CmsPhonenumber;
|
||||
$phonenumberA->phonenumber = '111111';
|
||||
$userA->addPhonenumber($phonenumberA);
|
||||
|
||||
$userB = new CmsUser;
|
||||
$userB->name = 'Alexander';
|
||||
$userB->username = 'asm89';
|
||||
$userB->status = 'developer';
|
||||
|
||||
$this->_em->persist($userA);
|
||||
$this->_em->persist($userB);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$query = $this->_em->createQuery("
|
||||
SELECT u, p
|
||||
FROM Doctrine\Tests\Models\CMS\CmsUser u
|
||||
LEFT JOIN Doctrine\Tests\Models\CMS\CmsPhonenumber p WITH u = p.user
|
||||
");
|
||||
$users = $query->execute();
|
||||
|
||||
$this->assertEquals(4, count($users));
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[0]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsPhonenumber', $users[1]);
|
||||
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[2]);
|
||||
$this->assertNull($users[3]);
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +206,11 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertValidDQL('SELECT u.name, a.topic, p.phonenumber FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a LEFT JOIN u.phonenumbers p');
|
||||
}
|
||||
|
||||
public function testJoinClassPath()
|
||||
{
|
||||
$this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsArticle a WITH a.user = u.id');
|
||||
}
|
||||
|
||||
public function testOrderBySingleColumn()
|
||||
{
|
||||
$this->assertValidDQL('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.name');
|
||||
|
@ -135,6 +135,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsJoinOnMultipleComponents()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN Doctrine\Tests\Models\CMS\CmsPhonenumber p WITH u = p.user',
|
||||
'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.phonenumber AS phonenumber4 FROM cms_users c0_ INNER JOIN cms_phonenumbers c1_ ON (c0_.id = c1_.user_id)'
|
||||
);
|
||||
}
|
||||
|
||||
public function testSupportsSelectWithCollectionAssociationJoin()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
|
Loading…
x
Reference in New Issue
Block a user