Added Alias Syntax for joinWith()

Add alias syntax to joinWith(), e.g. joinWith('author a').
No need to know the table name for defining an alias for the relation.

fixes #2377, alternative to #8788, which allows later implementation of
getting alias and column name ambiguation.

depends on #10813 to be merged first.
This commit is contained in:
Carsten Brandt
2016-02-11 18:31:51 +01:00
parent 157d6c79cc
commit 5f19e7aa41
4 changed files with 173 additions and 6 deletions

View File

@@ -63,6 +63,7 @@ Yii Framework 2 Change Log
- Bug #10760: `yii\widgets\DetailView::normalizeAttributes()` fixed for arrayable models (boehsermoe)
- Bug: Fixed generation of canonical URLs for `ViewAction` pages (samdark)
- Bug: Fixed `mb_*` functions calls to use `UTF-8` or `Yii::$app->charset` (silverfire)
- Enh #2377: Allow specifying a table alias when joining relations via `joinWith()` (cebe, nainoon)
- Enh #3506: Added `yii\validators\IpValidator` to perform validation of IP addresses and subnets (SilverFire, samdark)
- Enh #4972: Added `yii\db\ActiveQuery::alias()` to allow specifying a table alias for the model table without having to know the name (cebe, stepanselyuk)
- Enh #5146: Added `yii\i18n\Formatter::asDuration()` method (nineinchnick, SilverFire)

View File

@@ -355,11 +355,19 @@ class ActiveQuery extends Query implements ActiveQueryInterface
* This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement
* for the primary table. And when `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
*
* @param string|array $with the relations to be joined. Each array element represents a single relation.
* The array keys are relation names, and the array values are the corresponding anonymous functions that
* can be used to modify the relation queries on-the-fly. If a relation query does not need modification,
* you may use the relation name as the array value. Sub-relations can also be specified (see [[with()]]).
* For example,
* @param string|array $with the relations to be joined. This can either be a string, representing a relation name or
* an array with the following semantics:
*
* - Each array element represents a single relation.
* - You may specify the relation name as the array key and provide an anonymous functions that
* can be used to modify the relation queries on-the-fly as the array value.
* - If a relation query does not need modification, you may use the relation name as the array value.
*
* The relation name may optionally contain an alias for the relation table (e.g. `books b`).
*
* Sub-relations can also be specified, see [[with()]] for the syntax.
*
* In the following you find some examples:
*
* ```php
* // find all orders that contain books, and eager loading "books"
@@ -370,8 +378,12 @@ class ActiveQuery extends Query implements ActiveQueryInterface
* $query->orderBy('item.name');
* }
* ])->all();
* // find all orders that contain books of the category 'Science fiction', using the alias "b" for the books table
* Order::find()->joinWith(['books b'], true, 'INNER JOIN')->where(['b.category' => 'Science fiction'])->all();
* ```
*
* The alias syntax is available since version 2.0.7.
*
* @param boolean|array $eagerLoading whether to eager load the relations specified in `$with`.
* When this is a boolean, it applies to all relations specified in `$with`. Use an array
* to explicitly list which relations in `$with` need to be eagerly loaded. Defaults to `true`.
@@ -382,8 +394,33 @@ class ActiveQuery extends Query implements ActiveQueryInterface
*/
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
{
$this->joinWith[] = [(array) $with, $eagerLoading, $joinType];
$relations = [];
foreach((array) $with as $name => $callback) {
if (is_int($name)) {
$name = $callback;
$callback = null;
}
if (preg_match('/^(.*?)(?:\s+AS\s+|\s+)(\w+)$/i', $name, $matches)) {
// relation is defined with an alias, adjust callback to apply alias
list(, $relation, $alias) = $matches;
$name = $relation;
$callback = function($query) use ($callback, $alias) {
/** @var $query ActiveQuery */
$query->alias($alias);
if ($callback !== null) {
call_user_func($callback, $query);
}
};
}
if ($callback === null) {
$relations[] = $name;
} else {
$relations[$name] = $callback;
}
}
$this->joinWith[] = [$relations, $eagerLoading, $joinType];
return $this;
}