Yii2学习笔记系列14——Application Structure-Modules(应用结构之模块)
三 11 五月 2016
模块
模块是独立的软件单元,由模型、视图、控制器和其他支持组件组成,终端用户可以访问在应用主体中已安装的模块的控制器,模块被当成小应用主体来看待,但是和应用主体不同的是,模块不能被单独部署,必须属于某个应用主体。
创建模块
模块被组织成一个称为yii\base\Module::basePath的目录,在该目录中有子目录如controllers
、models
、views
,分别对应控制器、模型、视图。如下是一个模型的目录结构:
forum/
Module.php 模块类文件
controllers/ 包含控制器类文件
DefaultController.php default 控制器类文件
models/ 包含模型类文件
views/ 包含控制器视图文件和布局文件
layouts/ 包含布局文件
default/ 包含DefaultController控制器视图文件
index.php index视图文件
模块类
每个模块都有一个继承yii\base\Module的模块类,该类文件直接放在模块的yii\base\Module::basePath目录下,并且可以被自动加载。当一个模块被访问时,该模块的唯一实例会被创建,与应用主体实例类似,模块实例用来帮助模块内代码共享数据和组件。
以下示例是一个模块类的示例:
namespace app\modules\forum;
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
$this->params['foo'] = 'bar';
// ... 其它初始化代码 ...
}
}
如果init()
方法中包含很多初始化模块属性代码,可以将它们保存在配置文件中,并在init()
方法中使用以下代码加载:
public function init()
{
parent::init();
// 从config.php加载配置来初始化模块
\Yii::configure($this, require(__DIR__ . '/config.php'));
}
config.php
配置文件可能包含以下内容,类似应用主体配置。
<?php
return [
'components' => [
// 组件配置列表
],
'params' => [
// 参数列表
],
];
模块中的控制器
创建模块的控制器时,惯例是将控制器类放在模块命名空间的controllers
子命名空间中,这也意味着要将控制器的类文件放在模块yii\base\Module::basePath目录中的controllers
子目录中,例如,上小节中要在forum
模块中创建post
控制器,应该像如下声明控制器类一样:
namespace app\modules\forum\controllers;
use yii\web\Controller;
class PostController extends Controller
{
// ...
}
我们可以通过配置yii\base\Module::$controllerNamespace属性来实现自定义控制器类的命名空间,如果一些控制器不在该命名空间下,可以通过配置yii\base\Module::controllerMap属性让它们能被访问,这类似于我们在应用主体配置中所做的。
模块中的视图
视图应该放在模块的yii\base\Module::basePath对应目录下的views
目录中,对于模块中控制器对应的视图文件应该放在views/ControllerID
目录下,其中ControllerID
对应控制器ID。例如,假设控制器类是PostController
,目录对应模块yii\base\Module::basePath目录下的
views/post```目录。
模块可以指定在模块控制器视图渲染中所使用的布局,该布局默认存放在view/layouts
目录下,我们可以通过配置yii\base\Module::layout属性来指定布局名,如果没有配置该属性,默认使用应用的布局名。
模块中的控制台命令
我们的模块中可能也会声明一些控制台模式下可用的命令。
为了使得命令行实用程序(command line utility)能够识别到我们的命令,在Yii以命令行模式运行时,我们需要修改yii\base\Module::controllerNamespace属性,将其指向我们定义的命令的命名空间。
如下代码就是其中的一个实现方法,在模块的初始化方法中测试Yii应用程序的实例类型:
public function init()
{
parent::init();
if (Yii::$app instanceof \yii\console\Application) {
$this->controllerNamespace = 'app\modules\forum\commands';
}
}
之后我们的命令可以通过如下的规则从命令行中访问到:
yii <module_id>/<command>/<sub_command>
使用模块
想要在应用中使用模块,只需要将模块加入到应用主体配置的yii\base\Application::modules属性对应的列表中,如下代码的应用主体配置使用forum
模块:
[
'modules' => [
'forum' => [
'class' => 'app\modules\forum\Module',
// ... 模块其他配置 ...
],
],
]
yii\base\Application::modules属性使用模块配置数组,每个数组的键为模块ID,它标识该应用中唯一的模块,数组的值为用来创建模块的配置。
路由
和访问应用的控制器类似,路由也被用在模块中对控制器进行寻址。模块中控制器的路由必须以模块ID开始,接下来是控制器ID和操作ID。例如,假设应用使用一个forum
模块,路由forum.post/index
代表模块中post
控制器的index
操作,如果路由只包含模块ID,默认为default
的yii\base\Module::defaultRoute属性来决定使用哪个控制器/操作,也就是说路由forum
可能代表forum
模块的default
控制器。
访问模块
在模块中,可能经常需要获取模块类的实例来访问模块ID、模块参数、模块组件等,我们可以使用如下的语句来获取module的实例:
$module = MyModuleClass::getInstance();
其中,MyModuleClass
对应你想要的模块类,getInstance()
方法返回当前请求的模块类实例,如果模块没有被请求,该方法会返回空。需要注意的是,这里不需要手动创建一个模块类的实例,因为手动创建的和Yii处理请求时自动创建的是不一样的。
补充说明:在开发模块时,我们不能假设模块是使用固定的ID的,因为在应用或者其他模块中,模块可能会对应到任意的ID,为了获取模块ID,应该使用上述代码获取模块实例,然后通过$module->id
获取模块ID。
也可以使用如下方式访问模块实例:
// 获取ID为 "forum" 的模块
$module = \Yii::$app->getModule('forum');
// 获取处理当前请求控制器所属的模块
$module = \Yii::$app->controller->module;
第一种方式仅在我们明确知道模块ID的情况下有效,第二种方式在我们知道处理请求的控制器的情况下使用。
一旦获取到模块实例,我们就可以访问注册到模块的参数和组件,例如:
$maxPostCount = $module->params['maxPostCount'];
引导启动模块
有些模块在每个请求下都会运行,比如yii\debug\Module模块,为此,我们需要将这种模块加入到主体的yii\base\Application::bootstrap属性中。
例如,如下实例的应用主体配置会确保debug
模块每次都被加载:
[
'bootstrap' => [
'debug',
],
'modules' => [
'debug' => 'yii\debug\Module',
],
]
嵌套模块
模块可以无限级嵌套,也就是说,模块可以包含另一个模块的模块,我们称前者为父模块,后者为子模块,子模块必须在父模块的yii\base\Module::modules属性中声明,例如:
namespace app\modules\forum;
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
$this->modules = [
'admin' => [
// 此处应考虑使用一个更短的命名空间
'class' => 'app\modules\forum\modules\admin\Module',
],
];
}
}
在嵌套模块中的控制器,它的路由应该包含它所有的上级模块(Ancestor Module)的ID,例如forum/admin/dashboard/index
代表在模块forum
中子模块的admin
中dashboard
控制器的index
操作。
最佳实践
模块在大型项目中常备使用,这些项目的特性可分组,每个组包含一些强相关的特性,每个特性组可以做成一个模块,由特定的开发人员和开发组来进行开发和维护。
在特性组上,使用模块也是重用代码的好方式,一些常用特性,如用户管理,评论管理,可以开发成模块,这样儿在相关项目中非常容易被重用。
Category: PHP Develop