3 关注者

数据小部件

Yii 提供了一组 小部件,可用于显示数据。虽然 DetailView 小部件可以用于显示单个记录的数据,但 ListViewGridView 可以用于显示数据记录的列表或表格,提供分页、排序和过滤等功能。

DetailView

DetailView 小部件显示单个数据 模型 的详细信息。

它最适合用于以常规格式显示模型(例如,每个模型属性都作为表格中的一行显示)。模型可以是 yii\base\Model 的实例或子类,例如 活动记录 或关联数组。

DetailView 使用 $attributes 属性来确定要显示哪些模型属性以及如何对其进行格式化。有关可用的格式化选项,请参见 格式化程序部分

DetailView 的典型用法如下

echo DetailView::widget([
    'model' => $model,
    'attributes' => [
        'title',                                           // title attribute (in plain text)
        'description:html',                                // description attribute formatted as HTML
        [                                                  // the owner name of the model
            'label' => 'Owner',
            'value' => $model->owner->name,            
            'contentOptions' => ['class' => 'bg-red'],     // HTML attributes to customize value tag
            'captionOptions' => ['tooltip' => 'Tooltip'],  // HTML attributes to customize label tag
        ],
        'created_at:datetime',                             // creation date formatted as datetime
    ],
]);

请记住,与 yii\widgets\GridView 处理一组模型不同,DetailView 只处理一个模型。因此,大多数情况下不需要使用闭包,因为 $model 是唯一用于显示的模型,并且在视图中作为一个变量可用。

但是,在某些情况下,使用闭包可能很有用。例如,当指定 visible 并且你想要在它评估为 false 的情况下阻止 value 计算时

echo DetailView::widget([
    'model' => $model,
    'attributes' => [
        [
            'attribute' => 'owner',
            'value' => function ($model) {
                return $model->owner->name;
            },
            'visible' => \Yii::$app->user->can('posts.owner.view'),
        ],
    ],
]);

ListView

ListView 小部件用于显示来自 数据提供者 的数据。每个数据模型都使用指定的 视图文件 呈现。由于它开箱即用地提供了一些功能,例如分页、排序和过滤,因此它既便于向最终用户显示信息,也便于创建数据管理 UI。

典型的用法如下

use yii\widgets\ListView;
use yii\data\ActiveDataProvider;

$dataProvider = new ActiveDataProvider([
    'query' => Post::find(),
    'pagination' => [
        'pageSize' => 20,
    ],
]);
echo ListView::widget([
    'dataProvider' => $dataProvider,
    'itemView' => '_post',
]);

_post 视图文件可以包含以下内容

<?php
use yii\helpers\Html;
use yii\helpers\HtmlPurifier;
?>
<div class="post">
    <h2><?= Html::encode($model->title) ?></h2>

    <?= HtmlPurifier::process($model->text) ?>    
</div>

在上面的视图文件中,当前数据模型可以作为 $model 使用。此外,还可以使用以下变量

  • $key:混合,与数据项关联的键值。
  • $index:整数,数据项在数据提供者返回的项目数组中的零索引位置。
  • $widget:ListView,此小部件实例。

如果需要向每个视图传递更多数据,可以使用 $viewParams 属性来传递键值对,如下所示

echo ListView::widget([
    'dataProvider' => $dataProvider,
    'itemView' => '_post',
    'viewParams' => [
        'fullView' => true,
        'context' => 'main-page',
        // ...
    ],
]);

然后,这些变量也会在视图中可用。

GridView

数据网格或 GridView 是 Yii 中最强大的小部件之一。如果您需要快速构建系统的管理部分,它非常有用。它从 数据提供者 获取数据,并使用一组 渲染每一行,以表格的形式呈现数据。

表格的每一行代表单个数据项的数据,而一列通常代表该项的一个属性(某些列可能对应于属性的复杂表达式或静态文本)。

使用 GridView 所需的最小代码如下所示

use yii\grid\GridView;
use yii\data\ActiveDataProvider;

