I\'m trying to get data from a join table in Yii2 without an additional query. I have 2 models (User, Group) associated via the junction table (user_group). In
In short: Using an ActiveRecord
for the junction table like you suggested is IMHO the right way because you can set up via()
to use that existing ActiveRecord
. This allows you to use Yii's link()
method to create items in the junction table while adding data (like your admin flag) at the same time.
The official Yii Guide 2.0 states two ways of using a junction table: using viaTable()
and using via()
(see here). While the former expects the name of the junction table as parameter the latter expects a relation name as parameter.
If you need access to the data inside the junction table I would use an ActiveRecord
for the junction table as you suggested and use via()
:
class User extends ActiveRecord
{
public function getUserGroups() {
// one-to-many
return $this->hasMany(UserGroup::className(), ['user_id' => 'id']);
}
}
class Group extends ActiveRecord
{
public function getUserGroups() {
// one-to-many
return $this->hasMany(UserGroup::className(), ['group_id' => 'id']);
}
public function getUsers()
{
// many-to-many: uses userGroups relation above which uses an ActiveRecord class
return $this->hasMany(User::className(), ['id' => 'user_id'])
->via('userGroups');
}
}
class UserGroup extends ActiveRecord
{
public function getUser() {
// one-to-one
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
public function getGroup() {
// one-to-one
return $this->hasOne(Group::className(), ['id' => 'userh_id']);
}
}
This way you can get the data of the junction table without additional queries using the userGroups
relation (like with any other one-to-many relation):
$group = Group::find()->where(['id' => $id])->with('userGroups.user')->one();
// --> 3 queries: find group, find user_group, find user
// $group->userGroups contains data of the junction table, for example:
$isAdmin = $group->userGroups[0]->adminFlag
// and the user is also fetched:
$userName = $group->userGroups[0]->user->name
This all can be done using the hasMany
relation. So you may ask why you should declare the many-to-many relation using via()
: Because you can use Yii's link()
method to create items in the junction table:
$userGroup = new UserGroup();
// load data from form into $userGroup and validate
if ($userGroup->load(Yii::$app->request->post()) && $userGroup->validate()) {
// all data in $userGroup is valid
// --> create item in junction table incl. additional data
$group->link('users', $user, $userGroup->getDirtyAttributes())
}