數(shù)據(jù)小部件(Data Widgets)

2018-02-24 15:40 更新

數(shù)據(jù)小部件

Yii提供了一套數(shù)據(jù)小部件?widgets?,這些小部件可以用于顯示數(shù)據(jù)。?DetailView?小部件能夠用于顯示一條記錄數(shù)據(jù),?ListView?和GridView?小部件能夠用于顯示一個(gè)擁有分頁、排序和過濾功能的一個(gè)列表或者表格。

DetailView

yii\widgets\DetailView 小部件顯示的是單一 yii\widgets\DetailView::$model 數(shù)據(jù)的詳情。

它非常適合用常規(guī)格式顯示一個(gè)模型(例如在一個(gè)表格的一行中顯示模型的每個(gè)屬性)。 這里說的模型可以是 \yii\base\Model 或者其子類的一個(gè)實(shí)例,例如子類?active record,也可以是一個(gè)關(guān)聯(lián)數(shù)組。

DetailView使用 yii\widgets\DetailView::$attributes 屬性來決定顯示模型哪些屬性以及如何格式化。 可用的格式化選項(xiàng),見?formatter section?章節(jié)。

一個(gè)典型的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,
        ],
        'created_at:datetime', // creation date formatted as datetime
    ],
]);

ListView

yii\widgets\ListView 小部件用于顯示數(shù)據(jù)提供者?data provider?提供的數(shù)據(jù)。 每個(gè)數(shù)據(jù)模型用指定的視圖文件 yii\widgets\ListView::$itemView 來渲染。 因?yàn)樗峁╅_箱即用式的(譯者注:封裝好的)分頁、排序以及過濾這樣一些特性,所以它可以很方便地為最終用戶顯示信息并同時(shí)創(chuàng)建數(shù)據(jù)管理界面。

一個(gè)典型的用法如下例所示:

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>

在上面的視圖文件中,當(dāng)前的數(shù)據(jù)模型?$model?是可用的。另外,下面的這些變量也是可用的:

  • $key:混合類型,鍵的值與數(shù)據(jù)項(xiàng)相關(guān)聯(lián)。
  • $index:整型,是由數(shù)據(jù)提供者返回的數(shù)組中以0起始的數(shù)據(jù)項(xiàng)的索引。
  • $widget:類型是ListView,是小部件的實(shí)例。

假如你需要傳遞附加數(shù)據(jù)到每一個(gè)視圖中,你可以像下面這樣用 yii\widgets\ListView::$viewParams 屬性傳遞鍵值對:

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

在視圖中,上述這些附加數(shù)據(jù)也是可以作為變量來使用的。

GridView

數(shù)據(jù)網(wǎng)格或者說 GridView 小部件是Yii中最強(qiáng)大的部件之一。如果你需要快速建立系統(tǒng)的管理后臺, GridView 非常有用。它從數(shù)據(jù)提供者?data provider?中取得數(shù)據(jù)并使用 yii\grid\GridView::columns 屬性的一組列配置,在一個(gè)表格中渲染每一行數(shù)據(jù)。

表中的每一行代表一個(gè)數(shù)據(jù)項(xiàng)的數(shù)據(jù),并且一列通常表示該項(xiàng)的屬性(某些列可以對應(yīng)于屬性或靜態(tài)文本的復(fù)雜表達(dá)式)。

使用GridView的最少代碼如下:

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

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

上面的代碼首先創(chuàng)建了一個(gè)數(shù)據(jù)提供者,然后使用GridView顯示每一行的每個(gè)屬性,每一行的數(shù)據(jù)是從數(shù)據(jù)提供者取來的。 展現(xiàn)出來的表格封裝了排序以及分頁功能。

表格列

表格的列是通過 yii\grid\Column 類來配置的,這個(gè)類是通過 GridView 配置項(xiàng)中的 yii\grid\GridView::columns 屬性配置的。根據(jù)列的類別和設(shè)置的不同,各列能夠以不同方式展示數(shù)據(jù)。 默認(rèn)的列類是 yii\grid\DataColumn,用于展現(xiàn)模型的某個(gè)屬性, 并且可以排序和過濾。

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        // 數(shù)據(jù)提供者中所含數(shù)據(jù)所定義的簡單的列
        // 使用的是模型的列的數(shù)據(jù)
        'id',
        'username',
        // 更復(fù)雜的列數(shù)據(jù)
        [
            'class' => 'yii\grid\DataColumn', //由于是默認(rèn)類型,可以省略 
            'value' => function ($data) {
                return $data->name; // 如果是數(shù)組數(shù)據(jù)則為 $data['name'] ,例如,使用 SqlDataProvider 的情形。
            },
        ],
    ],
]);

