GridView 关联其他表如何显示、排序和过滤相关的模型数据

技巧库 · echo · 于 7年前 发布 · 9051 次阅读

来源:How to display, sort, and filter related model data in GridView

我想分享如何处理相关的模型显示数据表格中的数据。开始,我们实际上有两种情况需要考虑。让我们用以下三个表来演示:

表结构如下:

tbl_user:    id, role_id, email, username, etc
tbl_role:    id, name
tbl_profile: id, user_id, full_name

从这三个表,您可以看到用户所属角色和配置文件。

根据 belongs to/has 关系,我们会做不同的事情。下面是示例,我们将tbl_role.name tbl_profile.full_name一起显示到GridView。

1、在视图中显示数据文件

文件: app/views/user/index.php

<?php echo \yii\grid\GridView::widget([               
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // 其他列
        [
            'attribute' => 'full_name',
            'label' => 'Full Name',
            'value' => function($model, $index, $dataColumn) {
                return $model->profile->full_name;
            }
        ],
        [
            'attribute' => 'role_id',
            'label' => 'Role',
            'filter' => Role::dropdown(),
            'value' => function($model, $index, $dataColumn) {
                // more optimized than $model->role->name;
                $roleDropdown = Role::dropdown();
                return $roleDropdown[$model->role_id];
            },
        ],
        // 其他列
        ['class' => 'yii\grid\ActionColumn'],
    ],
]); ?>

文件: app/models/Role.php

public static function dropdown() {
    $models = static::find()->all();
    foreach ($models as $model) {
        $dropdown[$model->id] = $model->name;
    }
    return $dropdown;
}

太棒了!信不信由你,但这是我们所需要的信息的作用。完成了,完成了。

下一步,让我们的工作概要信息。我们需要更新搜索模型(即$searchModel )。

2、将属性添加到模型中

文件:app/models/search/UserSearch.php (3个地方要改)

a) public $full_name;
b) public function rules() { } // 添加一条规则,以便它可以验证
c) public function attributeLabels() { } // 添加一个标签

3、修改搜索查询来处理新属性 文件:app/models/search/UserSearch.php

public function search($params)
{
    // add the inner join
    $query = User::find();
    $query->innerJoin("tbl_profile", "tbl_user.id=tbl_profile.user_id");
    $query->with("profile"); // 急切的负荷减少数量的查询
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
 
    // 添加额外的类属性
    $addSortAttributes = ["full_name"];
    foreach ($addSortAttributes as $addSortAttribute) {
        $dataProvider->sort->attributes[$addSortAttribute] = [
            'asc' => [$addSortAttribute => SORT_ASC],
            'desc' => [$addSortAttribute => SORT_DESC],
            'label' => $this->getAttributeLabel($addSortAttribute),
        ];
    }
 
    if (!($this->load($params) && $this->validate())) {
        return $dataProvider;
    }
 
    // 最后添加 full_name 条件
    $this->addCondition($query, 'id');
    ...
    $this->addCondition($query, 'full_name', true);
}

文件:app/models/search/UserSearch.php

protected function addCondition($query, $attribute, $partialMatch = false)
{
    $value = $this->$attribute;
    if (trim($value) === '') {
        return;
    }
 
    // 添加表名与profile.id id来避免模棱两可的错误,
    // 即, "tbl_user.id"
    if ($attribute == "id") {
        $attribute = "tbl_user.id";
    }
 
    if ($partialMatch) {
        $query->andWhere(['like', $attribute, $value]);
    } else {
        $query->andWhere([$attribute => $value]);
    }
}

我们现在有一个完整的网格与排序和过滤我们的相关模型。

但是,等等,如果我们有很多角色吗?我们怎么做如果下拉列表太大,我们需要寻找的名字吗?

好的,那么我们需要将它转换成一个文本搜索。这意味着我们不再使用role_id,而是role_name。所以让我们继续,改变在我们的所有实例的代码。

4、在视图中更新数据文件

文件:app/views/user/index.php

