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
c4c328dc
Commit
c4c328dc
authored
Jan 06, 2014
by
Qiang Xue
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fixes #1791: support ON condition for relational query.
parent
a70808f8
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
147 additions
and
27 deletions
+147
-27
active-record.md
docs/guide/active-record.md
+36
-1
ActiveQuery.php
framework/yii/db/ActiveQuery.php
+4
-1
ActiveRelation.php
framework/yii/db/ActiveRelation.php
+66
-25
Order.php
tests/unit/data/ar/Order.php
+7
-0
ActiveRecordTest.php
tests/unit/framework/db/ActiveRecordTest.php
+34
-0
No files found.
docs/guide/active-record.md
View file @
c4c328dc
...
...
@@ -449,10 +449,45 @@ Below are some more examples,
```
php
// find all orders that contain books, but do not eager loading "books".
$orders
=
Order
::
find
()
->
innerJoinWith
(
'books'
,
false
)
->
all
();
// equivalent to the above
//
which is
equivalent to the above
$orders
=
Order
::
find
()
->
joinWith
(
'books'
,
false
,
'INNER JOIN'
)
->
all
();
```
Sometimes when joining two tables, you may need to specify some extra condition in the ON part of the JOIN query.
This can be done by calling the
[
[\yii\db\ActiveRelation::onCondition()
]
] method like the following:
```
php
class
User
extends
ActiveRecord
{
public
function
getBooks
()
{
return
$this
->
hasMany
(
Item
::
className
(),
[
'owner_id'
=>
'id'
]
->
onCondition
([
'category_id'
=>
1
]);
}
}
```
In the above, the
`hasMany()`
method returns an
`ActiveRelation`
instance, upon which
`onCondition()`
is called
to specify that only items whose
`category_id`
is 1 should be returned.
When you perform query using
[
[ActiveQuery::joinWith()|joinWith()
]
], the on-condition will be put in the ON part
of the corresponding JOIN query. For example,
```
php
// SELECT tbl_user.* FROM tbl_user LEFT JOIN tbl_item ON tbl_item.owner_id=tbl_user.id AND category_id=1
// SELECT * FROM tbl_item WHERE owner_id IN (...) AND category_id=1
$users
=
User
::
model
()
->
joinWith
(
'books'
)
->
all
();
```
Note that if you use eager loading via
[
[ActiveQuery::with()
]
] or lazy loading, the on-condition will be put
in the WHERE part of the corresponding SQL statement, because there is no JOIN query involved. For example,
```
php
// SELECT * FROM tbl_user WHERE id=10
$user
=
User
::
model
(
10
);
// SELECT * FROM tbl_item WHERE owner_id=10 AND category_id=1
$books
=
$user
->
books
;
```
Working with Relationships
--------------------------
...
...
framework/yii/db/ActiveQuery.php
View file @
c4c328dc
...
...
@@ -389,8 +389,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
$on
[]
=
"
$parentAlias
.[[
$parentColumn
]] =
$childAlias
.[[
$childColumn
]]"
;
}
$on
=
implode
(
' AND '
,
$on
);
if
(
!
empty
(
$child
->
on
))
{
$on
=
[
'and'
,
$on
,
$child
->
on
];
}
}
else
{
$on
=
''
;
$on
=
$child
->
on
;
}
$this
->
join
(
$joinType
,
$childTable
,
$on
);
...
...
framework/yii/db/ActiveRelation.php
View file @
c4c328dc
...
...
@@ -29,6 +29,28 @@ class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
use
ActiveRelationTrait
;
/**
* @var string|array the join condition. Please refer to [[Query::where()]] on how to specify this parameter.
* The condition will be used in the ON part when [[ActiveQuery::joinRelation()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
*/
public
$on
;
/**
* Sets the ON condition for the query.
* The condition will be used in the ON part when [[ActiveQuery::joinRelation()]] is called.
* Otherwise, the condition will be used in the WHERE part of a query.
* @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
* @param array $params the parameters (name => value) to be bound to the query.
* @return static the query object itself
*/
public
function
onCondition
(
$condition
,
$params
=
[])
{
$this
->
on
=
$condition
;
$this
->
addParams
(
$params
);
return
$this
;
}
/**
* Specifies the pivot table.
* @param string $tableName the name of the pivot table.
* @param array $link the link between the pivot table and the table associated with [[primaryModel]].
...
...
@@ -62,33 +84,52 @@ class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
*/
public
function
createCommand
(
$db
=
null
)
{
if
(
$this
->
primaryModel
!==
null
)
{
$where
=
$this
->
where
;
// lazy loading
if
(
$this
->
via
instanceof
self
)
{
// via pivot table
$viaModels
=
$this
->
via
->
findPivotRows
([
$this
->
primaryModel
]);
$this
->
filterByModels
(
$viaModels
);
}
elseif
(
is_array
(
$this
->
via
))
{
// via relation
/** @var ActiveRelation $viaQuery */
list
(
$viaName
,
$viaQuery
)
=
$this
->
via
;
if
(
$viaQuery
->
multiple
)
{
$viaModels
=
$viaQuery
->
all
();
$this
->
primaryModel
->
populateRelation
(
$viaName
,
$viaModels
);
}
else
{
$model
=
$viaQuery
->
one
();
$this
->
primaryModel
->
populateRelation
(
$viaName
,
$model
);
$viaModels
=
$model
===
null
?
[]
:
[
$model
];
}
$this
->
filterByModels
(
$viaModels
);
if
(
$this
->
primaryModel
===
null
)
{
// eager loading
if
(
!
empty
(
$this
->
on
))
{
$where
=
$this
->
where
;
$this
->
andWhere
(
$this
->
on
);
$command
=
parent
::
createCommand
(
$db
);
$this
->
where
=
$where
;
return
$command
;
}
else
{
return
parent
::
createCommand
(
$db
);
}
}
// lazy loading
$where
=
$this
->
where
;
if
(
$this
->
via
instanceof
self
)
{
// via pivot table
$viaModels
=
$this
->
via
->
findPivotRows
([
$this
->
primaryModel
]);
$this
->
filterByModels
(
$viaModels
);
}
elseif
(
is_array
(
$this
->
via
))
{
// via relation
/** @var ActiveRelation $viaQuery */
list
(
$viaName
,
$viaQuery
)
=
$this
->
via
;
if
(
$viaQuery
->
multiple
)
{
$viaModels
=
$viaQuery
->
all
();
$this
->
primaryModel
->
populateRelation
(
$viaName
,
$viaModels
);
}
else
{
$this
->
filterByModels
([
$this
->
primaryModel
]);
$model
=
$viaQuery
->
one
();
$this
->
primaryModel
->
populateRelation
(
$viaName
,
$model
);
$viaModels
=
$model
===
null
?
[]
:
[
$model
];
}
$
command
=
parent
::
createCommand
(
$db
);
$this
->
where
=
$where
;
return
$command
;
$
this
->
filterByModels
(
$viaModels
);
}
else
{
$this
->
filterByModels
([
$this
->
primaryModel
])
;
}
return
parent
::
createCommand
(
$db
);
if
(
!
empty
(
$this
->
on
))
{
$this
->
andWhere
(
$this
->
on
);
}
$command
=
parent
::
createCommand
(
$db
);
$this
->
where
=
$where
;
return
$command
;
}
}
tests/unit/data/ar/Order.php
View file @
c4c328dc
...
...
@@ -58,6 +58,13 @@ class Order extends ActiveRecord
->
where
([
'category_id'
=>
1
]);
}
public
function
getBooks2
()
{
return
$this
->
hasMany
(
Item
::
className
(),
[
'id'
=>
'item_id'
])
->
onCondition
([
'category_id'
=>
1
])
->
viaTable
(
'tbl_order_item'
,
[
'order_id'
=>
'id'
]);
}
public
function
beforeSave
(
$insert
)
{
if
(
parent
::
beforeSave
(
$insert
))
{
...
...
tests/unit/framework/db/ActiveRecordTest.php
View file @
c4c328dc
...
...
@@ -290,5 +290,39 @@ class ActiveRecordTest extends DatabaseTestCase
$this
->
assertTrue
(
$orders
[
0
]
->
isRelationPopulated
(
'customer'
));
$this
->
assertTrue
(
$orders
[
1
]
->
isRelationPopulated
(
'customer'
));
$this
->
assertTrue
(
$orders
[
2
]
->
isRelationPopulated
(
'customer'
));
// join with ON condition
$orders
=
Order
::
find
()
->
joinWith
(
'books2'
)
->
orderBy
(
'tbl_order.id'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$orders
));
$this
->
assertEquals
(
1
,
$orders
[
0
]
->
id
);
$this
->
assertEquals
(
2
,
$orders
[
1
]
->
id
);
$this
->
assertEquals
(
3
,
$orders
[
2
]
->
id
);
$this
->
assertTrue
(
$orders
[
0
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertTrue
(
$orders
[
1
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertTrue
(
$orders
[
2
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertEquals
(
2
,
count
(
$orders
[
0
]
->
books2
));
$this
->
assertEquals
(
0
,
count
(
$orders
[
1
]
->
books2
));
$this
->
assertEquals
(
1
,
count
(
$orders
[
2
]
->
books2
));
// lazy loading with ON condition
$order
=
Order
::
find
(
1
);
$this
->
assertEquals
(
2
,
count
(
$order
->
books2
));
$order
=
Order
::
find
(
2
);
$this
->
assertEquals
(
0
,
count
(
$order
->
books2
));
$order
=
Order
::
find
(
3
);
$this
->
assertEquals
(
1
,
count
(
$order
->
books2
));
// eager loading with ON condition
$orders
=
Order
::
find
()
->
with
(
'books2'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$orders
));
$this
->
assertEquals
(
1
,
$orders
[
0
]
->
id
);
$this
->
assertEquals
(
2
,
$orders
[
1
]
->
id
);
$this
->
assertEquals
(
3
,
$orders
[
2
]
->
id
);
$this
->
assertTrue
(
$orders
[
0
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertTrue
(
$orders
[
1
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertTrue
(
$orders
[
2
]
->
isRelationPopulated
(
'books2'
));
$this
->
assertEquals
(
2
,
count
(
$orders
[
0
]
->
books2
));
$this
->
assertEquals
(
0
,
count
(
$orders
[
1
]
->
books2
));
$this
->
assertEquals
(
1
,
count
(
$orders
[
2
]
->
books2
));
}
}
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