請注意,假如配置中沒有指定 yii\grid\GridView::columns 屬性,那么Yii會試圖顯示數(shù)據(jù)提供者的模型中所有可能的列。

列類

通過使用不同類,網(wǎng)格列可以自定義:

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        [
            'class' => 'yii\grid\SerialColumn', // <-- 這里
            // 你還可以在此配置其他屬性
        ],

除了我們下面將要展開討論的Yii自帶的列類,你還可以創(chuàng)建你自己的列類。

每個(gè)列類是從 yii\grid\Column 擴(kuò)展而來, 從而在配置網(wǎng)格列的時(shí)候,你可以設(shè)置一些公共的選項(xiàng)。

  • yii\grid\Column::header 允許為頭部行設(shè)置內(nèi)容。
  • yii\grid\Column::footer 允許為尾部行設(shè)置內(nèi)容。
  • yii\grid\Column::visible 定義某個(gè)列是否可見
  • yii\grid\Column::content 允許你傳遞一個(gè)有效的PHP回調(diào)來為一行返回?cái)?shù)據(jù),格式如下:

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

你可以傳遞數(shù)組來指定各種容器式的HTML選項(xiàng):

  • yii\grid\Column::headerOptions
  • yii\grid\Column::footerOptions
  • yii\grid\Column::filterOptions
  • yii\grid\Column::contentOptions

數(shù)據(jù)列

yii\grid\DataColumn 用于顯示和排序數(shù)據(jù)。這是默認(rèn)的列的類型, 所以在使用 DataColumn 為列類時(shí),可省略類的指定(譯者注:不需要'class'選項(xiàng)的意思)。

數(shù)據(jù)列的主要配置項(xiàng)是 yii\grid\DataColumn::format 屬性。它的值對應(yīng)于?formatter?application component?應(yīng)用組件里面的一些方法, 默認(rèn)是使用 \yii\i18n\Formatter 應(yīng)用組件:

echo GridView::widget([
    'columns' => [
        [
            'attribute' => 'name',
            'format' => 'text'
        ],
        [
            'attribute' => 'birthday',
            'format' => ['date', 'php:Y-m-d']
        ],
    ],
]); 

在上面的代碼中,text?對應(yīng)于 \yii\i18n\Formatter::asText()。列的值作為第一個(gè)參數(shù)傳遞。在第二列的定義中,date?對應(yīng)于 \yii\i18n\Formatter::asDate()。 同樣地,列值也是通過第一個(gè)參數(shù)傳遞的,而 'php:Y-m-d' 用作第二個(gè)參數(shù)的值。

可用的格式化方法列表,請參照?section about Data Formatting。

