CRUD для Yii2 за пару минут

22 октября, 2014
Метки:

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

Метки:

Оставить комментарий

1 комментарий »

    osi322

    Глава 11 там как раз про это написано, как сделать общий контроллер для CRUD, и там проще и лучше написано
    Web Application
    Development
    with Yii 2 and РНР
    Магk Safгonov, Jeffrey Winesett


Оставить комментарий:









Копирование материалов разрешено при наличии активной ссылки на источник