$dataProvider = new ActiveDataProvider([
    'query' => Post::find(),
    'pagination' => [
        'pageSize' => 20,
    ],
]);
echo GridView::widget([
    'dataProvider' => $dataProvider,
]);

上面的代码首先创建一个数据提供者,然后使用 GridView 显示从数据提供者获取的每一行中的每个属性。显示的表格开箱即用地配备了排序和分页功能。

网格列

网格表格的列根据 yii\grid\Column 类进行配置,这些类在 GridView 配置的 columns 属性中进行配置。根据列类型和设置,这些列能够以不同的方式呈现数据。默认类是 yii\grid\DataColumn,它代表模型属性,可以对其进行排序和过滤。

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // Simple columns defined by the data contained in $dataProvider.
        // Data from the model's column will be used.
        'id',
        'username',
        // More complex one.
        [
            'class' => 'yii\grid\DataColumn', // can be omitted, as it is the default
            'value' => function ($data) {
                return $data->name; // $data['name'] for array data, e.g. using SqlDataProvider.
            },
        ],
    ],
]);

请注意,如果未指定配置的 columns 部分,Yii 将尝试显示数据提供者模型的所有可能列。

列类

可以使用不同的列类自定义网格列

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\SerialColumn', // <-- here
            // you may configure additional properties here
        ],

除了 Yii 提供的我们将在下面回顾的列类之外,您还可以创建自己的列类。

每个列类都扩展自 yii\grid\Column,因此在配置网格列时,您可以设置一些通用选项。

  • header 允许设置标题行的内容。
  • footer 允许设置页脚行的内容。
  • visible 定义列是否应可见。
  • content 允许您传递一个有效的 PHP 回调,该回调将返回一行的值。格式如下

    function ($model, $key, $index, $column) {
        return 'a string';
    }
    

您可以通过将数组传递给以下内容来指定各种容器 HTML 选项

数据列

数据列 用于显示和排序数据。它是默认的列类型,因此在使用它时可以省略指定类。

数据列的主要设置是其 format 属性。它的值对应于 formatter 应用程序组件 中的方法,默认情况下该方法是 Formatter

echo GridView::widget([
    'columns' => [
        [
            'attribute' => 'name',
            'format' => 'text'
        ],
        [
            'attribute' => 'birthday',
            'format' => ['date', 'php:Y-m-d']
        ],
        'created_at:datetime', // shortcut format
        [
            'label' => 'Education',
            'attribute' => 'education',
            'filter' => ['0' => 'Elementary', '1' => 'Secondary', '2' => 'Higher'],
            'filterInputOptions' => ['prompt' => 'All educations', 'class' => 'form-control', 'id' => null]
        ],
    ],
]);

在上面,text 对应于 yii\i18n\Formatter::asText()。列的值作为第一个参数传递。在第二个列定义中,date 对应于 yii\i18n\Formatter::asDate()。列的值再次作为第一个参数传递,而 'php:Y-m-d' 用作第二个参数值。

有关可用格式化程序的列表,请参见 关于数据格式化的部分

为了配置数据列,还有一个在 columns 的 API 文档中描述的快捷格式。

使用 filterfilterInputOptions 来控制筛选器输入的 HTML。

默认情况下,列标题由 yii\data\Sort::link() 呈现。可以使用 yii\grid\Column::$header 进行调整。要更改标题文本,您应该设置 yii\grid\DataColumn::$label,如上面的示例所示。默认情况下,标签将从数据模型中填充。有关更多详细信息,请参见 yii\grid\DataColumn::getHeaderCellLabel()

操作列

操作列 显示操作按钮,例如更新或删除,用于每一行。

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\ActionColumn',
            // you may configure additional properties here
        ],

