本文链接:https://www.cnblogs.com/alanabc/p/10246023.html
讲解思路
把关系提到前面来讲,希望能够大致说清楚吧:
- 版本化需要路由支持,这样才能从URL中解析并定位到方法;
- 定位方法需要先定位到控制器;
- 控制器有两种:
- 一种是默认的,通过
controllerNamespace
进行指定 - 另一种是组件里的,通过组件的“加载”进行指定
- 一种是默认的,通过
Yii的版本化,说白了就是通过路由,把URL定位到对应的方法去。
我们这里用Yii的基础版(basic)作为讲解模板吧。
路由机制
路由机制的工作过程,简单来说就是:
- 当客户端(比如浏览器)发起请求时,先通过URL定位文件,然后把请求送达这个文件进行处理和响应。
若无法通过URL直接定位到文件,则通过
index.php
来接收请求(这里可能需要配置一下你的web服务器)。index.php
接收到请求后,先送达路由管理器,通过路由解析出URL对应的方法,然后送达这个方法,由方法受理请求并作出响应。当无法定位到文件也无法解析出方法时,一般来说做404错误处理,表示资源不存在。
如何定位
在框架中,除了静态资源是直接定位文件,其他的请求都是路由到某个方法进行处理。Yii是一个面向对象的框架,路由表通过命名空间来一步一步的解析class、action,确定处理请求的方法。比如,在基础版中,请求URL<域名>/index.php?r=site/index
,定位到SiteController
的actionIndex
方法。
那么路由表是怎么知道,URL里的site
指的就是SiteController
呢?继续看:
控制器命名空间 - controllerNamespace
Yii的配置文件里,有一项很重要的配置是controllerNamespace
,顾名思义就是控制器的命名空间。在基础版里,它大概是这么配置的:'controllerNamespace' => 'app\controllers'
,根据这个命名空间,路由就能找到控制器的目录,然后就能定位到控制器和方法了。当然,你看不到它,因为这是核心框架的默认配置。如果你想要验证我的说法,那么:
vendor\yiisoft\yii2\base\Application.php
line 95
/**
* @var string the namespace that controller classes are located in.
* This namespace will be used to load controller classes by prepending it to the controller class name.
* The default namespace is `app\controllers`.
*
* Please refer to the [guide about class autoloading](guide:concept-autoloading.md) for more details.
*/
public $controllerNamespace = 'app\\controllers';
当你请求URL<域名>?r=site/index
时,路由会去寻找app\controllers\SiteController
这个类(其实就是去应用根目录下的controllers
里寻找SiteController.php
文件),然后找到里面的actionIndex
方法。
凭什么site
就对应SiteController
类,index
就对应actionIndex
方法?它也是默认的配置!不过那就是另一件话题了。
模块 - modules
你使用过Yii自带的debug组件吗?如果没有,那你应该知道,当启用了这个组件,你就有两种方式可以使用它:一是在页面的右下角,会有一个Yii的小logo,展开它就是debug组件;二是通过<域名>/index.php?r=debug/default/index
,直接打开debug页面。
yii-debug组件必然是有处理请求的方法的,可是为啥上面这个URL可以准确定位到方法呢,controllerNamespace
里没有配置过它呀?
其实yii-debug是一个组件(module),每一个组件都由控制器、模型、视图等构成,是一个独立的小应用。对于这样的组件,我们要在$config['modules']
里配置,然后就能挂载它,你就可以正常使用了。也正是这个“挂载”的过程,框架寻找到了对应的命名空间,就可以准确的找到控制器和方法了。
我们来找找这个组件吧:vendor\yiisoft\yii2-debug
,会找到文件Module.php
:
<?php
namespace yii\debug;
//use ...
class Module extends \yii\base\Module implements BootstrapInterface
{
//...
}
它继承了\yii\base\Module
类,这是一个模块类,你只需要知道,继承模块类的文件在对应的目录时,会被自动加载。
模块和版本化
组件的“挂载”大致就是这样。可是这和版本化有什么关系?关系可大了,我们的版本化,就是依靠组件实现的。
先来梳理一下:
- 版本化需要路由支持,这样才能从URL中解析并定位到方法;
- 定位方法需要先定位到控制器;
- 控制器有两种:
- 一种是默认的,通过
controllerNamespace
进行指定 - 另一种是组件里的,通过组件的“加载”进行指定
- 一种是默认的,通过
动手做
我们在上面看到,yii-debug组件是在配置项的$config['modules']
里多加了一个debug
组件,并且指明了'class' => 'yii\debug\Module'
。
仍然是拿Yii基础版来,做个简单的“版本化”:我现在打算增加一个version1
应用,算是加了一个“版本1”吧。
编写配置
首先在配置文件web.php
里:
$config = [
//略...
'modules' => [
//这里class指定的是模块类,看下文
'version1' => ['class' => 'app\version1\Version1'],
],
];
编写模块
上面配置里的class
可不是乱写的哦!是有依据的。
在应用根目录下新建文件夹version1
(vendor
同一级,兄弟文件夹),在文件夹内新建文件Version1.php
作为一个模块类,继承\yii\base\Module
。在这个类里面还指定了$controllerNamespace
,注意跟上文提到的不一样:上文指定的是整个应用的,这里指定的是组件自己的:
<?php
namespace app\version1;
class Version1 extends \yii\base\Module
{
//这里指定的是组件自己的控制器命名空间
public $controllerNamespace = 'app\version1\controllers';
public function init()
{
parent::init();
}
}
接下来在version1
文件夹里继续新建三个子文件夹:
controllers
:控制器文件夹。里面的控制器文件,命名空间要写成namespace app\version1\controllers;
models
:模型文件夹。里面的模型文件,命名空间要写成namespace app\version1\models;
views
:视图文件夹
你只要像正常的那样在这三个文件夹里写你的代码就可以了。
编写路由
最后,去配置一下路由:
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
'version1/<controller:[\w-]+>/<action:[\w-]+>'
=> 'version1/<controller>/<action>', //version1路由
'<controller:[\w-]+>/<action:[\w-]+>'
=> '<controller>/<action>', //原有应用的路由(比如原本URL:<域名>/index.php?r=site/index,现在URL:<域名>/site/index)
//其他略...
],
],
这样,访问URL<域名>/version1/site/index
的时候,就路由到version1
的SiteController
的actionIndex
方法进行处理。
结语
语言组织的不够好见谅!
API的版本化和上面的也没什么太大的区别,你可以试着把API做个版本化;还可以试着把Yii基础版配置成Yii高级版的样子,这样可以加深理解。