В админке любого сайта есть множество элементов, которые можно добавлять/редактировать и удалять (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