數(shù)據(jù)列配置,還有一個(gè)”快捷格式化串”的方法,詳情見API文檔 yii\grid\GridView::columns。 (譯者注:舉例說明,"name:text:Name"?快捷格式化串,表示列名為?name?格式為?text?顯示標(biāo)簽是?Name

動作列

yii\grid\ActionColumn 用于顯示一些動作按鈕,如每一行的更新、刪除操作。

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

可配置的屬性如下:

  • yii\grid\ActionColumn::controller 是應(yīng)該執(zhí)行這些動作的控制器ID。 如果沒有設(shè)置,它將使用當(dāng)前控制器。
  • yii\grid\ActionColumn::template 定義在動作列中使用的構(gòu)建每個(gè)單元格的模板。 在大括號內(nèi)括起來的的令牌被當(dāng)做是控制器的 action 方法ID (在動作列的上下文中也稱作按鈕名稱)。 它們將會被 yii\grid\ActionColumn::$buttons 中指定的對應(yīng)按鈕的關(guān)聯(lián)的渲染回調(diào)函數(shù)替代。 例如,令牌?{view}?將被?buttons['view']?關(guān)聯(lián)的渲染回調(diào)函數(shù)的返回結(jié)果所替換。 如果沒有找到回調(diào)函數(shù),令牌將被替換成一個(gè)空串。默認(rèn)的令牌有?{view} {update} {delete}?。
  • yii\grid\ActionColumn::buttons 是一個(gè)按鈕的渲染回調(diào)數(shù)數(shù)組。數(shù)組中的鍵是按鈕的名字(沒有花括號),并且值是對應(yīng)的按鈕渲染回調(diào)函數(shù)。 這些回調(diào)函數(shù)須使用下面這種原型:

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

    在上面的代碼中,$url?是列為按鈕創(chuàng)建的URL,$model是當(dāng)前要渲染的模型對象,并且?$key?是在數(shù)據(jù)提供者數(shù)組中模型的鍵。

  • yii\grid\ActionColumn::urlCreator 是使用指定的模型信息來創(chuàng)建一個(gè)按鈕URL的回調(diào)函數(shù)。 該回調(diào)的原型和 yii\grid\ActionColumn::createUrl() 是一樣的。 假如這個(gè)屬性沒有設(shè)置,按鈕的URL將使用 yii\grid\ActionColumn::createUrl() 來創(chuàng)建。

復(fù)選框列

yii\grid\CheckboxColumn 顯示一個(gè)復(fù)選框列。

想要添加一個(gè)復(fù)選框到網(wǎng)格視圖中,將它添加到 yii\grid\GridView::$columns 的配置中,如下所示:

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'columns' => [
        // ...
        [
            'class' => 'yii\grid\CheckboxColumn',
            // 你可以在這配置更多的屬性
        ],
    ],

用戶可點(diǎn)擊復(fù)選框來選擇網(wǎng)格中的一些行。被選擇的行可通過調(diào)用下面的JavaScript代碼來獲得:

var keys = $('#grid').yiiGridView('getSelectedRows');
// keys 為一個(gè)由與被選行相關(guān)聯(lián)的鍵組成的數(shù)組

序號列

yii\grid\SerialColumn 渲染行號,以?1?起始并自動增長。

使用方法和下面的例子一樣簡單:

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

數(shù)據(jù)排序

注意:這部分正在開發(fā)中。

數(shù)據(jù)過濾

為了過濾數(shù)據(jù)的 GridView 需要一個(gè)模型?model?來 從過濾表單接收數(shù)據(jù),以及調(diào)整數(shù)據(jù)提供者的查詢對象,以滿足搜索條件。 使用活動記錄?active records?時(shí),通常的做法是 創(chuàng)建一個(gè)能夠提供所需功能的搜索模型類(可以使用?Gii?來生成)。 這個(gè)類為搜索定義了驗(yàn)證規(guī)則并且提供了一個(gè)將會返回?cái)?shù)據(jù)提供者對象的?search()?方法。

為了給?Post?模型增加搜索能力,我們可以像下面的例子一樣創(chuàng)建?PostSearch?模型:

<?php

namespace app\models;

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

class PostSearch extends Post
{
    public function rules()
    {
        // 只有在 rules() 函數(shù)中聲明的字段才可以搜索
        return [
            [['id'], 'integer'],
            [['title', 'creation_date'], 'safe'],
        ];
    }

    public function scenarios()
    {
        // 旁路在父類中實(shí)現(xiàn)的 scenarios() 函數(shù)
        return Model::scenarios();
    }

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

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

        // 從參數(shù)的數(shù)據(jù)中加載過濾條件,并驗(yàn)證
        if (!($this->load($params) && $this->validate())) {
            return $dataProvider;
        }

        // 增加過濾條件來調(diào)整查詢對象
        $query->andFilterWhere(['id' => $this->id]);
        $query->andFilterWhere(['like', 'title', $this->title])
              ->andFilterWhere(['like', 'creation_date', $this->creation_date]);

        return $dataProvider;
    }
}

你可以在控制器中使用如下方法為網(wǎng)格視圖獲取數(shù)據(jù)提供者:

$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' => [
        // ...
    ],
]);

處理關(guān)系型模型

當(dāng)我們在一個(gè)網(wǎng)格視圖中顯示活動數(shù)據(jù)的時(shí)候,你可能會遇到這種情況,就是顯示關(guān)聯(lián)表的列的值,例如:發(fā)帖者的名字,而不是顯示他的?id。 當(dāng)?Post?模型有一個(gè)關(guān)聯(lián)的屬性名(譯者注:?Post?模型中用?hasOne?定義?getAuthor()?函數(shù)) 叫?author?并且作者模型(譯者注:本例的作者模型是?users?)有一個(gè)屬性叫?name,那么你可以通過在 yii\grid\GridView::$columns 中定義屬性名為author.name?來處理。這時(shí)的網(wǎng)格視圖能顯示作者名了,但是默認(rèn)是不支持按作者名排序和過濾的。 你需要調(diào)整上個(gè)章節(jié)介紹的PostSearch?模型,以添加此功能。

為了使關(guān)聯(lián)列能夠排序,你需要連接關(guān)系表,以及添加排序規(guī)則到數(shù)據(jù)提供者的排序組件中:

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

