В админке любого сайта есть множество элементов, которые можно добавлять/редактировать и удалять (CRUD — операции). В Yii, еще со времен первой версии фреимворка, есть генератор CRUD. Он генерирует контроллер и представления.
Мне не нравится то, что для каждой модели создается отдельный контроллер, в каждый из которых придется прописывать права доступа (AccessControl). Если у вас более 2-3х моделей, для которых нужно создать набор CRUD операций, то автоматическая генерация приведет к ужасному дублированию кода. Я хочу рассказать о том, как избавляюсь от этого дублирования, попутно упрощая создание новых CRUD-операций.
Все повторяющиеся куски кода, вынесла в базовый контроллер, от которого наследуются все остальные контроллеры:
public function createGridPage($model, $view, $searchModel='') { if($searchModel != '') { $searchModel = new $searchModel(); $dataProvider = $searchModel->search(Yii::$app->request->queryParams); return $this->render($view, [ 'dataProvider' => $dataProvider, 'searchModel' => $searchModel, ]); } else { $dataProvider = new ActiveDataProvider([ 'query' => $model::find(), ]); return $this->render($view, [ 'dataProvider' => $dataProvider, ]); } } public function actionWithItemInGrid($id, $modelName, $view, $redirectAfterSave, $dopInView='') { if(is_numeric($id)) $model = $this->findModel($id, $modelName); else $model = new $modelName(); if ($model->load(Yii::$app->request->post()) && $model->save()) { Yii::$app->getSession()->setFlash('success', 'Сохранено'); return $this->redirect($redirectAfterSave); } else { if(is_array($dopInView)) $viewParams = $dopInView; $viewParams['model'] = $model; return $this->render($view, $viewParams); } } public function deleteAndRedirect($id, $model, $redirectAfterDelete) { $this->findModel($id, $model)->delete(); return $this->redirect($redirectAfterDelete); }
Мне кажется не самой лучшей идеей выносить в базовый контроллер рендеринг вьюшек, но пока оставлю как есть.
public function actionPages() { return $this->createGridPage('common\models\Pages', 'pages', 'app\models\search\SearchPages'); } public function actionAddPage() { return $this->actionWithItemInGrid(null, 'common\models\Pages', 'createPage', ['pages']); } public function actionUpdatePage($id) { return $this->actionWithItemInGrid($id, 'common\models\Pages', 'updatePage', ['update-page', 'id' => $id]); } public function actionDeletePage($id) { return $this->deleteAndRedirect($id, 'common\models\Pages', ['pages']); }
Передаем модель, представление и ссылку для редиректа после сохранения данных.
Теперь можно сгенерировать view, переименовать их и положить в папку нашего контроллера.
По умолчанию, все ссылки на редактирование/удаление выглядят как update/delete и тп. Понятное дело, что в одном контроллере не может быть два метода update. Поэтому нужно поменять ссылки в GridView. Я зачастую не использую кнопку {view} за ненадобностью. И чтобы не дублировать код, создала свой класс для кнопок, от наследованный от ActionColumn
namespace backend\components; use yii\grid\ActionColumn; use Yii; use yii\helpers\Html; class ButtonUpdateDelete extends ActionColumn{ public $template = '{update} {delete}'; public $updateAction, $deleteAction; protected function initDefaultButtons() { if (!isset($this->buttons['update'])) { $this->buttons['update'] = function ($url, $model) { return Html::a('<span class="glyphicon glyphicon-pencil"></span>', [$this->updateAction, 'id' => $model->id], [ 'title' => Yii::t('yii', 'Update'), 'data-pjax' => '0', ]); }; } if (!isset($this->buttons['delete'])) { $this->buttons['delete'] = function ($url, $model) { return Html::a('<span class="glyphicon glyphicon-trash"></span>', [$this->deleteAction, 'id' => $model->id], [ 'title' => Yii::t('yii', 'Delete'), 'data-confirm' => Yii::t('yii', 'Are you sure you want to delete this item?'), 'data-method' => 'post', 'data-pjax' => '0', ]); }; } } }
По сути, я только переопределила метод initDefaultButtons, добавив использование переменных класса $updateAction и $deleteAction.
GridView::widget([ 'dataProvider' => $dataProvider, 'layout' => "{pager}\n{items}\n{pager}", 'filterModel' => $searchModel, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'title', ... ['class' => 'backend\components\ButtonUpdateDelete', 'updateAction' => 'update-page', 'deleteAction' => 'delete-page' ], ], ]);
Глава 11 там как раз про это написано, как сделать общий контроллер для CRUD, и там проще и лучше написано
Web Application
Development
with Yii 2 and РНР
Магk Safгonov, Jeffrey Winesett