Программирование, веб-кодинг

Замещение контроллеров, моделей, видов из сторонних расширений на собственные

Иногда необходимо добавить какую-то свою функнкциональность в известное и удобное расширение. Но в целом собственное расширение создавать необходимости нет, чтобы не изобретать велосипед. В таких случаях делают замещение (overriding) контроллера, или модели или вида, или всего вместе на свои. Как это сделать?

Вы пишете свой кусок в отдельном классе соответствующей сущности, но наследуете этот свой класс от стандартного, например, в расширении, установленном композером в папку vendor (как известно в этой папке ничего менять нельзя, это прерогатива composer). В своём классе создаёте функции нужные вам, новые, а старые не копируете повторно из расширения, они подключатся по настроенному наследству.

Рассмотрю на примере расширения bupy7/yii2-pages


Замещение контроллера в Yii2

1. Начинаем с контроллера. Создаю свой контроллер в папке controllers, наследую его от bupy7\pages\controllers\ManagerController as BaseManagerController. Я только в нём указываю на свою модель. Поэтому вообще говоря, в таком случае нужно замещать только модель. Но для примера, пусть будет. Может понадобится какую-то логику тут ещё свою внедрить.

<?php

namespace app\controllers;

use Yii;
use app\models\Mypage;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use bupy7\pages\Module;
use bupy7\pages\controllers\ManagerController as BaseManagerController;

/**
 * ManagerController implements the CRUD actions for Page model.
 * @author Belosludcev Vasilij <bupy765@gmail.com>
 * @since 1.0.0
 */
class ManagerController extends BaseManagerController
{


    /**
     * Creates a new Page model.
     * If creation is successful, the browser will be redirected to the 'update' page.
     * @return mixed
     */
    public function actionCreate()
    {
        return $this->actionUpdate(null);
    }

    /**
     * Updates an existing Page model.
     * If update is successful, the browser will be redirected to the 'update' page.
     * @param integer|null $id
     * @return mixed
     */
    public function actionUpdate($id = null)
    {
        if ($id === null) {
            $model = new Mypage;
        } else {
            $model = $this->findModel($id);
        }

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            Yii::$app->session->setFlash('success', Module::t('SAVE_SUCCESS'));
            return $this->redirect(['update', 'id' => $model->id]);
        } 
        
        $module = Yii::$app->getModule('pages');
        
        return $this->render($id === null ? 'create' : 'update', [
            'model' => $model,
            'module' => $module,
        ]);
    }

    
    /**
     * Finds the Page model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return Page the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = Mypage::findOne($id)) !== null) {
            return $model;
        }
        throw new NotFoundHttpException(Module::t('PAGE_NOT_FOUND'));
    }
    
}

2. Теперь пропишем в конфиг свой контроллер новый в controllerMap в массиве модулей

<?php
return[
...
    'modules'=>[
        ...
        'pages' => [
            'class' => 'bupy7\pages\Module',
            'controllerMap' => [
                'manager' => [
                    'class' => 'app\controllers\ManagerController',
                    'as access' => [
                        'class' => yii\filters\AccessControl::className(),
                        'ruleConfig' => [
                            'class' => yii\filters\AccessRule::className(),
                        ],
                        'rules' => [
                            [
                                'allow' => true,
                                'roles' => ['admin'],
                            ],
                        ],
                    ],
                ],
            ],
            ...
        ],
        ...
    ],

Ещё почитать про замещение контроллеров - http://yii2-user.readthedocs.org/en/latest/basics/...


Замещение модели в Yii2

1.Создаю в @app/models свой класс модели Mypage.php. Сначала я скопировал всю модель из расширения и в ней менял что мне надо. Но так делать не надо. Надо поступить аналогично как поступили с контроллером. Привожу пример для расширения dektrium/yii2-users

namespace app\models;

use dektrium\user\models\User as BaseUser;

class User extends BaseUser
{
public function register()
    { 
// do your magic
    }
}

2. Не забываем в конфиг добавить в нашем модуле в modelMap (аналогично в контроллере было в controllerMap)

...
'user' => [
    'class' =>'dektrium\user\Module',
    'modelMap' => [
        'User'=>'app\models\User',
    ],
],
...

Подробнее посмотрите в доке yii2-users - https://github.com/dektrium/yii2-user/blob/master/...


Замещение вида в Yii2

Это делается манипуляцией с темой (theme), так как названия видов невозможно менять на своё. Просто укажем путь к нашей папке view или к отдельной папке с темой вашей. А в ней уже будут файлы видов с названиями одноимёнными, как и в стандартном расширении, которое мы хотим усовершенствовать. В конфиге в массиве components пишем

<?php 
return[
    ...
    'components'=>[
        'view'=>[
            'theme'=>[
                'pathMap'=>[
                    '@dektrium/user/views'=>'@app/views/user'
                ],
            ],
        ],
    ],
...
];

или для yii-pages

...
'view' => [
                'theme' => [
                    'pathMap' => ['@bupy7/pages/views' => '@app/views'],
                ],
        ],
...

Теперь после этого yii будет каждый вид искать сначала по указанному нами пути в папке @app/views/user (@app/views), а для тех, чьи файлы видов не найдёт, уже пойдёт искать в папку vendor в расширение.

Ещё гляньте - http://yii2-user.readthedocs.org/en/latest/basics/...

Другим вариантом внести свою логику в чужое расширение, и расширить его по-своему может стать использование событий и обработчиков их.