// 連接與 `users` 表相關(guān)聯(lián)的 `author` 表
// 并將 `users` 表的別名設(shè)為 `author`
$query->joinWith(['author' => function($query) { $query->from(['author' => 'users']); }]);
// 使得關(guān)聯(lián)字段可以排序
$dataProvider->sort->attributes['author.name'] = [
    'asc' => ['author.name' => SORT_ASC],
    'desc' => ['author.name' => SORT_DESC],
];

// ...

過濾也需要像上面一樣調(diào)用joinWith方法。你也需要在屬性和規(guī)則中定義該列,就像下面這樣:

public function attributes()
{
    // 添加關(guān)聯(lián)字段到可搜索屬性集合
    return array_merge(parent::attributes(), ['author.name']);
}

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

然后在?search()?方法中,你僅需要添加一個(gè)額外過濾條件:

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

信息:在上面的代碼中,我們使用相同的字符串作為關(guān)聯(lián)名稱和表別名; 然而,當(dāng)你的表別名和關(guān)聯(lián)名稱不相同的時(shí)候,你得注意在哪使用你的別名,在哪使用你的關(guān)聯(lián)名稱。 一個(gè)簡單的規(guī)則是在每個(gè)構(gòu)建數(shù)據(jù)庫查詢的地方使用別名,而在所有其他和定義相關(guān)的諸如:attributes()?和?rules()?等地方使用關(guān)聯(lián)名稱。

例如,你使用?au?作為作者關(guān)系表的別名,那么聯(lián)查語句就要寫成像下面這樣:

$query->joinWith(['author' => function($query) { $query->from(['au' => 'users']); }]);

當(dāng)別名已經(jīng)在關(guān)聯(lián)函數(shù)中定義了時(shí),也可以只調(diào)用?$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],
];

同樣,當(dāng)指定使用 yii\data\Sort::defaultOrder 來排序的時(shí)候,你需要使用關(guān)聯(lián)名稱替代別名:

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

信息:更多關(guān)于?joinWith?和在后臺執(zhí)行查詢的相關(guān)信息, 可以查看?active record docs on joining with relations。

SQL視圖用于過濾、排序和顯示數(shù)據(jù)

還有另外一種方法可以更快、更有用 - SQL 視圖。例如,我們要在?GridView?中顯示用戶和他們的簡介,可以這樣創(chuàng)建 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

然后你需要?jiǎng)?chuàng)建活動記錄模型來代表這個(gè)視圖:

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 [
            // 在這定義你的規(guī)則
        ];
    }

    /**
     * @inheritdoc
     */
    public static function attributeLabels()
    {
        return [
            // 在這定義你的屬性標(biāo)簽
        ];
    }

}

之后你可以使用這個(gè) UserView 活動記錄和搜索模型,無需附加的排序和過濾屬性的規(guī)則。 所有屬性都可開箱即用。請注意,這種方法有利有弊:

  • 你不需要指定不同排序和過濾條件,一切都包裝好了;
  • 它可以更快,因?yàn)閿?shù)據(jù)的大小,SQL查詢的執(zhí)行(對于每個(gè)關(guān)聯(lián)數(shù)據(jù)你都不需要額外的查詢)都得到優(yōu)化;
  • 因?yàn)樵赟QL視圖中這僅僅是一個(gè)簡單的映射UI,所以在你的實(shí)體中,它可能缺乏某方面的邏輯,所以,假如你有一些諸如isActive、isDeleted?或者其他影響到UI的方法, 你也需要在這個(gè)類中復(fù)制他們。

單個(gè)頁面多個(gè)網(wǎng)格視圖部件

你可以在一個(gè)單獨(dú)頁面中使用多個(gè)網(wǎng)格視圖,但是一些額外的配置是必須的,為的就是它們相互之間不干擾。 當(dāng)使用多個(gè)網(wǎng)格視圖實(shí)例的時(shí)候,你必須要為生成的排序和分頁對象配置不同的參數(shù)名,以便于每個(gè)網(wǎng)格視圖有它們各自獨(dú)立的排序和分頁。 你可以通過設(shè)置 yii\data\Sort::sortParam 和 yii\data\Pagination::pageParam,對應(yīng)于數(shù)據(jù)提供者的 yii\data\BaseDataProvider::$sort 和 yii\data\BaseDataProvider::$pagination 實(shí)例。

假如我們想要同時(shí)顯示?Post?和?User?模型,這兩個(gè)模型已經(jīng)在?$userProvider?和?$postProvider?這兩個(gè)數(shù)據(jù)提供者中準(zhǔn)備好, 具體做法如下:

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,
]);

Using GridView with Pjax

注意: 這部分正在開發(fā)中。

待定

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號