mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-11 19:20:01 +08:00
Added ActiveQuery::innerJoinWith().
This commit is contained in:
@@ -391,35 +391,26 @@ Joining with Relations
|
|||||||
|
|
||||||
When working with relational databases, a common task is to join multiple tables and apply various
|
When working with relational databases, a common task is to join multiple tables and apply various
|
||||||
query conditions and parameters to the JOIN SQL statement. Instead of calling [[ActiveQuery::join()]]
|
query conditions and parameters to the JOIN SQL statement. Instead of calling [[ActiveQuery::join()]]
|
||||||
explicitly to build up the JOIN query, you may reuse the existing relation definitions and call [[ActiveQuery::joinWith()]]
|
explicitly to build up the JOIN query, you may reuse the existing relation definitions and call
|
||||||
to achieve the same goal. For example,
|
[[ActiveQuery::joinWith()]] to achieve this goal. For example,
|
||||||
|
|
||||||
```php
|
```php
|
||||||
|
// find all orders and sort the orders by the customer id and the order id. also eager loading "customer"
|
||||||
|
$orders = Order::find()->joinWith('customer')->orderBy('tbl_customer.id, tbl_order.id')->all();
|
||||||
// find all orders that contain books, and eager loading "books"
|
// find all orders that contain books, and eager loading "books"
|
||||||
$orders = Order::find()->joinWith('books')->all();
|
$orders = Order::find()->innerJoinWith('books')->all();
|
||||||
// find all orders that contain books, and sort the orders by the book names.
|
|
||||||
$orders = Order::find()->joinWith([
|
|
||||||
'books' => function ($query) {
|
|
||||||
$query->orderBy('tbl_item.id');
|
|
||||||
}
|
|
||||||
])->all();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that [[ActiveQuery::joinWith()]] differs from [[ActiveQuery::with()]] in that the former will build up
|
In the above, the method [[ActiveQuery::innerJoinWith()|innerJoinWith()]] is a shortcut to [[ActiveQuery::joinWith()|joinWith()]]
|
||||||
and execute a JOIN SQL statement for the primary model class. For example, `Order::find()->joinWith('books')->all()`
|
with the join type set as `INNER JOIN`.
|
||||||
returns all orders that contain books, while `Order::find()->with('books')->all()` returns all orders
|
|
||||||
regardless they contain books or not.
|
|
||||||
|
|
||||||
Because `joinWith()` will cause generating a JOIN SQL statement, you are responsible to disambiguate column
|
You may join with one or multiple relations; you may apply query conditions to the relations on-the-fly;
|
||||||
names. For example, we use `tbl_item.id` to disambiguate the `id` column reference because both of the order table
|
and you may also join with sub-relations. For example,
|
||||||
and the item table contain a column named `id`.
|
|
||||||
|
|
||||||
You may join with one or multiple relations. You may also join with sub-relations. For example,
|
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// join with multiple relations
|
// join with multiple relations
|
||||||
// find out the orders that contain books and are placed by customers who registered within the past 24 hours
|
// find out the orders that contain books and are placed by customers who registered within the past 24 hours
|
||||||
$orders = Order::find()->joinWith([
|
$orders = Order::find()->innerJoinWith([
|
||||||
'books',
|
'books',
|
||||||
'customer' => function ($query) {
|
'customer' => function ($query) {
|
||||||
$query->where('tbl_customer.create_time > ' . (time() - 24 * 3600));
|
$query->where('tbl_customer.create_time > ' . (time() - 24 * 3600));
|
||||||
@@ -429,23 +420,37 @@ $orders = Order::find()->joinWith([
|
|||||||
$orders = Order::find()->joinWith('books.author')->all();
|
$orders = Order::find()->joinWith('books.author')->all();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Behind the scene, Yii will first execute a JOIN SQL statement to bring back the primary models
|
||||||
|
satisfying the conditions applied to the JOIN SQL. It will then execute a query for each relation
|
||||||
|
and populate the corresponding related records.
|
||||||
|
|
||||||
|
The difference between [[ActiveQuery::joinWith()|joinWith()]] and [[ActiveQuery::with()|with()]] is that
|
||||||
|
the former joins the tables for the primary model class and the related model classes to retrieve
|
||||||
|
the primary models, while the latter just queries against the table for the primary model class to
|
||||||
|
retrieve the primary models.
|
||||||
|
|
||||||
|
Because of this difference, you may apply query conditions that are only available to a JOIN SQL statement.
|
||||||
|
For example, you may filter the primary models by the conditions on the related models, like the example
|
||||||
|
above. You may also sort the primary models using columns from the related tables.
|
||||||
|
|
||||||
|
When using [[ActiveQuery::joinWith()|joinWith()]], you are responsible to disambiguate column names.
|
||||||
|
In the above examples, we use `tbl_item.id` and `tbl_order.id` to disambiguate the `id` column references
|
||||||
|
because both of the order table and the item table contain a column named `id`.
|
||||||
|
|
||||||
By default, when you join with a relation, the relation will also be eagerly loaded. You may change this behavior
|
By default, when you join with a relation, the relation will also be eagerly loaded. You may change this behavior
|
||||||
by passing the `$eagerLoading` parameter which specifies whether to eager load the specified relations.
|
by passing the `$eagerLoading` parameter which specifies whether to eager load the specified relations.
|
||||||
|
|
||||||
Also, when the relations are joined with the primary table, the default join type is `INNER JOIN`. You may change
|
And also by default, [[ActiveQuery::joinWith()|joinWith()]] uses `LEFT JOIN` to join the related tables.
|
||||||
to use other type of joins, such as `LEFT JOIN`.
|
You may pass it with the `$joinType` parameter to customize the join type. As a shortcut to the `INNER JOIN` type,
|
||||||
|
you may use [[ActiveQuery::innerJoinWith()|innerJoinWith()]].
|
||||||
|
|
||||||
Below are some more examples,
|
Below are some more examples,
|
||||||
|
|
||||||
```php
|
```php
|
||||||
// find all orders that contain books, but do not eager loading "books".
|
// find all orders that contain books, but do not eager loading "books".
|
||||||
$orders = Order::find()->joinWith('books', false)->all();
|
$orders = Order::find()->innerJoinWith('books', false)->all();
|
||||||
// find all orders and sort them by the customer IDs. Do not eager loading "customer".
|
// equivalent to the above
|
||||||
$orders = Order::find()->joinWith([
|
$orders = Order::find()->joinWith('books', false, 'INNER JOIN')->all();
|
||||||
'customer' => function ($query) {
|
|
||||||
$query->orderBy('tbl_customer.id');
|
|
||||||
},
|
|
||||||
], false, 'LEFT JOIN')->all();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ Yii Framework 2 Change Log
|
|||||||
- Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code)
|
- Enh #1552: It is now possible to use multiple bootstrap NavBar in a single page (Alex-Code)
|
||||||
- Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
|
- Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
|
||||||
- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
|
- Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
|
||||||
- Enh #1581: Added `ActiveQuery::joinWith()` to support joining with relations (qiangxue)
|
- Enh #1581: Added `ActiveQuery::joinWith()` and `ActiveQuery::innerJoinWith()` to support joining with relations (qiangxue)
|
||||||
- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
|
- Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
|
||||||
- Enh: Added `favicon.ico` and `robots.txt` to defauly application templates (samdark)
|
- Enh: Added `favicon.ico` and `robots.txt` to defauly application templates (samdark)
|
||||||
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
|
- Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
|
||||||
|
|||||||
@@ -200,8 +200,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
|||||||
*
|
*
|
||||||
* Note that because a JOIN query will be performed, you are responsible to disambiguate column names.
|
* Note that because a JOIN query will be performed, you are responsible to disambiguate column names.
|
||||||
*
|
*
|
||||||
* This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement.
|
* This method differs from [[with()]] in that it will build up and execute a JOIN SQL statement
|
||||||
* When `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
|
* for the primary table. And when `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
|
||||||
*
|
*
|
||||||
* @param array $with the relations to be joined. Each array element represents a single relation.
|
* @param 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
|
* The array keys are relation names, and the array values are the corresponding anonymous functions that
|
||||||
@@ -211,8 +211,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
|||||||
*
|
*
|
||||||
* ```php
|
* ```php
|
||||||
* // find all orders that contain books, and eager loading "books"
|
* // find all orders that contain books, and eager loading "books"
|
||||||
* Order::find()->joinWith('books')->all();
|
* Order::find()->joinWith('books', true, 'INNER JOIN')->all();
|
||||||
* // find all orders that contain books, and sort the orders by the book names.
|
* // find all orders, eager loading "books", and sort the orders and books by the book names.
|
||||||
* Order::find()->joinWith([
|
* Order::find()->joinWith([
|
||||||
* 'books' => function ($query) {
|
* 'books' => function ($query) {
|
||||||
* $query->orderBy('tbl_item.name');
|
* $query->orderBy('tbl_item.name');
|
||||||
@@ -228,7 +228,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
|||||||
* in the format of `relationName => joinType` to specify different join types for different relations.
|
* in the format of `relationName => joinType` to specify different join types for different relations.
|
||||||
* @return static the query object itself
|
* @return static the query object itself
|
||||||
*/
|
*/
|
||||||
public function joinWith($with, $eagerLoading = true, $joinType = 'INNER JOIN')
|
public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
|
||||||
{
|
{
|
||||||
$with = (array)$with;
|
$with = (array)$with;
|
||||||
$this->joinWithRelations(new $this->modelClass, $with, $joinType);
|
$this->joinWithRelations(new $this->modelClass, $with, $joinType);
|
||||||
@@ -250,6 +250,20 @@ class ActiveQuery extends Query implements ActiveQueryInterface
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inner joins with the specified relations.
|
||||||
|
* This is a shortcut method to [[joinWith()]] with the join type set as "INNER JOIN".
|
||||||
|
* Please refer to [[joinWith()]] for detailed usage of this method.
|
||||||
|
* @param array $with the relations to be joined with
|
||||||
|
* @param boolean|array $eagerLoading whether to eager loading the relations
|
||||||
|
* @return static the query object itself
|
||||||
|
* @see joinWith()
|
||||||
|
*/
|
||||||
|
public function innerJoinWith($with, $eagerLoading = true)
|
||||||
|
{
|
||||||
|
return $this->joinWith($with, $eagerLoading, 'INNER JOIN');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modifies the current query by adding join fragments based on the given relations.
|
* Modifies the current query by adding join fragments based on the given relations.
|
||||||
* @param ActiveRecord $model the primary model
|
* @param ActiveRecord $model the primary model
|
||||||
|
|||||||
@@ -220,8 +220,18 @@ class ActiveRecordTest extends DatabaseTestCase
|
|||||||
|
|
||||||
public function testJoinWith()
|
public function testJoinWith()
|
||||||
{
|
{
|
||||||
|
// left join and eager loading
|
||||||
|
$orders = Order::find()->joinWith('customer')->orderBy('tbl_customer.id DESC, tbl_order.id')->all();
|
||||||
|
$this->assertEquals(3, count($orders));
|
||||||
|
$this->assertEquals(2, $orders[0]->id);
|
||||||
|
$this->assertEquals(3, $orders[1]->id);
|
||||||
|
$this->assertEquals(1, $orders[2]->id);
|
||||||
|
$this->assertTrue($orders[0]->isRelationPopulated('customer'));
|
||||||
|
$this->assertTrue($orders[1]->isRelationPopulated('customer'));
|
||||||
|
$this->assertTrue($orders[2]->isRelationPopulated('customer'));
|
||||||
|
|
||||||
// inner join filtering and eager loading
|
// inner join filtering and eager loading
|
||||||
$orders = Order::find()->joinWith([
|
$orders = Order::find()->innerJoinWith([
|
||||||
'customer' => function ($query) {
|
'customer' => function ($query) {
|
||||||
$query->where('tbl_customer.id=2');
|
$query->where('tbl_customer.id=2');
|
||||||
},
|
},
|
||||||
@@ -233,7 +243,7 @@ class ActiveRecordTest extends DatabaseTestCase
|
|||||||
$this->assertTrue($orders[1]->isRelationPopulated('customer'));
|
$this->assertTrue($orders[1]->isRelationPopulated('customer'));
|
||||||
|
|
||||||
// inner join filtering without eager loading
|
// inner join filtering without eager loading
|
||||||
$orders = Order::find()->joinWith([
|
$orders = Order::find()->innerJoinWith([
|
||||||
'customer' => function ($query) {
|
'customer' => function ($query) {
|
||||||
$query->where('tbl_customer.id=2');
|
$query->where('tbl_customer.id=2');
|
||||||
},
|
},
|
||||||
@@ -245,7 +255,7 @@ class ActiveRecordTest extends DatabaseTestCase
|
|||||||
$this->assertFalse($orders[1]->isRelationPopulated('customer'));
|
$this->assertFalse($orders[1]->isRelationPopulated('customer'));
|
||||||
|
|
||||||
// join with via-relation
|
// join with via-relation
|
||||||
$orders = Order::find()->joinWith('books')->orderBy('tbl_order.id')->all();
|
$orders = Order::find()->innerJoinWith('books')->orderBy('tbl_order.id')->all();
|
||||||
$this->assertEquals(2, count($orders));
|
$this->assertEquals(2, count($orders));
|
||||||
$this->assertEquals(1, $orders[0]->id);
|
$this->assertEquals(1, $orders[0]->id);
|
||||||
$this->assertEquals(3, $orders[1]->id);
|
$this->assertEquals(3, $orders[1]->id);
|
||||||
@@ -255,7 +265,7 @@ class ActiveRecordTest extends DatabaseTestCase
|
|||||||
$this->assertEquals(1, count($orders[1]->books));
|
$this->assertEquals(1, count($orders[1]->books));
|
||||||
|
|
||||||
// join with sub-relation
|
// join with sub-relation
|
||||||
$orders = Order::find()->joinWith([
|
$orders = Order::find()->innerJoinWith([
|
||||||
'items.category' => function ($q) {
|
'items.category' => function ($q) {
|
||||||
$q->where('tbl_category.id = 2');
|
$q->where('tbl_category.id = 2');
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user