问题 从Yii2中的联结表中检索数据
我试图 从Yii2中的连接表中获取数据 没有额外的查询。我有2个模型(用户,组)通过联结表(user_group)关联。在user_group表中,我想为此关系存储额外数据(admin flag,...)。
仅供参考,这是我在模型中定义关系的方式:
Group.php
public function getUsers() {
return $this->hasMany(User::className(), ['id' => 'user_id'])
->viaTable('user_group', ['group_id' => 'id']);
}
user.php的
public function getGroups() {
return $this->hasMany(Group::className(), ['id' => 'group_id'])
->viaTable('user_group', ['user_id' => 'id']);
}
7118
2018-02-15 12:16
起源
答案:
简而言之:使用 ActiveRecord
像你建议的联络表是恕我直言,因为你可以设置 via()
使用现有的 ActiveRecord
。这允许你使用Yii的 link()
在同时添加数据(如管理员标志)的同时在联结表中创建项目的方法。
官方Yii指南2.0说明了两种使用联结表的方法:使用 viaTable()
和使用 via()
(看到 这里)。虽然前者期望联结表的名称作为参数,但后者期望关系名称作为参数。
如果您需要访问联结表中的数据,我会使用 ActiveRecord
对于您建议和使用的联结表 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']);
}
}
通过这种方式,您无需使用其他查询即可获取联结表的数据 userGroups
关系(与任何其他一对多关系一样):
$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
这一切都可以使用 hasMany
关系。所以你可能会问为什么你应该声明使用多对多关系 via()
:因为你可以使用Yii的 link()
在联结表中创建项目的方法:
$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())
}
7
2017-09-01 20:43
答案:
简而言之:使用 ActiveRecord
像你建议的联络表是恕我直言,因为你可以设置 via()
使用现有的 ActiveRecord
。这允许你使用Yii的 link()
在同时添加数据(如管理员标志)的同时在联结表中创建项目的方法。
官方Yii指南2.0说明了两种使用联结表的方法:使用 viaTable()
和使用 via()
(看到 这里)。虽然前者期望联结表的名称作为参数,但后者期望关系名称作为参数。
如果您需要访问联结表中的数据,我会使用 ActiveRecord
对于您建议和使用的联结表 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']);
}
}
通过这种方式,您无需使用其他查询即可获取联结表的数据 userGroups
关系(与任何其他一对多关系一样):
$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
这一切都可以使用 hasMany
关系。所以你可能会问为什么你应该声明使用多对多关系 via()
:因为你可以使用Yii的 link()
在联结表中创建项目的方法:
$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())
}
7
2017-09-01 20:43
由于我差不多14天没有得到答案,我会发布我是如何解决这个问题的。这不是我想到的,但是它有效,现在已经足够了。所以......这就是我做的:
- 添加了一个模型 用户组 用于连接表
添加了关系 组
public function getUserGroups()
{
return $this->hasMany(UserGroup::className(), ['user_id' => 'id']);
}
在我的搜索模型函数中加入了UserGroup
$query = Group::find()->where('id =' . $id)->with('users')->with('userGroups');
这就是我想要的东西 组 所有 用户 并且,由我的新模型代表 用户组,来自联结表的数据。
我想首先扩展构建Yii2函数的查询 - 这可能是解决此问题的更好方法。但由于我还不太了解Yii2,我现在决定不这样做。
如果您有更好的解决方案,请告诉我。
3
2018-02-28 16:54
我不确定这是最好的解决方案。但对于我的项目,它现在将是好的:)
1)左连接
添加新的类属性 User
模型 public $flag;
。
在基本关系中附加两行但不删除 viaTable
这可以(而且应该)留下来。
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])
->viaTable('user_group', ['group_id' => 'id'])
->leftJoin('user_group', '{{user}}.id=user_id')
->select('{{user}}.*, flag') //or all ->select('*');
}
leftJoin
可以从联结表和选择数据 select
自定义返回列。
记住这一点 viaTable
必须留下来因为 链接() 依靠它。
2)子选择查询
添加新的类属性 User
模型 public $flag;
并在 Group
模型修改 getUsers()
关系:
public function getUsers()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])
->viaTable('user_group', ['group_id' => 'id'])
->select('*, (SELECT flag FROM user_group WHERE group_id='.$this->id.' AND user_id=user.id LIMIT 1) as flag');
}
如您所见,我添加了默认选择列表的子选择。此选择适用于不是组模型的用户。是的,我同意这有点丑陋,但做的工作。
3)条件关系
不同的选择是仅为管理员创建一个以上的关系:
// Select all users
public function getUsers() { .. }
// Select only admins (users with specific flag )
public function getAdmins()
{
return $this->hasMany(User::className(), ['id' => 'user_id'])
->viaTable('user_group', ['group_id' => 'id'],
function($q){
return $q->andWhere([ 'flag' => 'ADMIN' ]);
});
}
$Group->admins
- 获取具有特定管理标志的用户。但是这个解决方案不会添加属性 $flag
。您需要知道何时只选择管理员和所有用户。缺点:您需要为每个标志值创建单独的关系。
使用单独模型的解决方案 UserGroup
对于所有情况,仍然更加灵活和普遍。就像你可以添加验证和基本的ActiveRecord东西。这些解决方案更多的是单向方向 - 将问题排除在外。
3
2017-10-02 13:34
为此,我创建了一个简单的扩展,它允许将联结表中的列作为属性附加到子模型中。
因此,在设置此扩展后,您将能够访问联结表属性,如
foreach ($parentModel->relatedModels as $childModel)
{
$childModel->junction_table_column1;
$childModel->junction_table_column2;
....
}
欲了解更多信息,请查看
Yii2联结表属性扩展
谢谢。
1
2017-09-15 17:58