Работа с Yii2. Модели и Контроллеры
Вступление
О! Эти вечные проблемы с подбором КДПВ (Картинки для привлечения внимания). Было сто идей, что нужно сделать, но готовых бесплатных картинок я не нашел, более того, даже нормальных платных я не нашел. Заморочился колхозить сам. Поэтому — получай, фашист, гранату! Вот вам моя картинка =)
Постановка задачи
Это третья часть изучения yii2. У меня есть развернутое и настроенное приложение на Yii2, и есть база данных с необходимыми таблицами. Цель этой части подружить одно с другим. Framework уже видит нашу базу, но пока ничего делать с ней не может: ни отображать данные, ни добавлять новые, ни удалять или изменять.
Построение моделей
Думаю, что тебе не надо рассказывать про MVC-парадигму, но если кратко, то это вот так: Модель (M — model «Но, Холмс! Как вы догадались?!») — это данные, структура которая работает с данными, в нашем случае с БД; Представление (V — view) — структура, которая отображает данные и состояния, если очень грубо, то это шаблон, верстка страницы; Контроллер (C — controller) — это прослойка, которая обеспечивает взаимодействие между моделью и представлением, когда пользователь что-то совершает на странице и ожидает какого-то ответа от системы, в дело вступает контроллер.
Когда-то давно, в умных книжках услышал фразу «Толстые модели и тонкие контроллеры». Интересно, какая картинка в вашей голове? =). Я понял эту идею следующим образом: вся бизнес-логика (бизнес от слова дело, а не от слова барыга) ложится в модель, а контроллер исключительно готовит данные для модели, когда они идут от пользователя, или для представления, в обратном случае.
Думаю, можно прекратить теоретический курс. Сделаем нашу толстую модель!
Всё можно писать ручками, и вероятно, для понимания работы архитектуры это будет много эффективнее. Но я учусь, мне можно. Не буду ходить вокруг да около: открываю адрес finansomer.my/gii и моему взору открывается истина и все тайны вселенной — Gii.
Сам про себя он говорит, что может сам написать код для меня, это так мило с его стороны, но будучи лентяем (gii, не я), его надо пнуть. А именно: мне нужная модель, и первый блок загадочно манит меня словами «Model Generate». Пойдем сверху. Попробуем сгенерировать модель для финансовых досок.
На сколько я понимаю, что-то сильно поломать из формы генератора очень непросто, а уж если используется система контроля версий, тем более. В общем, я заполнил свою форму вот так
Из примечательного: если не поставить галку «Use Table Prefix», то имя таблицы будет указано явно, и при смене префикса таблиц придется ручками исправлять имена, лучше поставить эту галочку, тогда обращение к таблицам будет идти через синтаксис префиксов, например, в данном случае. Кстати, модель рекомендуется называть в единственном числе. Я затупил и оставил автоматически созданное имя FinBoards, потом создал еще одну модель FinBoard. Как я понял, какого-то специального механизма удаления моделей нет, просто удаляем файл и все.
На выходе получил вот такой файл
<?php namespace app\models; use Yii; /** * This is the model class for table "{{%fin_boards}}". * * @property integer $id * @property string $name * @property string $dsc */ class FinBoard extends \yii\db\ActiveRecord { /** * @inheritdoc */ public static function tableName() { return '{{%fin_boards}}'; } /** * @inheritdoc */ public function rules() { return [ [['name'], 'required'], [['dsc'], 'string'], [['name'], 'string', 'max' => 255], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'name' => 'Name', 'dsc' => 'Dsc', ]; } }
Как я говорил во вступлении, yii уже знает о существовании нашей базы и наличии в ней таблиц, это хорошо видно при создании модели, если начать вводить название таблицы, то генератор любезно отобразит все существующие таблицы.
По аналогии надо создать модели для таблиц transaction_list и transaction_cats, остальные таблицы вспомогательные и сами по себе не являются объектами представления модели данных — хорошо сказал, ничего не понятно, но звучит умно. В общем, не надо для таблиц связи делать отдельные модели.
Пока создавал модели в голову пришла отличная мысль, что можно переименовать название таблицы транзакций, избавившись от слова _list, если следуешь по следам моего «обучения», то учти этот момент, простите меня, не смог удержаться, я постараюсь больше так не делать.
Построение конроллеров и представлений
В заголовке я немного слукавил, обозначив только модели и контроллеры, еще сразу сделаю представления, но я не хотел их создавать на этом шаге, честно-честно. Не знал, что yii позволяет одним действием генерировать и контроллеры и представления, но это не может не радовать, особенно в случае, когда вообще ничего не понятно, а тут будет монументальная точка отсчета.
Как обозначил выше, мануалы говорят, что этап генерации контроллеров и представлений можно выполнить одним махом, используя CRUD Generator. Попробую воспользоваться этим ходом и посмотрю что из этого выйдет.
В поля нужно писать имена файлов с учетом пространства имен. Таким образом автоматически создается структура папок и файлов, на сколько я понимаю, можно создавать свою структуру, пока в этом нет смысла. Именование классов это исключительно соглашение, а не требование. Но лучше следовать соглашениям, чтобы кровавые слезы не шли у последователей.
Как видно gii нам сразу нагенерирует целых 8 файлов. Зачем столько? За что они все будут отвечать??? Но главное ввязаться, а там будет видно
Жамкаем кнопку Generate и коммитим новые файлы. Не буду тут приводить всё что получилось, посмотришь сам, дам только код контроллера
<?php namespace app\controllers; use Yii; use app\models\FinBoard; use app\models\FinBoardSearch; use yii\web\Controller; use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; /** * FinBoardController implements the CRUD actions for FinBoard model. */ class FinBoardController extends Controller { /** * @inheritdoc */ public function behaviors() { return [ 'verbs' => [ 'class' => VerbFilter::className(), 'actions' => [ 'delete' => ['POST'], ], ], ]; } /** * Lists all FinBoard models. * @return mixed */ public function actionIndex() { $searchModel = new FinBoardSearch(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); return $this->render('index', [ 'searchModel' => $searchModel, 'dataProvider' => $dataProvider, ]); } /** * Displays a single FinBoard model. * * @param integer $id * * @return mixed */ public function actionView($id) { return $this->render('view', [ 'model' => $this->findModel($id), ]); } /** * Creates a new FinBoard model. * If creation is successful, the browser will be redirected to the 'view' page. * @return mixed */ public function actionCreate() { $model = new FinBoard(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('create', [ 'model' => $model, ]); } } /** * Updates an existing FinBoard model. * If update is successful, the browser will be redirected to the 'view' page. * * @param integer $id * * @return mixed */ public function actionUpdate($id) { $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } else { return $this->render('update', [ 'model' => $model, ]); } } /** * Deletes an existing FinBoard model. * If deletion is successful, the browser will be redirected to the 'index' page. * * @param integer $id * * @return mixed */ public function actionDelete($id) { $this->findModel($id)->delete(); return $this->redirect(['index']); } /** * Finds the FinBoard model based on its primary key value. * If the model is not found, a 404 HTTP exception will be thrown. * * @param integer $id * * @return FinBoard the loaded model * @throws NotFoundHttpException if the model cannot be found */ protected function findModel($id) { if (($model = FinBoard::findOne($id)) !== null) { return $model; } else { throw new NotFoundHttpException('The requested page does not exist.'); } } }
Из странностей хочу отметить пару моментов:
- С какого-то рожна в папке с моделями я получил еще по одной модели Search. А чего так сложно? В одной модели не поместилось? Или может в контроллер, не? Помимо самого метода поиска в этой модели еще есть метод rules и scenarios.
- Я тут где-то говорил про толстые модели и тонкие контроллеры, и был искренне уверен, что Yii2 следует этой парадигме «из коробки». А теперь смотри количества кода в контроллере и в модели. Очень хочется сказать: «Эээ… Чё?!» — что значит, что я в недоумении.
Сейчас нет желания разбираться, но с этим определенно нужно будет разобраться.
Круто! Мы с тобой все создали, а как посмотреть, что мы получили? Все просто. В рамках нашей задачи нужно открыть такие ссылочки
http://finansomer.my/fin-board/
http://finansomer.my/transaction/
http://finansomer.my/transaction-cats/
Уиии! Оно работает!
Если внимательно посмотреть на эту форму, то можно заметить волшебную фразу «No results found», что наталкивает на мысль, что это форма поиска. Но вверху есть кнопка создания. Форма будет содержать все поля кроме автоинкрементного.
А вот так, например будет выглядеть форма добавления транзакции.
Ясно, что поля дат должны будут обрабатываться автоматически, но об этом уже буду говорить в следующих частях, когда начну описывать свою логику в приложении.
После создания записей их можно отредактировать, просмотреть или удалить — иконки в последнем поле как бы об этом намекают =)
Итог
На выходе мы имеем: модель, которая умеет работать с записями, модель которая умеет искать записи (хз почему так, но так), контроллеры и представления для полноценной работы с записями. Таким образом, на этом шаге очень хорошо (я думаю, что очень хорошо) продемонстрированы возможности генератора кода. Если делать что-то типа блога, где нужен только заголовок и текст, то стандартный генератор покрывает базовые потребности, конечно, без особых красивостей и тонкостей, но уже кое-что, с этим можно работать.
В следующей части, я надеюсь, наконец-то приступлю к самому интересному — программированию!
Всем рок!