您可以配置的可用属性是

  • controller 是应处理操作的控制器的 ID。如果未设置,它将使用当前活动的控制器。
  • template 定义用于组合操作列中每个单元格的模板。用大括号括起来的令牌将被视为控制器操作 ID(在操作列的上下文中也称为按钮名称)。它们将被 buttons 中指定的相应按钮渲染回调替换。例如,令牌 {view} 将被回调 buttons['view'] 的结果替换。如果找不到回调,则该令牌将被空字符串替换。默认令牌是 {view} {update} {delete}
  • buttons 是一个按钮渲染回调数组。数组键是按钮名称(不带大括号),而值是相应的按钮渲染回调。回调应使用以下签名

    function ($url, $model, $key) {
        // return the button HTML code
    }
    

    在上面的代码中,$url 是列为按钮创建的 URL,$model 是当前行正在渲染的模型对象,而 $key 是数据提供者数组中模型的键。

  • urlCreator 是一个回调,它使用指定的模型信息创建按钮 URL。回调的签名应与 yii\grid\ActionColumn::createUrl() 的签名相同。如果未设置此属性,则按钮 URL 将使用 yii\grid\ActionColumn::createUrl() 创建。
  • visibleButtons 是每个按钮的可见性条件数组。数组键是按钮名称(不带大括号),而值是布尔值 true/false 或匿名函数。当按钮名称未在此数组中指定时,它将默认显示。回调必须使用以下签名

    function ($model, $key, $index) {
        return $model->status === 'editable';
    }
    

    或者您可以传递一个布尔值

    [
        'update' => \Yii::$app->user->can('update')
    ]
    

复选框列

复选框列 显示一列复选框。

要将 CheckboxColumn 添加到 GridView,请将其添加到 columns 配置中,如下所示

echo GridView::widget([
    'id' => 'grid',
    'dataProvider' => $dataProvider,
    'columns' => [
        // ...
        [
            'class' => 'yii\grid\CheckboxColumn',
            // you may configure additional properties here
        ],
    ],

用户可以单击复选框以选择网格的行。可以通过调用以下 JavaScript 代码来获取选定的行

var keys = $('#grid').yiiGridView('getSelectedRows');
// keys is an array consisting of the keys associated with the selected rows

序列列

序列列1 开始渲染行号,并向前递增。

用法与以下内容一样简单

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'], // <-- here
        // ...

排序数据

注意:此部分正在开发中。

过滤数据

为了过滤数据,GridView 需要一个 模型 来表示搜索条件,该条件通常取自 GridView 表格中的过滤器字段。当使用 活动记录 时,一种常见的做法是创建一个搜索模型类,该类提供所需的功能(可以使用 Gii 为您生成)。此类定义验证规则,以在 GridView 表格上显示过滤器控件,并提供一个 search() 方法,该方法将返回具有经过调整的查询的数据提供者,该查询处理搜索条件。

为了添加 Post 模型的搜索功能,我们可以创建一个 PostSearch 模型,如下例所示

<?php

namespace app\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;

class PostSearch extends Post
{
    public function rules()
    { 
        // only fields in rules() are searchable
        return [
            [['id'], 'integer'],
            [['title', 'creation_date'], 'safe'],
        ];
    }

    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    public function search($params)
    {
        $query = Post::find();

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

        // load the search form data and validate
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }

        // adjust the query by adding the filters
        $query->andFilterWhere(['id' => $this->id]);
        $query->andFilterWhere(['like', 'title', $this->title])
              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);

        return $dataProvider;
    }
}

提示:请参见 查询生成器,尤其是 过滤条件,以了解如何构建过滤查询。

您可以在控制器中使用此函数为 GridView 获取数据提供者

$searchModel = new PostSearch();
$dataProvider = $searchModel->search(Yii::$app->request->get());

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

然后,在视图中,将 $dataProvider$searchModel 分配给 GridView

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        // ...
    ],
]);

单独的过滤器表单

