Data widgets

Yii предоставляет набор виджетов, которые могут использоваться для отображения данных. Хотя виджет DetailView может использоваться для отображения данных для одной записи, ListView и GridView могут использоваться для отображения списка или таблицы записей данных, предоставляющих такие функции, как разбиение на страницы, сортировка и фильтрация.

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 и вы хотите предотвратить вычисление value в случае, если оно принимает значение false:

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 используется для отображения данных от поставщика данных. Каждая модель данных визуализируется с использованием указанного файла представления. Поскольку он предоставляет такие функции, как разбиение на страницы, сортировка и фильтрация из коробки, это удобно как для отображения информации конечным пользователям, так и для создания пользовательского интерфейса управления данными.

Типичное использование:

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: mixed, значение ключа, связанное с элементом данных.
  • $index: integer, индекс индекса элемента данных в массиве items, возвращаемый поставщиком данных.
  • $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 для отображения каждого атрибута в каждой строке, взятой от поставщика данных. Отображаемая таблица оснащена функциями сортировки и разбивки на страницы.

Grid columns

Столбцы таблицы сетки настраиваются в терминах классов yii\grid\Column, которые настраиваются в свойстве columns конфигурации GridView. В зависимости от типа столбца и настроек они могут представлять данные по-разному. Класс по умолчанию - 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.
            },
        ],
    ],
]);

Обратите внимание: если часть столбцов конфигурации не указана, Yii пытается отобразить все возможные столбцы модели поставщика данных.

Column classes

Столбцы сетки могут быть настроены с использованием разных классов столбцов:

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-контейнера, передав массивы:

  • headerOptions
  • footerOptions
  • filterOptions
  • contentOptions

Data column

Столбец данных используется для отображения и сортировки данных. Это тип столбца по умолчанию, поэтому указывающий класс может быть опущен при его использовании.

Основной настройкой столбца данных является его свойство формата. Его значения соответствуют методам в компоненте приложения форматирования, который по умолчанию является Formatter:

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

В приведенном выше тексте text соответствует yii\i18n\Formatter::asText(). Значение столбца передается в качестве первого аргумента. Во втором определении столбца date соответствует yii\i18n\Formatter::asDate(). Значение столбца, опять же, передается как первый аргумент, а в качестве второго аргумента используется «php: Y-m-d».

Список доступных форматеров см. В разделе о форматировании данных.

Для настройки столбцов данных имеется также формат ярлыков, который описан в документации API для столбцов.

Action column

В Action column отображаются кнопки действий, такие как обновление или удаление для каждой строки.

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

Доступные свойства, которые вы можете настроить:

  • controller - это идентификатор контроллера, который должен обрабатывать действия. Если он не установлен, он будет использовать текущий активный контроллер.
  • template определяет шаблон, используемый для создания каждой ячейки в столбце действия. Токены, заключенные в фигурные скобки, рассматриваются как идентификаторы действия контроллера (также называемые именами кнопок в контексте столбца действий). Они будут заменены соответствующими обратными вызовами рендеринга кнопок, указанными в кнопках. Например, токен {view} будет заменен на результат кнопок обратного вызова ['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')
]

Checkbox column

Столбец Checkbox отображает столбец флажков.Чтобы добавить столбец Checkbox в GridView, добавьте его в конфигурацию колонок следующим образом:

echo GridView::widget([
    '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

Serial column

Серийный столбец отображает номера строк, начиная с 1, и идет вперед. Использование так просто, как следующее:

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

Фильтрация данных

Для фильтрации данных GridView нужна модель, которая представляет критерии поиска, которые обычно берутся из полей фильтра в таблице GridView. Обычной практикой при использовании активных записей является создание поискового класса Model, который предоставляет необходимую функциональность (он может быть сгенерирован вами 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;
    }
}

Вы можете использовать эту функцию в контроллере, чтобы получить 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]) ?>

Отдельная форма фильтра полезна, когда вам нужно фильтровать по полям, которые не отображаются в GridView или для специальных условий фильтрации, таких как диапазон дат. Для фильтрации по диапазону дат мы можем добавить в поисковую модель атрибуты не-БД createdFrom и createdTo:

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, введенную в предыдущем разделе, чтобы добавить эту функциональность.

Чтобы включить сортировку в связанном столбце, вам необходимо присоединиться к связанной таблице и добавить правило сортировки к компоненту Сортировка поставщика данных:

$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')]);

Использование представлений SQL для фильтрации, сортировки и отображения данных

Существует также другой подход, который может быть более быстрым и полезным - представления SQL. Например, если нам нужно показать gridview с пользователями и их профилями, мы можем сделать это следующим образом:

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 static function attributeLabels()
    {
        return [
            // define here your attribute labels
        ];
    }


}

После этого вы можете использовать эту активную запись UserView с поисковыми моделями без дополнительной спецификации атрибутов сортировки и фильтрации. Все атрибуты будут работать из коробки. Обратите внимание, что этот подход имеет несколько плюсов и минусов:

  • Вам не нужно указывать различные условия сортировки и фильтрации. Все работает из коробки;
  • Это может быть намного быстрее из-за размера данных, количества выполненных SQL-запросов (для каждого отношения вам не понадобится дополнительный запрос);
  • Поскольку это всего лишь простой интерфейс отображения в представлении sql, в нем отсутствует какая-либо логика домена, находящаяся в ваших сущностях, поэтому если у вас есть некоторые методы, такие как isActive, isDeleted или другие, которые будут влиять на пользовательский интерфейс, вам нужно будет дублировать их в этом классе слишком.

Несколько GridView на одной странице

Вы можете использовать более одного GridView на одной странице, но требуется дополнительная конфигурация, чтобы они не мешали друг другу. При использовании нескольких экземпляров GridView вам необходимо настроить различные имена параметров для сгенерированных ссылок сортировки и разбивки на страницы, чтобы каждый GridView имел свою собственную индивидуальную сортировку и разбиение на страницы. Это можно сделать, установив sortParam и pageParam для экземпляров sort и pagination dataProvider.

Предположим, мы хотим перечислить модели Post и User, для которых мы уже подготовили двух поставщиков данных в $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,
]);

Использование GridView с 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. Чтобы предотвратить это, добавьте атрибут HTML data-pjax="0" к ссылкам при изменении свойства ActionColumn::$buttons.

GridView / ListView с Pjax в Gii

Генератор CRUD в Gii имеет опцию $enablePjax, которую можно использовать либо через веб-интерфейс, либо через командную строку.

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

Что генерирует виджет Pjax, обертывающий виджеты GridView или ListView.