Вот и настал долгожданный момент практики. Кто не читал предыдущий урок, обязательно с ним ознакомьтесь. В нем говорится о теории gridView (ссылка: здесь). А теперь я бы хотел предложить вам реализовать следующий функционал (можно не читать, а сразу перейти к Миграции):
1 Вывод таблицы типа Stripped (полосатая): когда одна строка имеет фон, а следующая - нет
2 Один из фильтров показывает данные по выбранным чекбоксам
3 Один из фильтров имеет выпадающий список
4 Одна из колонок содержит отформатированную дату
5 Одна из колонок содержит изображение
6 Если данные пустые, то мы закрашиваем ячейку в красный цвет
7 Обрезать длинные тексты, если они превышают 10 знаков
8 В футере вывести пояснения к ячейкам
9 Разместить 2 пагинации: одну - сверху, а другую-снизу.
10 Сделать заголовок синим
11 Сделать текст футера серого цвета
12 Вывести ошибки с фильтра вверх в общий контейнер
13 Изменить текст количества отображаемых элементов на странице
14 Если запись удалена, в ячейке использовать крестик, если активная - галочку
Структура базы данных, Миграции
Как и обычно, начнем с миграции базы данных. Дадим название БД: gridview. Напишем поочередно 2 команды:
php yii migrate/create books_init
и
php yii migrate/create books_insert
Будет создано 2 файла в папке /migrations. Поменяем функции safeUp и safeDown поочередно в каждом файле. Наполнение вы найдете в моем репозитории: github. Для примера можете посмотреть, как мы выполняли данные действия в этом уроке: Простое приложение на Yii2 и Angular 2 Часть1
Для файла books_init:
<?php
use yii\db\Migration;
/**
* Class m180406_065815_books_init
*/
class m180406_065815_books_init extends Migration
{
public function safeUp()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE=InnoDB';
}
$this->createTable('{{%books}}', [
'id' => $this->primaryKey(),
'title' => $this->string()->notNull(),
'src' => $this->string(),
'author' => $this->string()->notNull(),
'description' => $this->text(),
'created_at' => $this->integer()->notNull(),
'delete' => $this->smallInteger()->notNull()->defaultValue(0),
], $tableOptions);
}
public function safeDown()
{
$this->dropTable('{{%books}}');
}
}
Для файла books_insert:
<?php
use yii\db\Migration;
/**
* Class m180406_065902_books_insert
*/
class m180406_065902_books_insert extends Migration
{
public function safeUp()
{
$insert = [
[
'title' => 'Ленинград. Невероятная и правдивая история группы',
'src' => 'http://ozon-st.cdn.ngenix.net/multimedia/c300/1019082045.jpg',
'author' => 'Семеляк Максим Анатольевич',
'description' => 'Книга, которую ждут миллионы - АВТОРИЗОВАННАЯ БИОГРАФИЯ ГРУППЫ ЛЕНИНГРАД! В этом году возрожденному "Ленинграду" исполняется 20 лет. Десятки альбомов, сотни видеоклипов, миллионы просмотров и скачиваний. "Ленинград" создает хиты и мемы. Творчество Сергея Шнурова обсуждают и осуждают, хвалят и ругают. Его любят и ненавидят, но равнодушных к песням Шнура просто нет. В книге "Ленинград. Невероятная и правдивая история" писатель и журналист, друг Сергея Шнурова, Максим Семеляк попытался объяснить феномен Шнура. И честно рассказать о том, как самая популярная группа страны работала все эти годы. В книгу включены размышления Сергея Шнурова, его интервью, заметки и комментарии. А также очень много фотографий из личных альбомов, многие из которых ранее нигде не публиковались.',
'created_at' => '1371201284',
'delete' => 0,
],
[
'title' => 'Цветы для Элджернона',
'src' => 'http://ozon-st.cdn.ngenix.net/multimedia/c300/1012950149.jpg',
'author' => 'Дэниел Киз',
'description' => '"Цветы для Элджернона" Дэниела Киза входят в программу обязательного чтения в американских школах. Это единственная история в жанре научной фантастики, автор которой был дважды награжден сначала за рассказ, а потом за роман с одним и тем же названием, героем, сюжетом. ',
'created_at' => '1427966084',
'delete' => 0,
],
[
'title' => 'В Питере жить. От Дворцовой до Садовой, от Гангутской до Шпалерной. Личные истории',
'src' => 'http://ozon-st.cdn.ngenix.net/multimedia/c300/1018537441.jpg',
'author' => 'Елена Данииловна Шубина',
'description' => '"В Питере жить" - это вам не в Москве, о которой нам рассказали в книге-бестселлере "Москва: место встречи". Что и говорить - другая ментальность, петербургский текст. Евгений Водолазкин, Андрей Аствацатуров, Борис Гребенщиков, Елизавета Боярская, Андрей Битов, Михаил Пиотровский, Елена Колина, Михаил Шемякин, Татьяна Москвина, Валерий Попов, "митёк" Виктор Тихомиров, Александр Городницкий и многие другие "знаковые лица" города на Неве - о питерских маршрутах и маршрутках, дворах-колодцах и дворцах Растрелли, Васильевском острове, Московском проспекте и платформе Ржевка, исчезнувшем в небытии Введенском канале и «желтом паре петербургской зимы"…
Книга иллюстрирована акварелями Лизы Штормит и рисунками Виктора Тихомирова, на переплете - офорт Михаила Шемякина. ',
'created_at' => '1458304450',
'delete' => 0,
],
[
'title' => 'Семь навыков высокоэффективных людей.',
'src' => 'http://ozon-st.cdn.ngenix.net/multimedia/c300/1018960911.jpg',
'author' => 'Стивен Р. Кови',
'description' => 'Во-первых, эта книга излагает системный подход к определению жизненных целей, приоритетов человека. Эти цели у всех разные, но книга помогает понять себя и четко сформулировать жизненные цели. Во-вторых, книга показывает, как достигать этих целей. И в-третьих, книга показывает, как каждый человек может стать лучше. Причем речь идет не об изменении имиджа, а о настоящих изменениях, самосовершенствовании. Книга не дает простых решений и не обещает мгновенных чудес. Любые позитивные изменения требуют времени, работы и упорства. Но для людей, стремящихся максимально реализовать потенциал, заложенный в них природой, эта книга - дорожная карта.',
'created_at' => '1467462131',
'delete' => 1,
],
[
'title' => 'Приключения Эмиля из Леннеберги',
'src' => 'http://ozon-st.cdn.ngenix.net/multimedia/c300/1007505349.jpg',
'author' => 'Emil i Lonneberga',
'description' => 'Веселая повесть про Эмиля из Леннеберги, которую написала замечательная шведская писательница Астрид Линдгрен, а на русский язык блистательно пересказала Лилианна Лунгина, полюбилась и взрослым и детям всей планеты. Этот вихрастый мальчуган - ужасный озорник, он и дня не проживет, не напроказничав. Ну кому придет в голову гонять кошку, чтобы проверить, хорошо ли она прыгает?! Или надеть на себя супницу? Или поджечь перо на шляпе у пасторши? Или поймать в крысоловку родного отца, а поросенка накормить пьяными вишнями?..',
'created_at' => '1460203863',
'delete' => 0,
],
[
'title' => 'English Grammar in Use with Answers',
'src' => Null,
'author' => 'Рэймонд Мерфи',
'description' => 'English Grammar in Use Fourth Edition is an updated version of the best-selling grammar title.
This new edition with answers:
has a fresh, appealing new design and clear layout, with revised and updated examples;
is arranged in a tried-and-trusted, easy to use format, with explanations of grammar points on each left-hand page and exercises to check understanding on the right;
is perfect for independent studying and the study guide helps learners to identify which language points to focus on;
contains lots of additional practice exercises to consolidate learning.',
'created_at' => '1496315482',
'delete' => 0,
]
];
$this->batchInsert('{{%books}}', [
'title',
'src',
'author',
'description',
'created_at',
'delete',
], $insert);
}
public function safeDown()
{
$this->truncateTable('{{%books}}');
}
}
После наполнения выполните команду:
php yii migrate
Теперь наша база данных готова!
Модели и контроллеры
Далее нам нужно создать базовую модель (в нашем случае это Books) и ту, которая будет работать с ActiveDataProvider, чтобы организовать фильтрацию, сортировку и пагинацию. Приступим к созданию главной модели: Books. Расположить ее нужно в папке /models. Листинг этого кода вы найдете на github
Также в этой папке рядом нужно создать файл: BooksSearch.php. Здесь я прокомментирую листинг кода
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\Books;
/**
* BooksSearch represents the model behind the search form of `app\models\Books`.
*/
class BooksSearch extends Books
{
/**
* @inheritdoc
*/
public function rules()
{
return [
[['id', 'created_at', 'delete'], 'integer'],
[['title', 'src', 'author', 'description'], 'safe'],
];
}
/**
* @inheritdoc
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = Books::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'created_at' => $this->created_at,
'delete' => $this->delete,
]);
$query->andFilterWhere(['like', 'title', $this->title])
->andFilterWhere(['like', 'src', $this->src])
->andFilterWhere(['IN', 'author', $this->author])
->andFilterWhere(['like', 'description', $this->description]);
return $dataProvider;
}
}
Наш класс BooksSearch наследуется от класса Books. Это нужно для того, чтобы получить доступ к важному классу Model, на котором и держится наша валидация, а также, при возможности, дополнительные функции и свойства родителя. Например, различные связи (relations)
Ниже идет полностью переопределенный метод rules. Так как нам не нужно здесь правило required, нам важно, чтобы значения в принципе отправлялись (это касается фильтра) и имели нужный нам тип (например, чтобы защититься от SQL инъекции) Замечу, что благодаря свойствам этой модели и строится фильтр. Если свойства в этой модели не будет, то и поле в фильтре вы не увидите. Говоря, простым языком: все, что есть в function rules(), будет работать в фильтрации. И логично предположить: все, что отправляется из фильтра, попадает сюда. Сценарии мы просто пропустим, там ничего интересного, и сразу переходим к методу search()
В методе search() работает основная логика отображения данных. Во-первых, обратите внимание на переменную $query - это instance запроса ActiveRecord (то есть, select, where, join), НО! Здесь не должны использоваться методы ->all(), ->one(), ->count() Это очень важно! За вас всё сделает ActiveDataProvider. Там и будут работать count при построении пагинации и offset() и и выборка данных на основе условий и прочее.
Вот мы и дошли до самой основной переменной $dataProvider. Это результат класса ActiveDataProvider(). Если вам интересно, вы можете посмотреть, что там внутри. В двух словах: там настройки пагинации, сортировки и много всего остального. Можете вкратце ознакомиться с гайдом (тут все нормально написано: ссылка)
В целом, вы его можете подкрутить в плане: в каком порядке выводить данные при инициализации и по сколько записей на странице.
Теперь обратите внимание на конструкцию:
$this->load($params);
Она означает, что параметры, пришедшие с фильтра, "вгрузились" в модель, и с ее свойствами можно будет работать (если они валидны конечно).
Далее идет конструкция:
if (!$this->validate()) {
Что в буквальном смысле означает: если не валидно, то просто проинициализировать $dataProvider (по умолчанию)
Но если модель валидная и реально пришли параметры, то мы можем повлиять на переменную $query. Так вот $query->andFilterWhere означает: если в модель удалось вгрузить это свойство, то оно участвует в выдаче, если нет, то ничего не происходит, и данный метод не отработает. Давайте разберем на примере: пусть пришел параметр "delete", то есть пользователь выбрал в фильтре что-то связанное с удалением. Это значит, что будет построен SQL запрос: SELECT * FROM gridview WHERE delete = 1. Если же этого параметра не пришло, то ничего в "where" не попадет.
Теперь осталось создать контроллер, заходите в /controllers/SiteController.php найдите там actionIndex (63 строчка), добавьте следующее:
$searchModel = new BooksSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]);
Я здесь заострю внимание на Yii::$app->request->queryParams. Образно говоря: взять все параметры и попытаться вгрузить их в метод search(). Там он уже будет разбираться и валидировать все. Теперь переходим к самому пикантному файлу: /views/site/index.php. Там находится основной код построение gridView виджета
View
<?php
use yii\helpers\Html;
use yii\grid\GridView;
/* @var $this yii\web\View */
/* @var $searchModel app\models\BooksSearch */
/* @var $dataProvider yii\data\ActiveDataProvider */
$this->title = 'Books';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="books-index">
<h1><?= Html::encode($this->title) ?></h1>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
<p>
<?= Html::a('Create Books', ['create'], ['class' => 'btn btn-success']) ?>
</p>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'tableOptions' => ['class' => 'table'],
'showFooter' => true,
'layout' => "{errors}\n{pager}\n{items}\n{pager}\n{summary}",
'summary' => '<i>Сейчас вы видите от </i> <b>{begin}</b> до <b>{end}</b> записей (всего: <b style="color: #090">{totalCount}</b>)',
'headerRowOptions' => [
'class' => 'headerCustomRow'
],
'footerRowOptions' => [
'style' => 'color: #888;font-style: italic;'
],
'rowOptions' => function($model, $key, $index){
return [
'class' => ($index % 2 == 0) ? 'stripShow' : ''
];
},
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
[
'attribute' => 'title',
'contentOptions' => function($model){
return [
'class' => (empty($model->title)) ? 'empty-cell' : ''
];
},
'footer' => 'Здесь рассказывается о заголовоке книги'
],
[
'attribute' => 'src',
'format' => 'image',
'filter' => false,
'contentOptions' => function($model){
return [
'class' => (empty($model->src)) ? 'empty-cell' : ''
];
},
'footer' => 'Куда же без картинки?'
],
[
'attribute' => 'author',
'filter' => Html::activeCheckboxList($searchModel, 'author',
\yii\helpers\ArrayHelper::map(\app\models\Books::find()->all(), 'author', 'author')),
'contentOptions' => function($model){
return [
'class' => (empty($model->author)) ? 'empty-cell' : ''
];
},
'footer' => 'Самые любимые и уважаемые всеми, авторы данных книг'
],
[
'attribute' => 'description',
'content' => function($model){
return \yii\helpers\StringHelper::truncateWords($model->description, 10);
},
'footer' => 'Полное описание, к каждому произведению, чтобы читатель сразу мог понять о чем книга'
],
[
'attribute' => 'created_at',
'format' => ['date', 'php:d/m/Y'],
'contentOptions' => function($model){
return [
'class' => (empty($model->created_at)) ? 'empty-cell' : ''
];
}
],
//'delete',
[
'attribute' => 'delete',
'format' => 'raw',
'filter' => [
'Активные',
'В корзине'
],
'contentOptions' => [
'width' => '150px'
],
'content' => function($model){
return ($model->delete == 0) ? '<span style="color:#090" class="glyphicon glyphicon-ok"></span>' :
'<span style="color:#c11" class="glyphicon glyphicon-remove"></span>';
}
],
],
]); ?>
</div>
<style>
.empty-cell{
background: #c12e2a;
}
.headerCustomRow, .headerCustomRow th, .headerCustomRow a{
background: #337ab7;
color: #fff;
}
.stripShow{
background: #e9f1ff
}
</style>
Теперь давайте на примере кода ответим на все вопросы, которые мы задали себе вначале:
1 Вывод таблицы типа Stripped (полосатая): когда одна строка имеет фон, а следующая - нет
Понятное дело, мы могли это сделать на CSS. Более того это уже сделано по умолчанию, но мы тренируемся в работе функций виджета, поэтому для начала я отключил общий класс таблицы, сделал обычный ее вариант:
'tableOptions' => ['class' => 'table'],
И сама функция, которая нам нужна это rowOptions
'rowOptions' => function($model, $key, $index){
return [
'class' => ($index % 2 == 0) ? 'stripShow' : ''
];
},
Мы берем каждый индексный номер и пытаемся нацело поделить на 2. Если делится без остатка, то добавляем класс к stripShow. Ниже я описал это свойство в теге <style>
2 Один из фильтров показывает данные по выбранным чекбоксам
Все что нам нужно здесь, это в колонке определить свойство filter. Для этих целей я буду использовать html-хелпер:
'filter' => Html::activeCheckboxList($searchModel, 'author',
\yii\helpers\ArrayHelper::map(\app\models\Books::find()->all(), 'author', 'author')),
ActiveCheckboxList принимаем модель, название атрибута, а также массив, данный для построения чекбокса. В нашем случае это массив вида:
[ 'Дэниел Киз' => 'Дэниел Киз' ]
где ключ используется для передачи в модель, а значение - для вывода в чекбокс лист. Также обратите внимание на ArrayHelper. Функция map выберет только те значения, которые нам нужны из выборки
3 Один из фильтров имеет выпадающий список
Здесь вообще все просто: также переопределяем фильтр, только задаем обычный массив:
'filter' => [ 'Активные', 'В корзине' ],
Еще я добавил свойство, которое фиксирует ширину ячейки, чтобы текст внутри select'a хорошо читался:
'contentOptions' => [ 'width' => '150px' ],
4 Одна из колонок содержит отформатированную дату
Используем встроенный форматер, а для представления формат php дат:
'attribute' => 'created_at',
'format' => ['date', 'php:d/m/Y'],
5 Одна из колонок содержит изображение
Повторяем предыдущие действия: используем форматтер, а фильтр вообще отключим:
'attribute' => 'src',
'format' => 'image',
'filter' => false,
6 Если данные пустые, то мы закрашиваем ячейку в красный цвет
Решение, которое я вам предложу мне не очень нравится. Оно получается несколько громоздким, но, тем не менее, иных вариантов я не нашел. Если есть идеи, пишите ваши предложения. Мое решение - добавить к каждой ячейке данное свойство:
'contentOptions' => function($model){
return [
'class' => (empty($model->src)) ? 'empty-cell' : ''
];
},
Соответветственно, ниже вы найдете опеределение этого CSS класса
7 Обрезать длинные тексты, если они превышают 10 знаков
Для этих целей отлично подойдет встроенный StringHelper, а свойство, которое нам нужно - это контент, отвечающий за вывод данных в ячейку:
'content' => function($model){
return \yii\helpers\StringHelper::truncateWords($model->description, 10);
},
8 В футере вывести пояснения к ячейкам
Для начала нам нужно активировать сам футер:
'showFooter' => true,
А теперь в каждой ячейке можно написать пояснение:
'footer' => 'Здесь рассказывается о заголовоке книги'
9 Разместить 2 пагинации, одну сверху другую снизу И 12 Вывести ошибки с фильтра вверх в общий контейнер
Я объединил два вопроса в один, так как они решаются на уровне layout:
'layout' => "{errors}\n{pager}\n{items}\n{pager}\n{summary}",
ошибки - это {errors} - теперь можно проверить попробуйте в фильтер created_at передать строку.
{pager} - это пагинации. Для того, чтобы ее увидеть, я бы предложил зайти в BooksSearch и найти там наш ActiveDataProvider, добавить лимит на отображаемые записи, например, 5. Должно получиться как-то так:
$dataProvider = new ActiveDataProvider([
'query' => $query,
'Pagination' => [
'pageSize' => 5
]
]);
10 Сделать заголовок синим и 11 Сделать текст футера серого цвета
Я также объединил 2 этих вопроса. Для заголовка это свойство headerRowOptions и для футера footerRowOptions (но не забывайте, что свойство showFooter должно быть true). Получилось вот так:
'headerRowOptions' => [ 'class' => 'headerCustomRow' ], 'footerRowOptions' => [ 'style' => 'color: #888;font-style: italic;' ],
CSS свойство headerCustomRow задано ниже
13 Изменить текст количества отображаемых элементов на странице
'summary' => 'Сейчас вы видите от {begin} до {end} записей (всего: {totalCount})',
Здесь, я думаю, все понятно, и отсутствует необходимость в комментариях.
14 Если запись удалена в ячейке использовать - крестик, если активная то - галочку
Также пользуемся свойством content в внутри ячейки:
'content' => function($model){
return ($model->delete == 0) ? '' :
'';
}
В моем случае выводится glyphicon, и, соответственно, условие: если delete == 0, то мы выводим галочку, если 1 - крестик.
Заключение
На этом все. Если вы нашли какие-то неточности или даже ошибки, то обязательно пишите о них мне, а я буду исправлять и делать для вас контент лучше. Лучи добра!
Содержание
Облако тегов
Следующая статья
Bootstrap 4.1 – модальное окно
Добро пожаловать! При построении модальных окошек используются HTML, JS, и CSS. Они выводятся поверх всего контента, при этом скролл работает только внутри всплывающего окна.