大多数时候,使用 GridView 标题过滤器就足够了,但如果您需要单独的过滤器表单,也可以轻松地添加它。您可以创建具有以下内容的部分视图 _search.php

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\models\PostSearch */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="post-search">
    <?php $form = ActiveForm::begin([
        'action' => ['index'],
        'method' => 'get',
    ]); ?>

    <?= $form->field($model, 'title') ?>

    <?= $form->field($model, 'creation_date') ?>

    <div class="form-group">
        <?= Html::submitButton('Search', ['class' => 'btn btn-primary']) ?>
        <?= Html::submitButton('Reset', ['class' => 'btn btn-default']) ?>
    </div>

    <?php ActiveForm::end(); ?>
</div>

并将其包含在 index.php 视图中,如下所示

<?= $this->render('_search', ['model' => $searchModel]) ?>

注意:如果您使用 Gii 生成 CRUD 代码,则默认情况下会生成单独的过滤器表单 (_search.php),但在 index.php 视图中已注释掉。取消注释它,它就可以使用了!

当您需要按 GridView 中未显示的字段进行过滤,或者用于特殊过滤条件(如日期范围)时,单独的过滤器表单非常有用。为了按日期范围进行过滤,我们可以将非数据库属性 createdFromcreatedTo 添加到搜索模型中

class PostSearch extends Post
{
    /**
     * @var string
     */
    public $createdFrom;

    /**
     * @var string
     */
    public $createdTo;
}

像这样扩展 search() 方法中的查询条件

$query->andFilterWhere(['>=', 'creation_date', $this->createdFrom])
      ->andFilterWhere(['<=', 'creation_date', $this->createdTo]);

并将代表性字段添加到过滤器表单中

<?= $form->field($model, 'creationFrom') ?>

<?= $form->field($model, 'creationTo') ?>

使用模型关系

在 GridView 中显示活动记录时,您可能会遇到显示相关列值的情况,例如帖子作者的姓名,而不是仅显示他的 id。您可以通过在 yii\grid\GridView::$columns 中将属性名称定义为 author.name 来做到这一点,其中 Post 模型具有名为 author 的关系,而作者模型具有属性 name。然后,GridView 将显示作者的姓名,但默认情况下不会启用排序和过滤。您必须调整上节中介绍的 PostSearch 模型以添加此功能。

要启用对相关列的排序,您必须联接相关表格并将排序规则添加到数据提供者的 Sort 组件中

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

// join with relation `author` that is a relation to the table `users`
// and set the table alias to be `author`
$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);
// since version 2.0.7, the above line can be simplified to $query->joinWith('author AS author');
// enable sorting for the related column
$dataProvider->sort->attributes['author.name'] = [
    'asc' => ['author.name' => SORT_ASC],
    'desc' => ['author.name' => SORT_DESC],
];

// ...

过滤也需要上述 joinWith 调用。您还需要在属性和规则中定义可搜索的列,如下所示

public function attributes()
{
    // add related fields to searchable attributes
    return array_merge(parent::attributes(), ['author.name']);
}

public function rules()
{
    return [
        [['id'], 'integer'],
        [['title', 'creation_date', 'author.name'], 'safe'],
    ];
}

然后,您只需在 search() 中添加另一个带以下内容的过滤条件

$query->andFilterWhere(['LIKE', 'author.name', $this->getAttribute('author.name')]);

信息:在上面,我们对关系名称和表格别名使用相同的字符串;但是,当您的别名和关系名称不同时,您必须注意在何处使用别名以及在何处使用关系名称。一个简单的规则是在用于构建数据库查询的所有位置使用别名,在所有其他定义(如 attributes()rules() 等)中使用关系名称。

例如,如果您对作者关系表使用别名 au,则 joinWith 语句看起来如下

$query->joinWith(['author au']);

当别名在关系定义中定义时,也可以只调用 $query->joinWith(['author']);

别名必须在过滤条件中使用,但属性名称保持不变

$query->andFilterWhere(['LIKE', 'au.name', $this->getAttribute('author.name')]);

排序定义也是如此

$dataProvider->sort->attributes['author.name'] = [
     'asc' => ['au.name' => SORT_ASC],
     'desc' => ['au.name' => SORT_DESC],
];

