python 2018-09-29 10:24:16 2737次浏览 0条评论 0 0 0

当前控制器方法仅支持GET请求参数的自动注入。

Yii自身有服务定位及容器的支持,于是便想着在控制器方法中实现对象的自动注入,以达到这样的使用效果:

<?php
namespace app\controllers;

use app\models\User;
use app\components\Request;
use app\components\Controller;

class TestController extends Controller
{
    public function actionIndex(User $user, Request $req)
    {
        echo $user->username;
        echo $req === \Yii::$app->request ? 'ok' : 'some errors';
    }
}

实现思路:

GET请求参数的自动注入,是通过\yii\web\Controller::bindActionParams在实现,于是通过自定义的基础控制器,覆写该方法以实现。

<?php
namespace app\components;

use Yii;
use yii\db\ActiveRecord;
use yii\base\InlineAction;
use yii\web\NotFoundHttpException;
use yii\web\BadRequestHttpException;

/**
 * Global base Controller
 */
abstract class Controller extends \yii\web\Controller
{
    /**
     * ActiveRecord 查询主键名
     * @var string
     */
    protected $arDiPrimaryKey = 'id';

    /**
     * @inheritDoc 覆写以实现方法依赖注入
     * Binds the parameters to the action.
     * This method is invoked by [[\yii\base\Action]] when it begins to run with the given parameters.
     * This method will check the parameter names that the action requires and return
     * the provided parameters according to the requirement. If there is any missing parameter,
     * an exception will be thrown.
     * @param \yii\base\Action $action the action to be bound with parameters
     * @param array $params the parameters to be bound to the action
     * @return array the valid parameters that the action can run with.
     * @throws BadRequestHttpException if there are missing or invalid parameters.
     */
    public function bindActionParams($action, $params)
    {
        if ($action instanceof InlineAction) {
            $method = new \ReflectionMethod($this, $action->actionMethod);
        } else {
            $method = new \ReflectionMethod($action, 'run');
        }

        $args = [];
        $missing = [];
        $actionParams = [];
        $classHint = null;
        foreach ($method->getParameters() as $param) {
            $name = $param->getName();
            if (array_key_exists($name, $params)) {
                if ($param->isArray()) {
                    $args[] = $actionParams[$name] = (array) $params[$name];
                } elseif (!is_array($params[$name])) {
                    $args[] = $actionParams[$name] = $params[$name];
                } else {
                    throw new BadRequestHttpException(Yii::t('yii', 'Invalid data received for parameter "{param}".', [
                        'param' => $name,
                    ]));
                }
                unset($params[$name]);
            } elseif ($param->isDefaultValueAvailable()) {
                $args[] = $actionParams[$name] = $param->getDefaultValue();
            } elseif ($classHint = $param->getClass()) {
                if ($classHint->isSubclassOf(ActiveRecord::class)) {
                    $args[] = $actionParams[$name] = 
                        $this->findModel($classHint->name, isset($params[$this->arDiPrimaryKey]) ? $params[$this->arDiPrimaryKey] : null);
                } else {
                    foreach (Yii::$app->getComponents() as $compId => $component) {
                        if (isset($component['class']) && $component['class'] === $classHint->name) {
                            $actionParams[$name] = Yii::$app->get($compId);
                            break;
                        }
                    }
                    if (! isset($actionParams[$name])) {
                        $actionParams[$name] = Yii::createObject($classHint->name);
                    }
                    $args[] = $actionParams[$name];
                }
            } else {
                $missing[] = $name;
            }
        }

        if (!empty($missing)) {
            throw new BadRequestHttpException(Yii::t('yii', 'Missing required parameters: {params}', [
                'params' => implode(', ', $missing),
            ]));
        }

        $this->actionParams = $actionParams;

        return $args;
    }

    /**
     * Finds the ActiveRecord model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer|string|null $pkValue
     * @return ActiveRecord the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($className, $pkValue)
    {
        if (null === $pkValue) {
            return new $className();
        }
        if (null !== ($model = $className::findOne($pkValue))) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}

关键点在60行至76行。
代码很清楚,不过多解释了。

    没有找到数据。
您需要登录后才可以评论。登录 | 立即注册