[
    'attribute' => 'role_name',
    'label' => 'Role',
    // 删除下拉过滤器返回文本
    //'filter' => Role::dropdown(),
    'value' => function($model, $index, $dataColumn) {
        // more optimized than $model->role->name;
        $roleDropdown = Role::dropdown();
        return $roleDropdown[$model->role_id];
    },
],

然后我们需要回到搜索模型。

5、更新模型中的属性

文件: app/models/search/UserSearch.php (3个地方要改)

a) public $role_name; // $role_id 改成 $role_name
b) public function rules() {  } // 'role_id' 改成 'role_name', int 改成 string
c) public function attributeLabels() {  } // 'role_id' 改成 'role_name'

6、修改搜索查询来处理新属性

文件: app/models/search/UserSearch.php

public function search($params)
{ 
    // 都添加内连接
    $query = User::find();
    $query->innerJoin("tbl_profile", "tbl_user.id=tbl_profile.user_id");
    $query->innerJoin("tbl_role", "tbl_user.role_id=tbl_role.id");
    $query->with("profile"); // 急切的负荷减少数量的查询
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
 
    // 更新为 role_id->role_name 排序
    $dataProvider->sort->attributes["role_name"]["asc"] = ["tbl_role.name" => SORT_ASC];
    $dataProvider->sort->attributes["role_name"]["desc"] = ["tbl_role.name" => SORT_DESC];
    $dataProvider->sort->attributes["role_name"]["label"] = "Role";
 
    // 添加额外的类属性
    $addSortAttributes = ["full_name"];
    foreach ($addSortAttributes as $addSortAttribute) {
        $dataProvider->sort->attributes[$addSortAttribute] = [
            'asc' => [$addSortAttribute => SORT_ASC],
            'desc' => [$addSortAttribute => SORT_DESC],
            'label' => $this->getAttributeLabel($addSortAttribute),
        ];
    }
 
    if (!($this->load($params) && $this->validate())) {
        return $dataProvider;
    }
 
    // 修改 addCondition,role_id->role_name 部分匹配
    $this->addCondition($query, 'id');
    ...
    //$this->addCondition($query, 'role_id'); // 替换为“role_name”
    $this->addCondition($query, 'role_name', true);
    ...
}

7、修改addCondition role_name 转换成适当的 sql 函数

文件:app/models/search/UserSearch.php

protected function addCondition($query, $attribute, $partialMatch = false)
{
    $value = $this->$attribute;
    if (trim($value) === '') {
        return;
    }
 
    // 添加表名与profile.id id来避免模棱两可的错误,
    // 即, "tbl_user.id"
    if ($attribute == "id") {
        $attribute = "tbl_user.id";
    }
    // 为 "role_name" 转换为 "tbl_role.name"
    elseif ($attribute == "role_name") {
        $attribute = "tbl_role.name";
    }
     
    if ($partialMatch) {
        $query->andWhere(['like', $attribute, $value]);
    } else {
        $query->andWhere([$attribute => $value]);
    }
}

好了,我们现在有一个可搜索文本框的角色。

本帖已被设为精华帖!
共收到 4 条回复 翻译 Grid
Simon#17年前 3 个赞

视图部分可以有更简洁的写法

<?php echo \yii\grid\GridView::widget([               
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // 其他列
        [
            'attribute' => 'full_name',
            'label' => 'Full Name',
            //此处如果确定是关联对象的话可以这么写
            'value' => 'profile.full_name'
        ],
        [
            'attribute' => 'role_id',
            'label' => 'Role',
            'filter' => Role::dropdown(),
            'value' => function($model, $index, $dataColumn) {
                // more optimized than $model->role->name;
                $roleDropdown = Role::dropdown();
                return $roleDropdown[$model->role_id];
            },
        ],
        // 其他列
        ['class' => 'yii\grid\ActionColumn'],
    ],
]); ?>
test123#27年前 0 个赞

:smile:

iamwil#37年前 0 个赞

有界面效果截图吗?

forecho#47年前 0 个赞

@iamwil #3楼 界面跟默认的界面是一样的,你要看代码理解

5楼 已删除.
添加回复 (需要登录)
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册