此外,在指定用于排序的 defaultOrder 时,您需要使用关系名称,而不是别名

$dataProvider->sort->defaultOrder = ['author.name' => SORT_ASC];

信息:有关 joinWith 和后台执行的查询的更多信息,请查看 关于与关系联接的活动记录文档

使用 SQL 视图进行数据过滤、排序和显示

还有一种方法可以更快、更有效 - SQL 视图。例如,如果我们需要以这种方式显示包含用户及其资料的网格视图

CREATE OR REPLACE VIEW vw_user_info AS
    SELECT user.*, user_profile.lastname, user_profile.firstname
    FROM user, user_profile
    WHERE user.id = user_profile.user_id

然后您需要创建将代表此视图的 ActiveRecord


namespace app\models\views\grid;

use yii\db\ActiveRecord;

class UserView extends ActiveRecord
{

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return 'vw_user_info';
    }

    public static function primaryKey()
    {
        return ['id'];
    }

    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            // define here your rules
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels()
    {
        return [
            // define here your attribute labels
        ];
    }


}

之后,您可以将此 UserView ActiveRecord 与搜索模型一起使用,而无需额外指定排序和过滤属性。所有属性都将开箱即用。请注意,这种方法有几个优点和缺点

  • 您无需指定不同的排序和过滤条件。一切开箱即用;
  • 由于数据大小和执行的 SQL 查询数量(对于每个关系,您都不需要任何额外的查询),它可能会快得多;
  • 由于这只是一个在 SQL 视图上的简单映射 UI,它缺少实体中的一些领域逻辑,因此如果您有一些方法,例如 isActiveisDeleted 或其他将影响 UI 的方法,您将需要在此类中复制它们。

一个页面上的多个网格视图

您可以在一个页面上使用多个网格视图,但需要一些额外的配置才能使它们互不干扰。当使用多个网格视图实例时,您必须为生成的排序和分页链接配置不同的参数名称,以便每个网格视图都有其自己的独立排序和分页。您可以通过设置数据提供者的 sortParampageParam 来实现 sortpagination 实例。

假设我们要列出 PostUser 模型,我们已经为它们准备了两个数据提供者 $userProvider$postProvider

use yii\grid\GridView;

$userProvider->pagination->pageParam = 'user-page';
$userProvider->sort->sortParam = 'user-sort';

$postProvider->pagination->pageParam = 'post-page';
$postProvider->sort->sortParam = 'post-sort';

echo '<h1>Users</h1>';
echo GridView::widget([
    'dataProvider' => $userProvider,
]);

echo '<h1>Posts</h1>';
echo GridView::widget([
    'dataProvider' => $postProvider,
]);

将网格视图与 Pjax 一起使用

Pjax 小部件允许您更新页面的某个部分,而不是重新加载整个页面。您可以使用它来仅在使用过滤器时更新 GridView 内容。

use yii\widgets\Pjax;
use yii\grid\GridView;

Pjax::begin([
    // PJax options
]);
    Gridview::widget([
        // GridView options
    ]);
Pjax::end();

Pjax 也适用于 Pjax 小部件内部的链接以及 Pjax::$linkSelector 指定的链接。但这可能对 ActionColumn 的链接来说是一个问题。为了防止这种情况,当您编辑 ActionColumn::$buttons 属性时,请在链接中添加 HTML 属性 data-pjax="0"

在 Gii 中将网格视图/列表视图与 Pjax 一起使用

从 2.0.5 版本开始,Gii 的 CRUD 生成器有一个名为 $enablePjax 的选项,可以通过 Web 界面或命令行使用。

yii gii/crud --controllerClass="backend\\controllers\PostController" \
  --modelClass="common\\models\\Post" \
  --enablePjax=1

它生成一个 Pjax 小部件,它包装 GridViewListView 小部件。

进一步阅读

发现错别字或您认为此页面需要改进?
在 github 上编辑它 !