Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
03451912
Commit
03451912
authored
Dec 24, 2013
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added ActiveQuery::innerJoinWith().
parent
dc720d9b
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
67 additions
and
38 deletions
+67
-38
active-record.md
docs/guide/active-record.md
+33
-28
CHANGELOG.md
framework/CHANGELOG.md
+1
-1
ActiveQuery.php
framework/yii/db/ActiveQuery.php
+19
-5
ActiveRecordTest.php
tests/unit/framework/db/ActiveRecordTest.php
+14
-4
No files found.
docs/guide/active-record.md
View file @
03451912
...
@@ -391,35 +391,26 @@ Joining with Relations
...
@@ -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
names. For example, we use
`tbl_item.id`
to disambiguate the
`id`
column reference because both of the order table
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,
You may join with one or multiple relations; you may apply query conditions to the relations on-the-fly;
and 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
()
->
j
oinWith
([
$orders
=
Order
::
find
()
->
innerJ
oinWith
([
'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([
...
@@ -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
();
```
```
...
...
framework/CHANGELOG.md
View file @
03451912
...
@@ -27,7 +27,7 @@ Yii Framework 2 Change Log
...
@@ -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)
...
...
framework/yii/db/ActiveQuery.php
View file @
03451912
...
@@ -200,8 +200,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface
...
@@ -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
*
W
hen `$eagerLoading` is true, it will call [[with()]] in addition with the specified relations.
*
for the primary table. And w
hen `$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
...
@@ -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 order
s by the book names.
* // find all orders
, eager loading "books", and sort the orders and book
s 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
...
@@ -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
);
...
@@ -251,6 +251,20 @@ class ActiveQuery extends Query implements ActiveQueryInterface
...
@@ -251,6 +251,20 @@ class ActiveQuery extends Query implements ActiveQueryInterface
}
}
/**
/**
* 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
* @param array $with the relations to be joined
* @param array $with the relations to be joined
...
...
tests/unit/framework/db/ActiveRecordTest.php
View file @
03451912
...
@@ -220,8 +220,18 @@ class ActiveRecordTest extends DatabaseTestCase
...
@@ -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
()
->
j
oinWith
([
$orders
=
Order
::
find
()
->
innerJ
oinWith
([
'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
...
@@ -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
()
->
j
oinWith
([
$orders
=
Order
::
find
()
->
innerJ
oinWith
([
'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
...
@@ -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
()
->
j
oinWith
(
'books'
)
->
orderBy
(
'tbl_order.id'
)
->
all
();
$orders
=
Order
::
find
()
->
innerJ
oinWith
(
'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
...
@@ -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
()
->
j
oinWith
([
$orders
=
Order
::
find
()
->
innerJ
oinWith
([
'items.category'
=>
function
(
$q
)
{
'items.category'
=>
function
(
$q
)
{
$q
->
where
(
'tbl_category.id = 2'
);
$q
->
where
(
'tbl_category.id = 2'
);
},
},
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment