Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[PoC] Arbitrary join support

  • Loading branch information...
commit e7dfa08756f97ac788fc7dfbf5b8898020e67689 1 parent 27b4f58
@asm89 asm89 authored beberlei committed
View
8 lib/Doctrine/ORM/Query/AST/Join.php
@@ -40,15 +40,13 @@ class Join extends Node
const JOIN_TYPE_INNER = 3;
public $joinType = self::JOIN_TYPE_INNER;
- public $joinAssociationPathExpression = null;
- public $aliasIdentificationVariable = null;
+ public $joinPathExpression = null;
public $conditionalExpression = null;
- public function __construct($joinType, $joinAssocPathExpr, $aliasIdentVar)
+ public function __construct($joinType, $joinPathExpr)
{
$this->joinType = $joinType;
- $this->joinAssociationPathExpression = $joinAssocPathExpr;
- $this->aliasIdentificationVariable = $aliasIdentVar;
+ $this->joinAssociationPathExpression = $joinPathExpr;
}
public function dispatch($sqlWalker)
View
4 lib/Doctrine/ORM/Query/AST/JoinAssociationPathExpression.php
@@ -36,11 +36,13 @@ class JoinAssociationPathExpression extends Node
{
public $identificationVariable;
public $associationField;
+ public $aliasIdentificationVariable = null;
- public function __construct($identificationVariable, $associationField)
+ public function __construct($identificationVariable, $associationField, $aliasIdentVar)
{
$this->identificationVariable = $identificationVariable;
$this->associationField = $associationField;
+ $this->aliasIdentificationVariable = $aliasIdentVar;
}
public function dispatch($sqlWalker)
View
47 lib/Doctrine/ORM/Query/AST/JoinClassPathExpression.php
@@ -0,0 +1,47 @@
+<?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
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\ORM\Query\AST;
+
+/**
+ * JoinClassPathExpression ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
+ *
+ * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link www.doctrine-project.org
+ * @since 2.3
+ * @author Alexander <iam.asm89@gmail.com>
+ */
+class JoinClassPathExpression extends Node
+{
+ public $abstractSchemaName;
+ public $aliasIdentificationVariable;
+
+ public function __construct($abstractSchemaName, $aliasIdentificationVar)
+ {
+ $this->abstractSchemaName = $abstractSchemaName;
+ $this->aliasIdentificationVariable = $aliasIdentificationVar;
+ }
+
+ public function dispatch($walker)
+ {
+ return $sqlWalker->walkJoinPathExpression($this);
+ }
+}
View
95 lib/Doctrine/ORM/Query/Parser.php
@@ -905,11 +905,62 @@ public function JoinAssociationPathExpression()
$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);
}
- return new AST\JoinAssociationPathExpression($identVariable, $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);
}
/**
@@ -1570,41 +1621,19 @@ public function Join()
$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;
+ $next = $this->_lexer->glimpse();
+ if ($next['type'] === Lexer::T_DOT) {
+ $joinPathExpression = $this->JoinAssociationPathExpression();
+ } else {
+ $joinPathExpression = $this->JoinClassPathExpression();
- if ( ! $parentClass->hasAssociation($assocField)) {
- $this->semanticalError(
- "Class " . $parentClass->name . " has no association named '$assocField'."
- );
+ if (!$this->_lexer->isNextToken(Lexer::T_WITH)) {
+ $this->syntaxError('WITH');
+ }
}
- $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);
+ $join = new AST\Join($joinType, $joinPathExpression);
// Check for ad-hoc Join conditions
if ($this->_lexer->isNextToken(Lexer::T_WITH)) {
View
212 lib/Doctrine/ORM/Query/SqlWalker.php
@@ -812,133 +812,133 @@ public function walkJoinVariableDeclaration($joinVarDecl)
? ' 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
- );
- }
+ $joinPathExpr = $join->joinAssociationPathExpression;
+ $joinedDqlAlias = $joinPathExpr->aliasIdentificationVariable;
- $joinAssocPathExpr = $join->joinAssociationPathExpression;
- $joinedDqlAlias = $join->aliasIdentificationVariable;
+ if ($joinPathExpr instanceof \Doctrine\ORM\Query\AST\JoinClassPathExpression) {
+ $targetClass = $this->_queryComponents[$joinedDqlAlias]['metadata'];
- $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);
+ $sql .= $targetClass->getQuotedTableName($this->_platform) . ' '
+ . $targetTableAlias;
- $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']);
- }
+ $sql .= ' ON (' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
+ } else {
- // 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;
+ $relation = $this->_queryComponents[$joinedDqlAlias]['relation'];
+ $targetClass = $this->_em->getClassMetadata($relation['targetEntity']);
+ $sourceClass = $this->_em->getClassMetadata($relation['sourceEntity']);
+ $targetTableName = $targetClass->getQuotedTableName($this->_platform);
- foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
- if ( ! $first) $sql .= ' AND '; else $first = false;
+ $targetTableAlias = $this->getSQLTableAlias($targetClass->getTableName(), $joinedDqlAlias);
+ $sourceTableAlias = $this->getSQLTableAlias($sourceClass->getTableName(), $joinPathExpr->identificationVariable);
- 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;
+ // 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);
}
}
- } 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 ';
+ // 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;
- $first = true;
- if ($relation['isOwningSide']) {
- foreach ($assoc['relationToSourceKeyColumns'] as $relationColumn => $sourceColumn) {
+ foreach ($assoc['sourceToTargetKeyColumns'] as $sourceColumn => $targetColumn) {
if ( ! $first) $sql .= ' AND '; else $first = false;
- if ($sourceClass->containsForeignIdentifier && !isset($sourceClass->fieldNames[$sourceColumn])) {
- $quotedTargetColumn = $sourceColumn; // Join columns cannot be quoted.
+ 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 {
- $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$sourceColumn], $this->_platform);
+ 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;
}
-
- $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);
+ } 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;
+ $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 ';
+ // 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;
+ $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);
+ 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;
- $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);
+ }
- 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;
}
-
- $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) . ')';
+ }
}
// Apply the filters
@@ -946,13 +946,17 @@ public function walkJoinVariableDeclaration($joinVarDecl)
$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) . ')';
+ 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) {
View
8 tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php
@@ -135,6 +135,14 @@ public function testSupportsSelectUsingMultipleFromComponents()
);
}
+ 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(
Please sign in to comment.
Something went wrong with that request. Please try again.