第二层内功 - 模块

  • 作者:KK

  • 发表日期:2017.8.28


大家都知道一个软件大了可以分模块,但具体怎么分,种语言的各个框架都有自己的一套办法,Yii2在这方面也是有解决方案的。

要点速读

  1. 通常来说,模块的代码都在一个目录里(也可以改开去,一般不改)

  2. 通常在应用目录下建立一个modules目录,里面放模块目录,比如modules/shop这样来放一个商店模块

  3. 模块和APP一样可以有控制器、视图和模型和组件等等,本质上APP也是一个模块,我们可以称APP为顶级模块,其它都是里面的子模块

  4. 模块可以无限组嵌套,但不提倡超过2级嵌套


快速体验

  1. 在应用目录下建一个modules目录,里面再建一个shop目录,形成modules/shop,后面我们称这个是shop模块目录

  2. 在shop模块目录下建一个Module.php,里面的代码如下:

    namespace app\modules\shop;
    	
    class Module extends \yii\base\Module{}
    

    就这样可以了,里面不用写代码

  3. 在shop模块下再建controllersviews目录

  4. controllers里建一个DefaultController控制器,代码:

    namespace app\modules\shop\controllers;
    	
    class DefaultController extends \yii\web\Controller{
    	public function actionIndex(){
    		return 'shop index';
    	}
    		
    	public function actionIndex2(){
    		return $this->renderPartial('index2');
    	}
    }
    
  5. views里建一个default目录,这是Default控制器对应的视图目录,里面再建index2.php,内容就写“index2 in tpl”这样吧


向配置文件添加自定义模块信息

这样一个基本的模块就建立起来了,可是还不能运行,要在配置文件中为app添加modules这个属性的配置(如果有了就基于原来的值追加内容),它值是一个数组,每个元素就是一个模块。如果我没记错,它原来的值默认应该是这样的:

'modules' => [
	'gii' => 'yii\gii\Module',
	'debug' => 'yii\debug\Module',
]

这表示有2个模块,分别是gii模块和debug模块,分别对应两个yii下的Module类(这个类咱们上面也有创建),好了咱们也追加一个自定义的模块:

'modules' => [
	'gii' => 'yii\gii\Module',
	'debug' => 'yii\debug\Module',
	'shop' => 'app\modules\shop\Module',  //指向上面创建的Module类
]

访问模块

假设使用默认的URL路由模式(就是那个?r=controller/action模式),那么访问shop模块可以这样:

  • ?r=shop/default/index2 会显示“index2 in tpl”,很容易角读,就是找shop模块的default控制器的index2这个action

  • ?r=shop/default 会显示“shop index”,定位到shop的default控制器了嘛,没指定action就默认找index

  • ?r=shop 也会显示“shop index”,因为默认找Default控制器


模块的结构

模块目录里除了多了个Module类以外,其它跟顶层应用目录很像,至少会有controllers目录,如果控制器有render的话还可以加views目录,如果模块自己要添加模型还可以加个models目录

简单地说模块就是个小APP,而你观察网站入口文件new出来的yii\web\Application,它的继承链前面就是Module一个,所以说顶层App本质上就是一个模块


模块可以嵌套

上面在应用项目下添加shop的过程等于在APP模块下再加了个shop模块,而你甚至还可以尝试再在shop模块下再建一个modules目录再嵌套一个子模块

yii支持无限级模块嵌套,只是通常情况下一般不提倡超过2层嵌套,不是说不能正常稳定地运作,而是这样软件复杂度变大了,应该分割成不同的项目,将一些计算职责独立分给另一个项目

个人看法嘛,我用Yii2三年了其实也只嵌套过1层模块,没嵌过2层的,在1层的时候已经感知到应用开始变得不太好管制了,像个野孩子,越长大越难管。

所以如果嵌套第2层模块的时候来临,那意味着我要思考项目是否应该将某些内容独立成另一个项目了(变成一个服务),这样能更好地解耦软件架构,形成面向服务的组织形态。


获取当前模块的目录

比如在模块的Default控制器里要输出本模块的目录可以这样:

echo Yii::getAlias('@app') . '/modules/shop';
//或者
echo Yii::getAlias('@app/modules/shop');
//或者
echo Yii::getAlias('@app/modules/' . $this->module->id)

模块配置

如果模块要有自己的配置,最笨的人都能想到在配置文件(比如那个web.php或main.php)里的params加东西,然后模块根据params的值来进行不同的工作就行了

实际上正确的姿势是这样的:

  1. 在模块的Module类里声明一个public属性,比如public $interfaceUrl = '';

  2. 在模块目录下加一个config.php用来写模块配置,内容如下:

    return [
    	'interfaceUrl' => 'http://xxx.com/interface'
    ];
    
  3. Module类添加如下方法:

    public function init(){
    	parent::init();
    		
    	$config = include(__DIR__ . '/config.php'));
    	Yii::configure($this, $config);
    }
    

    记得我在第一层内功 - Object 基类的特性里讲过的东西嘛?所有继承yii\base\Object的类都提供了一个空的init方法给你重写,利用它来加载配置,通过属性注入到自身就行了

  4. 测试,在控制器里执行:

    $module = Yii::$app->getModule('shop');	// getModule你可以理解为去配置文件的`modules`下找key为shop的模块,然后value就是类名嘛,new出来就行了
    //也可以通过如下代码取得module实例
    //$module = \app\modules\shop\Module::getInstance();
    	
    echo $module->interfaceUrl;		// http://xxx.com/interface
    

模块也能加组件

模块就像一个小APP,顶层APP可以加组件,这里也可以,在配置文件中像APP那样增加components吧,里面的内容怎么写就不啰嗦了,一下就懂

而且Module类里不需要声明components属性,因为模块类自带这个属性,所以我那句废话又来了:APP本身就是一个继承了模块的子类啊


这里只是简单基本地介绍了模块,和官方的权威指南讲的内容差不多,要深入了解模块,你可以自己看源代码的执行流程,未来考虑写深入的模块知识