Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
vp-webdev
7 лет назад

Создание сайта на фреймворке 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

Первая часть - вступление
Вторая часть - модели

22
61.442 GOLOS
На Golos с August 2017
Комментарии (5)
Сортировать по:
Сначала старые