安装项目

我这里选用的是 yii advanced 版本,如果你没有运行环境的话可以参考官方的文档,使用 Vagrant 安装运行环境。

因为我本地已经有了 PHP 运行环境,所以直接使用 Composer 创建项目:

composer create-project --prefer-dist yiisoft/yii2-app-advanced service

进入项目目录,初始化项目:

$ cd service
$ php init
Yii Application Initialization Tool v1.0

Which environment do you want the application to be initialized in?

  [0] Development
  [1] Production

  Your choice [0-1, or "q" to quit] 0

  Initialize the application under 'Development' environment? [yes|no] yes

  Start initialization ...

   generate frontend/config/test-local.php
   generate frontend/config/params-local.php
   generate frontend/config/main-local.php
   generate frontend/config/codeception-local.php
   generate frontend/web/index.php
   generate frontend/web/robots.txt
   generate frontend/web/index-test.php
   generate yii
   generate backend/config/test-local.php
   generate backend/config/params-local.php
   generate backend/config/main-local.php
   generate backend/config/codeception-local.php
   generate backend/web/index.php
   generate backend/web/robots.txt
   generate backend/web/index-test.php
   generate common/config/test-local.php
   generate common/config/params-local.php
   generate common/config/main-local.php
   generate common/config/codeception-local.php
   generate yii_test.bat
   generate yii_test
   generate console/config/test-local.php
   generate console/config/params-local.php
   generate console/config/main-local.php
   generate cookie validation key in backend/config/main-local.php
   generate cookie validation key in common/config/codeception-local.php
   generate cookie validation key in frontend/config/main-local.php
      chmod 0777 backend/runtime
      chmod 0777 backend/web/assets
      chmod 0777 console/runtime
      chmod 0777 frontend/runtime
      chmod 0777 frontend/web/assets
      chmod 0755 yii
      chmod 0755 yii_test

  ... initialization completed.

安装完成后目录结构如下:

$ tree -L 1
.
├── LICENSE.md
├── README.md
├── Vagrantfile
├── backend
├── codeception.yml
├── common
├── composer.json
├── composer.lock
├── console
├── docker-compose.yml
├── environments
├── frontend
├── init
├── init.bat
├── requirements.php
├── vagrant
├── vendor
├── yii
├── yii.bat
├── yii_test
└── yii_test.bat

7 directories, 14 files

接下来我们将对 backend app 下的路由功能进行改造。

Module

生成

使用 gii 来为项目生成 Module,可以看到下面我生成了一个 auth 的 Module:

$ php yii gii/module --moduleClass='backend\modules\auth\Module' --moduleID=auth
Running 'Module Generator'...

The following files will be generated:
        [new] /Users/George/Develop/PHP/service/backend/modules/auth/Module.php
        [new] /Users/George/Develop/PHP/service/backend/modules/auth/controllers/DefaultController.php
        [new] /Users/George/Develop/PHP/service/backend/modules/auth/views/default/index.php

Ready to generate the selected files? (yes|no) [yes]:yes

Files were generated successfully!
Generating code using template "/Users/George/Develop/PHP/service/vendor/yiisoft/yii2-gii/src/generators/module/default"...
 generated /Users/George/Develop/PHP/service/backend/modules/auth/Module.php
 generated /Users/George/Develop/PHP/service/backend/modules/auth/controllers/DefaultController.php
 generated /Users/George/Develop/PHP/service/backend/modules/auth/views/default/index.php
done!

注册

编辑 backend\config\main.php 文件,在 modules 中添加 auth Module:

'modules' => [
    'auth' => ['class' => 'backend\modules\auth\Module'],
],

并修改 urlManager 属性如下:

'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'enableStrictParsing' => false,
    'rules' => [
        '<modules>/<controller>' => '<modules>/<controller>/enter'
    ],
],

主要是 rules 部分,将所有的请求路由到对应 Module 的相应 Controller 中的 enter 方法,这么做的目的是可以对所有请求统一进行处理。

处理统一逻辑的控制器

创建控制器

创建 common\controllers\RestController.php 控制器,内容如下:

<?php

namespace common\controllers;

use Yii;
use yii\base\Action;
use ReflectionException;
use yii\rest\Controller;
use yii\base\InlineAction;
use yii\base\InvalidRouteException;
use yii\base\InvalidConfigException;

/**
 * Date: 2019/1/1
 * @author George <george@betterde.com>
 * @package common\controllers
 */
class BaseController extends Controller
{
    /**
     * 统一处理请求
     * 
     * Date: 2019/1/1
     * @return mixed
     * @throws InvalidRouteException
     * @author George
     */
    public function main()
    {
        $request = Yii::$app->request;
        $method = strtolower($request->getMethod());
        $params = array_merge($request->get(), $request->post());
        // 你需要统一处理的业务逻辑
        $result = $this->runAction($method, ['params' => $params]);
        return $result;
    }

    /**
     * Date: 2019/1/1
     * @param array $params
     * @return array
     * @author George <george@betterde.com>
     */
    public function post(array $params = []): array
    {
        return [];
    }

    /**
     * Date: 2019/1/1
     * @param array $params
     * @return array
     * @author George <george@betterde.com>
     */
    public function put(array $params = []): array
    {
        return [];
    }

    /**
     * Date: 2019/1/1
     * @param array $params
     * @return array
     * @author George <george@betterde.com>
     */
    public function get(array $params = []): array
    {
        return [];
    }

    /**
     * 使用 index 方法名替代 actionIndex 这种方式
     *
     * Date: 2019/1/1
     * @param string $id
     * @return object|Action|InlineAction|null
     * @throws ReflectionException
     * @throws InvalidConfigException
     * @author George <george@betterde.com>
     */
    public function createAction($id)
    {
        if ($id === '') {
            $id = $this->defaultAction;
        }

        $actionMap = $this->actions();
        if (isset($actionMap[$id])) {
            return Yii::createObject($actionMap[$id], [$id, $this]);
        }

        if (preg_match('/^(?:[a-z0-9_]+-)*[a-z0-9_]+$/', $id)) {
            $methodName = $id;
            if (method_exists($this, $methodName)) {
                $method = new \ReflectionMethod($this, $methodName);
                if ($method->isPublic() && $method->getName() === $methodName) {
                    return new InlineAction($id, $this, $methodName);
                }
            }
        }

        return null;
    }
}

继承控制器

让项目中的 Module Controller 都继承自上面创建的 common\controllers\RestController

<?php

namespace backend\modules\auth\controllers;

use common\controllers\RestController;

/**
 * User SingIn Controller logic
 * 
 * Date: 2019/1/1
 * @author George <george@betterde.com>
 * @package backend\modules\auth\controllers
 */
class SigninController extends RestController
{
    public function post(array $params = []): array
    {
        return $params;
    }
}

到这里基本上就完成了,此时请求 POST /auth/signin,将会自动路由到 SigninControllerpost 方法。

I hope this is helpful, Happy hacking…