Создание сайта на фреймворке Yii2, часть 3 - Контроллеры, виджеты
Контроллеры предназначены для создания URL-адресов страниц сайта. Каждый метод контроллера, имя которого начинается с action - будет урлом страницы.
Чтобы не нагородить лишнего - опять чётко сформулируем что и как мы хотим выводить:
- Список постов
- Список аккаунтов
На данный момент пока всё.
Создание контроллера
Для этих целей создадим 2 контроллера - PostController и AccountController. Как и с моделью - я воспользуюсь Gii.
Открываем /index.php?r=gii и выбираем создание контроллера.
Controller Class пишем app\controllers\PostController
После генерации будет создан файл контроллера и файл представления.
После создания контроллера у вас сразу же будет доступна страница /index.php?r=post. В её заголовке будет текущий роут, а в теле ссылка на шаблон представления.
Сейчас основной работой будет формирование выборки постов и выдача их на странице.
Так как посты будут выдаваться постранично - создадим настройку, в которой будет указано количество постов на странице, пусть это будет 10.
Для этого я добавил переменную в файл /config/params.php
return [
'adminEmail' => 'admin@example.com',
'posts' => [
'on_page' => 10
],
];
Начнём работать с методом actionIndex в файле PostController.php
public function actionIndex()
{
$q = Post::find()
->limit(\Yii::$app->params['posts']['on_page']);
$posts = $q->all();
return $this->render('index', ['posts' => $posts]);
}
В файле /views/post/index.php пишем
<?php
if (!empty($posts)) {
print_r($posts);
}
?>
Проверяем. Данные из моделей успешно вывелись.
Так как фильтров до четырёх, причём они необязательны - я буду передавать их GET параметрами.
Фильтры:
- авторы
- кроме авторов
- тэги
- кроме тэгов
Для передачи фильтров в URL - я создам форму. На первом этапе будем просто вписывать id тэгов и ники авторов, потом сделаем всё юзабельным.
Создание виджета с формой
Форму сделаем вручную. Давайте оформим её в виде виджета.
В корне сайта создадим папку widgets.
В ней создадим файл PostFilterForm.php с содержимым:
namespace app\widgets;
class PostFilterForm extends \yii\bootstrap\Widget {
public function init()
{
parent::init();
}
public function run() {
echo $this->render('PostFilterForm/main');
}
}
Рядом с файлом создадим папку views и в ней папку PostFilterForm и в нём файл main.php - это будет шаблон виджета - именно этот "путь" мы передаём методу render в методе run класса PostFilterForm
В файле main.php выводим форму. Пишем обычный html код.
<form action="" method="get">
<p>включить аккаунты</p>
<input name="accounts" value="">
<p>исключить аккаунты</p>
<input name="ex_accounts" value="">
<p>включить тэги</p>
<input name="tags" value="">
<p>исключить тэги</p>
<input name="ex_tags" value="">
<input name="r" value="post/index" type="hidden">
<br /><br />
<input type="submit" value="Submit">
</form>
Я пока вывел урл страницы в скрытое поле, а то форма перекидывала на главную страницу сайта.
Теперь выведем виджет на странице post.
Сделать это можно прямо в шаблоне /views/post/index.php так:
echo \app\widgets\PostFilterForm::widget();
Проверяем:
В классе виджета(PostFilterForm) будем считывать GET переменные и отображать их в форме.
//в шапке файла
use \yii\helpers\BaseHtml;
public function run() {
//принимаем GET переменные
$accounts = BaseHtml::encode(\Yii::$app->request->get('accounts', ''));
$ex_accounts = BaseHtml::encode(\Yii::$app->request->get('ex_accounts', ''));
$tags = BaseHtml::encode(\Yii::$app->request->get('tags', ''));
$ex_tags = BaseHtml::encode(\Yii::$app->request->get('ex_tags', ''));
//вторым аргументом передаём переменные в шаблон
echo $this->render(
'PostFilterForm/main',
[
'data' => [
'accounts' => $accounts,
'ex_accounts' => $ex_accounts,
'tags' => $tags,
'ex_tags' => $ex_tags,
]
]
);
}
В шаблоне /widgets/views/PostFilterForm/main.php выводим переменные:
<form action="" method="get">
<p>включить аккаунты</p>
<input name="accounts" value="<?= $data['accounts'] ?>">
<p>исключить аккаунты</p>
<input name="ex_accounts" value="<?= $data['ex_accounts'] ?>">
<p>включить тэги</p>
<input name="tags" value="<?= $data['tags'] ?>">
<p>исключить тэги</p>
<input name="ex_tags" value="<?= $data['ex_tags'] ?>">
<input name="r" value="post/index" type="hidden">
<br /><br />
<input type="submit" value="Submit">
</form>
Как видите - я специально попытался "поломать форму" - вёрстка не нарушена.
Формирование запроса к БД
Ниже я приведу листинг метода actionIndex в классе PostController.
В нём я беру переменные с адресной строки, и на их основании формулирую SQL-запрос. Код в функции документирован.
public function actionIndex()
{
//инициализация запроса
$q = Post::find()
->limit(\Yii::$app->params['posts']['on_page']);
//получение переменных
$accounts = BaseHtml::encode(\Yii::$app->request->get('accounts', ''));
$ex_accounts = BaseHtml::encode(\Yii::$app->request->get('ex_accounts', ''));
$tags = BaseHtml::encode(\Yii::$app->request->get('tags', ''));
$ex_tags = BaseHtml::encode(\Yii::$app->request->get('ex_tags', ''));
//превращение строк в массивы
if (!empty($accounts)) {
$accounts = explode(',', $accounts);
}
if (!empty($ex_accounts)) {
$ex_accounts = explode(',', $ex_accounts);
}
if (!empty($tags)) {
$tags = explode(',', $tags);
}
if (!empty($ex_tags)) {
$ex_tags = explode(',', $ex_tags);
}
$q->where(['>', 'created', 0]);
//фильтр по авторам
if (!empty($accounts)) {
$q->andWhere(['IN', 'author', $accounts]);
}
if (!empty($ex_accounts)) {
$q->andWhere(['NOT IN', 'author', $ex_accounts]);
}
//фильтр по тэгам
if (!empty($tags)) {
$filter_array = ['or'];
foreach ($tags as $tag) {
$filter_array[] = ['LIKE', 'tags', '*' . $tag . '*'];
}
$q->andFilterWhere($filter_array);
}
if (!empty($ex_tags)) {
foreach ($ex_tags as $ex_tag) {
$q->andFilterWhere(['NOT LIKE', 'tags', '*' . $ex_tag . '*']);
}
}
//сортировка
$q->orderBy(['post_id' => SORT_DESC]);
$posts = $q->all();
return $this->render('index', ['posts' => $posts]);
}
В шаблоне контроллера /views/post/index.php выводим посты:
if (!empty($posts)) {
foreach ($posts as $post) {
echo $post->title.'<br>';
echo date('Y.m.d H:i', $post->created).'<br>';
echo $post->tags.'<br>';
echo '<hr />';
}
}
Теперь попробуем отфильтровать посты по двум аккаунтам, двум тэгам и исключить по двум другим тэгам.
У меня в БД тэги пронумерованы:
- 809 - ru--blokcheijn
- 1144 - ru--otkrytyij-kod
- 805 - golos
- 804 - ru--golos
Под датой поста я вывел номера тэгов. Если вы обратите внимание - то в этой строке обязательно есть либо 809, либо 1144, и точно нет 805 и 804(в БД посты с тэгами тэгам точно есть).
Получается фильтр работает корректно.
Более подробно о работе представлений будет в следующем уроке.
Заглавное фото с сайта kwork.ru
Первая часть - вступление
Вторая часть - модели