1. 最小化用法

控制器:

DataProvider 自行解决了数据分页甚至是排序问题,以下提供了 yii\data\ActiveDataProvideryii\data\ArrayDataProvider 的用法,yii\data\SqlDataProvider 还没遇到需要用它的情况,不做介绍。

use app\models\User;
use yii\data\ActiveDataProvider;
use yii\data\ArrayDataProvider;
use yii\db\Query;

/**
 * 使用 ActiveDataProvider
 * 
 * 使用 ActiveDataProvider 的好处:
 * 不用自己计算数据总数
 * 不用自己做分页控制
 */
public function actionDemoA()
{
    $query = User::find()/*->asArray()*/;// 加上 asArray() 可以让每条数据都是数组
    // 或者
    $query = (new Query())->from(User::tableName());
    
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
    
    return $this->render('users', [
        'dataProvider' => $dataProvider,
    ]);
}

/**
 * 使用 ArrayDataProvider
 * 
 * ArrayDataProvider 是对现成的数组进行分页和排序,数据量太大的话需要自己掂量一下
 */
public function actionDemoB()
{
    // 注意这里查询比上面多了 all()
    $data = User::find()/*->asArray()*/->all();
    // 或者
    $data = (new Query())->from(User::tableName())->all();
    // 或者
    $data = [
        [
            'id' => 1,
            'user_name' => 'a',
            // ...
        ],
        [
            'id' => 2,
            'user_name' => 'b',
            // ...
        ],
        // ...
    ];
    
    $dataProvider = new ArrayDataProvider([
        'allModels' => $data,
    ]);
    
    return $this->render('users', [
        'dataProvider' => $dataProvider,
    ]);
}

视图:

直接使用 DataProvider

use yii\widgets\LinkPager;

/**
 * @var $this \yii\web\View
 * @var $dataProvider \yii\data\ActiveDataProvider
 */
 
if ($dataProvider->totalCount) {
    foreach ($dataProvider->models as $model) {
        echo ($model->id, '<br>', $model->username, '<br>');
    }
} else {
    echo '没有数据';
}

// 输出分页链接
echo LinkPager::widget([
    'pagination' => $dataProvider->pagination,
]) ?>

// 输出排序链接
echo $dataProvider->sort->link('id');
echo $dataProvider->sort->link('name');

循环里的 $model 变量是对象还是数组,取决于使用 ActiveRecord 查询时是否执行了 asArray() 方法。

使用 GridView

如果你觉得上面自己写各种 html 麻烦的话,那么用 GridView 一定是更方便的方法,它使用 DataProvider 来帮你做好了上面做的事情,如果你对表格样式不满意,又不想每个页面都做大量配置,你还可以用 DI 容器来定制它,或者可以参考这里,封装好大量的默认配置。

use yii\grid\GridView;

/**
 * @var $this \yii\web\View
 * @var $dataProvider \yii\data\ActiveDataProvider
 */

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        'id',
        'user_name',
    ],
]);

2. 带搜索的用法

以下搜索无论是哪种用法,关键点都在于使用 filterWhere() 方法,当某字段在 GET 传过来的值为空时不对该字段进行任何查询,当然也可以用传统的先判断值,然后在判断里执行 where()

where() 的更多用法可到 yii\db\QueryInterface 查看。

2.1 基本用法

控制器代码如下:

use app\models\User;
use yii\data\ActiveDataProvider;

$request = Yii::$app->request;
$id = $request->get('id');
$username = $request->get('username');
$mobile = $request->get('mobile');

$query = User::find();

$query->andFilterWhere([
    'id' => $id,
    'mobile' => $mobile,
]);

$query->andFilterWhere(['like', 'username', $username]);

$dataProvider = new ActiveDataProvider([
    'query' => $query,
]);

return $this->render('users', [
    'dataProvider' => $dataProvider,
]);

2.2 SearchModel

有时候数据库表的字段太多,可以建立一个类来继承你的 Model 专门用来查询,其实你要是用过 gii 生成 crud 代码的话,你可以跳过这一节。

以用户表为例,建立一个用来搜索的 UserSearch 类,代码如下:

namespace app\models\search;

use app\models\User;
use yii\data\ActiveDataProvider;

class UserSearch extends User
{
    /**
     * 重写父类规则,同时也决定了 GridView 上该字段是否显示搜索框
     */
    public function rules()
    {
        return [
            ['id', 'integer'],
            ['mobile', 'number'],
            [['username'], 'safe'],
        ];
    }
    
    public function search($params)
    {
        $query = User::find();
        
        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);
        
        // 接收 get 参数,能否赋值到 model 取决于该字段是否出现在上面的 rules 方法里
        $this->load($params);
        
        // 可选,验证 rules,如果有不符合规则的,则不进行搜索
        if (!$this->validate()) {
            return $dataProvider;
        }
        
        // model 赋值后可以直接 $this 调用属性
        $query->andFilterWhere([
            'id' => $this->id,
            'mobile' => $this->mobile,
        ]);
        
        $query->andFilterWhere(['like', 'username', $this->username]);
        
        return $dataProvider;
    }
}

控制器

use app\models\search\UserSearch;

$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);

return $this->render('users', [
    'searchModel' => $searchModel,
    'dataProvider' => $dataProvider,
]);

视图:

和最简用法的区别在于传给 GridView 的参数多了一个 filterModel

use yii\grid\GridView;

/**
 * @var $this \yii\web\View
 * @var $searchModel \app\models\search\UsersSearch
 * @var $dataProvider \yii\data\ActiveDataProvider
 */

echo GridView::widget([
    'filterModel' => $searchModel,
    'dataProvider' => $dataProvider,
    'columns' => [
        'id',
        'user_name',
        'mobile',
    ],
]);

3. 自定义列的内容

首先,以下代码给出的两种输出 created_at 的作用是等效的,具有输出对应字段值、中文 label、搜索、排序的作用。

'columns' => [
    'created_at',
    [
        'attribute' => 'created_at',
    ],
],

以下以格式化时间戳为例(实际有更好的格式化方法,这里是为了演示):

'columns' => [
    [
        'attribute' => 'created_at',
        'value' => function ($model) {
            /** @var $model \app\models\User */
            return date('Y-m-d H:i:s', $model->created_at);
        },
    ],
],

使用这种方法,还可以创建自定义列,只是无法排序和搜索,而且 label 需要手动设置:

'columns' => [
    [
        'label' => '平均值',
        'value' => function ($model) {
            return $model['all'] / $model['count'];
        },
    ],
],