Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updated PoC for multiple components DQL support.

  • Loading branch information...
commit 41a650b699f2761b7b43c51d47d2c060c6004b0a 1 parent e7dfa08
@guilhermeblanco guilhermeblanco authored beberlei committed
View
7 UPGRADE_TO_2_3
@@ -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.
View
6 lib/Doctrine/ORM/Query/AST/IdentificationVariableDeclaration.php
@@ -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)
View
12 lib/Doctrine/ORM/Query/AST/Join.php
@@ -25,28 +25,26 @@
* 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 $joinPathExpression = null;
+ public $joinAssociationDeclaration = null;
public $conditionalExpression = null;
- public function __construct($joinType, $joinPathExpr)
+ public function __construct($joinType, $joinAssociationDeclaration)
{
$this->joinType = $joinType;
- $this->joinAssociationPathExpression = $joinPathExpr;
+ $this->joinAssociationDeclaration = $joinAssociationDeclaration;
}
public function dispatch($sqlWalker)
View
29 ...octrine/ORM/Query/AST/JoinVariableDeclaration.php → ...rine/ORM/Query/AST/JoinAssociationDeclaration.php
@@ -15,36 +15,39 @@
* 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
@yethee
yethee added a note

Artifact of merge

@guilhermeblanco Owner

Fixed in 2703a2b

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
*/
namespace Doctrine\ORM\Query\AST;
/**
- * JoinVariableDeclaration ::= Join [IndexBy]
+ * JoinAssociationDeclaration ::= JoinAssociationPathExpression ["AS"] AliasIdentificationVariable
*
- *
* @link www.doctrine-project.org
- * @since 2.0
- * @version $Revision: 3938 $
+ * @since 2.3
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
- * @author Jonathan Wage <jonwage@gmail.com>
- * @author Roman Borschel <roman@code-factory.org>
*/
-class JoinVariableDeclaration extends Node
+class JoinAssociationDeclaration extends Node
{
- public $join = null;
- public $indexBy = null;
+ public $joinAssociationPathExpression;
+ public $aliasIdentificationVariable;
+ public $indexBy;
- public function __construct($join, $indexBy)
+ public function __construct($joinAssociationPathExpression, $aliasIdentificationVariable, $indexBy)
{
- $this->join = $join;
- $this->indexBy = $indexBy;
+ $this->joinAssociationPathExpression = $joinAssociationPathExpression;
+ $this->aliasIdentificationVariable = $aliasIdentificationVariable;
+ $this->indexBy = $indexBy;
}
public function dispatch($sqlWalker)
{
- return $sqlWalker->walkJoinVariableDeclaration($this);
+ return $sqlWalker->walkJoinAssociationDeclaration($this);
}
}
View
10 lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php
@@ -24,10 +24,8 @@
/**
* 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>
@@ -36,17 +34,15 @@ class JoinAssociationPathExpression extends Node
{
public $identificationVariable;
public $associationField;
- public $aliasIdentificationVariable = null;
- public function __construct($identificationVariable, $associationField, $aliasIdentVar)
+ public function __construct($identificationVariable, $associationField)
{
$this->identificationVariable = $identificationVariable;
- $this->associationField = $associationField;
- $this->aliasIdentificationVariable = $aliasIdentVar;
+ $this->associationField = $associationField;
}
public function dispatch($sqlWalker)
{
- return $sqlWalker->walkJoinPathExpression($this);
+ return $sqlWalker->walkPathExpression($this);
}
}
View
210 lib/Doctrine/ORM/Query/Parser.php
@@ -909,58 +909,7 @@ public function JoinAssociationPathExpression()
$this->semanticalError('Class ' . $class->name . ' has no association named ' . $field);
}
- if ($this->_lexer->isNextToken(Lexer::T_AS)) {
- $this->match(Lexer::T_AS);
- }
-
- $token = $this->_lexer->lookahead;
- $aliasIdentificationVariable = $this->AliasIdentificationVariable();
-
- // Building queryComponent
- $joinQueryComponent = array(
- 'metadata' => $this->_em->getClassMetadata($class->associationMappings[$field]['targetEntity']),
- 'parent' => $identVariable,
- 'relation' => $class->getAssociationMapping($field),
- 'map' => null,
- 'nestingLevel' => $this->_nestingLevel,
- 'token' => $this->_lexer->lookahead
- );
-
- $this->_queryComponents[$aliasIdentificationVariable] = $joinQueryComponent;
-
- return new AST\JoinAssociationPathExpression($identVariable, $field, $aliasIdentificationVariable);
- }
-
- /**
- * JoinClassPathExpression ::= Class alias
- *
- * @return \Doctrine\ORM\Query\AST\JoinClassPathExpression
- */
- public function JoinClassPathExpression()
- {
- $abstractSchemaName = $this->AbstractSchemaName();
-
- if ($this->_lexer->isNextToken(Lexer::T_AS)) {
- $this->match(Lexer::T_AS);
- }
-
- $token = $this->_lexer->lookahead;
- $aliasIdentificationVariable = $this->AliasIdentificationVariable();
- $classMetadata = $this->_em->getClassMetadata($abstractSchemaName);
-
- // Building queryComponent
- $queryComponent = array(
- 'metadata' => $classMetadata,
- 'parent' => null,
- 'relation' => null,
- 'map' => null,
- 'nestingLevel' => $this->_nestingLevel,
- 'token' => $token
- );
-
- $this->_queryComponents[$aliasIdentificationVariable] = $queryComponent;
-
- return new AST\JoinClassPathExpression($abstractSchemaName, $aliasIdentificationVariable);
+ return new AST\JoinAssociationPathExpression($identVariable, $field);
}
/**
@@ -1452,7 +1401,7 @@ public function NewValue()
}
/**
- * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
+ * IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
*
* @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
*/
@@ -1460,18 +1409,18 @@ public function IdentificationVariableDeclaration()
{
$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
);
}
@@ -1501,16 +1450,57 @@ public function SubselectIdentificationVariableDeclaration()
}
/**
- * 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;
+
+ 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);
- return new AST\JoinVariableDeclaration($join, $indexBy);
+ $join->conditionalExpression = $this->ConditionalExpression();
+ }
+
+ return $join;
}
/**
@@ -1546,6 +1536,43 @@ public function RangeVariableDeclaration()
}
/**
+ * 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}* "}"
*
@@ -1587,65 +1614,6 @@ public function 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);
-
- $next = $this->_lexer->glimpse();
- if ($next['type'] === Lexer::T_DOT) {
- $joinPathExpression = $this->JoinAssociationPathExpression();
- } else {
- $joinPathExpression = $this->JoinClassPathExpression();
-
- if (!$this->_lexer->isNextToken(Lexer::T_WITH)) {
- $this->syntaxError('WITH');
- }
- }
-
- // Create AST node
- $join = new AST\Join($joinType, $joinPathExpression);
-
- // 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
*
* @return \Doctrine\ORM\Query\AST\IndexBy
View
382 lib/Doctrine/ORM/Query/SqlWalker.php
@@ -705,23 +705,10 @@ public function walkFromClause($fromClause)
$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) {
@@ -745,6 +732,174 @@ public function walkFromClause($fromClause)
}
/**
+ * 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.
*
* @return string The SQL.
@@ -799,173 +954,35 @@ 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
* @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 ';
- $joinPathExpr = $join->joinAssociationPathExpression;
- $joinedDqlAlias = $joinPathExpr->aliasIdentificationVariable;
-
- if ($joinPathExpr instanceof \Doctrine\ORM\Query\AST\JoinClassPathExpression) {
- $targetClass = $this->_queryComponents[$joinedDqlAlias]['metadata'];
-
- $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
- $sql .= $targetClass->getQuotedTableName($this->_platform) . ' '
- . $targetTableAlias;
-
- $sql .= ' ON (' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
- } else {
-
- $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(), $joinPathExpr->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.
- 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;
- }
- }
-
- } 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);
- }
+ switch (true) {
+ case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
+ $sql .= $this->walkRangeVariableDeclaration($joinDeclaration)
+ . ' ON (' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
+ break;
- $sql .= $targetTableAlias . '.' . $quotedTargetColumn . ' = ' . $joinTableAlias . '.' . $relationColumn;
- }
- } else {
- foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
- if ( ! $first) $sql .= ' AND '; else $first = false;
+ case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
+ $sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType);
- 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;
- }
+ // 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) . ')';
}
- }
-
- // 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) . ')';
- }
- }
-
- // Apply the filters
- if ($filterExpr = $this->generateFilterConditionSQL($targetClass, $targetTableAlias)) {
- $sql .= ' AND ' . $filterExpr;
- }
-
- 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']);
- }
-
-
- $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;
@@ -1308,23 +1325,10 @@ public function walkSubselectFromClause($subselectFromClause)
$sqlParts = array ();
foreach ($identificationVarDecls as $subselectIdVarDecl) {
- $sql = '';
-
- $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);
- }
+ $sql = $this->walkRangeVariableDeclaration($subselectIdVarDecl->rangeVariableDeclaration);
- 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));
View
6 lib/Doctrine/ORM/Query/TreeWalker.php
@@ -91,12 +91,12 @@ function walkOrderByItem($orderByItem);
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.
View
6 lib/Doctrine/ORM/Query/TreeWalkerAdapter.php
@@ -125,12 +125,12 @@ public function walkOrderByItem($orderByItem) {}
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.
View
8 lib/Doctrine/ORM/Query/TreeWalkerChain.php
@@ -148,15 +148,15 @@ 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)
{
foreach ($this->_walkers as $walker) {
- $walker->walkJoinVariableDeclaration($joinVarDecl);
+ $walker->walkJoin($join);
}
}
View
5 tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php
@@ -206,6 +206,11 @@ public function testMixingOfJoins()
$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');
Something went wrong with that request. Please try again.