diff --git a/src/Query.php b/src/Query.php index 8754a5f..38f7b25 100644 --- a/src/Query.php +++ b/src/Query.php @@ -405,6 +405,9 @@ public function assembleSelect() ) ); + // Needs to be reset before we can begin to resolve the actual model relations. + $resolver->resetKnownRelations(); + foreach ($this->getWith() as $relation) { $select->columns( $resolver->qualifyColumnsAndAliases( @@ -447,7 +450,8 @@ public function assembleSelect() $joinedRelations = []; foreach ($this->getWith() + $this->getUtilize() as $path => $_) { foreach ($resolver->resolveRelations($path) as $relationPath => $relation) { - if (isset($joinedRelations[$relationPath])) { + $tableName = $relation->getTarget()->getTableName(); + if (isset($joinedRelations[$tableName])) { continue; } @@ -458,6 +462,12 @@ public function assembleSelect() $sourceAlias = $resolver->getAlias($source); $targetAlias = $resolver->getAlias($target); + if (isset($joinedRelations[$source->getTableName()])) { + // When this relation is already joined, we only need to use + // it's alias instead of generating a new one + $sourceAlias = $joinedRelations[$source->getTableName()]; + } + $conditions = []; foreach ($relatedKeys as $fk => $ck) { $conditions[] = sprintf( @@ -484,7 +494,7 @@ public function assembleSelect() } } - $joinedRelations[$relationPath] = true; + $joinedRelations[$tableName] = $resolver->getAlias($relation->getTarget()); } } diff --git a/src/Resolver.php b/src/Resolver.php index fbeeb31..bec2268 100644 --- a/src/Resolver.php +++ b/src/Resolver.php @@ -46,6 +46,9 @@ class Resolver /** @var SplObjectStorage Resolved relations */ protected $resolvedRelations; + /** @var array Known and already resolved/joined relations */ + protected $knownRelations; + /** * Create a new resolver */ @@ -92,6 +95,20 @@ public function getRelations(Model $model) return $this->relations[$model]; } + /** + * Reset this model's known relations + * + * Should always be called before resolving this query's with[] relations + * + * @return $this + */ + public function resetKnownRelations() + { + $this->knownRelations = []; + + return $this; + } + /** * Get a model's behaviors * @@ -339,6 +356,11 @@ public function qualifyColumnsAndAliases($columns, Model $model = null, $autoAli ); } + $knownTargetAlias = null; + if (isset($this->knownRelations[$target->getTableName()])) { + $knownTargetAlias = $this->knownRelations[$target->getTableName()]; + } + $qualified = []; foreach ($columns as $alias => $column) { if (is_int($alias) && is_array($column)) { @@ -346,6 +368,10 @@ public function qualifyColumnsAndAliases($columns, Model $model = null, $autoAli list($target, $alias, $columnName) = $column; $targetAlias = $this->getAlias($target); + if (isset($this->knownRelations[$target->getTableName()])) { + $knownTargetAlias = $this->knownRelations[$target->getTableName()]; + } + // Thanks to PHP 5.6 where `list` is evaluated from right to left. It will extract // the values for `$target` and `$alias` then from the third argument (`$column`). $column = $columnName; @@ -375,12 +401,20 @@ public function qualifyColumnsAndAliases($columns, Model $model = null, $autoAli $column = clone $column; // The expression may be part of a model and those shouldn't change implicitly $column->setColumns($this->qualifyColumns($column->getColumns(), $target)); } else { - $column = $this->qualifyColumn($column, $targetAlias); + if (! $knownTargetAlias) { + $knownTargetAlias = $targetAlias; + } + + $column = $this->qualifyColumn($column, $knownTargetAlias); } $qualified[$alias] = $column; } + if (! isset($this->knownRelations[$target->getTableName()])) { + $this->knownRelations[$target->getTableName()] = $targetAlias; + } + return $qualified; }