2 关注者

模块

模块是自包含的软件单元,由模型视图控制器和其他支持组件组成。最终用户可以在模块安装在应用程序中时访问模块的控制器。出于这些原因,模块通常被视为迷你应用程序。模块与应用程序的不同之处在于模块不能单独部署,必须驻留在应用程序中。

创建模块

模块被组织为一个目录,称为模块的基本路径。在目录中,有一些子目录,例如 `controllers`、`models`、`views`,它们分别包含控制器、模型、视图和其他代码,就像在一个应用程序中一样。以下示例显示了模块中的内容

forum/
    Module.php                   the module class file
    controllers/                 containing controller class files
        DefaultController.php    the default controller class file
    models/                      containing model class files
    views/                       containing controller view and layout files
        layouts/                 containing layout view files
        default/                 containing view files for DefaultController
            index.php            the index view file

模块类

每个模块都应该有一个唯一的模块类,该类继承自yii\base\Module。该类应直接位于模块的基本路径下,并且应该是自动加载的。当访问模块时,将创建相应模块类的单个实例。类似于应用程序实例,模块实例用于共享模块内代码的数据和组件。

以下是模块类可能的样子示例

namespace app\modules\forum;

class Module extends \yii\base\Module
{
    public function init()
    {
        parent::init();

        $this->params['foo'] = 'bar';
        // ...  other initialization code ...
    }
}

如果 `init()` 方法包含许多用于初始化模块属性的代码,您也可以将它们保存在配置中,并使用以下代码在 `init()` 中加载它们

public function init()
{
    parent::init();
    // initialize the module with the configuration loaded from config.php
    \Yii::configure($this, require __DIR__ . '/config.php');
}

其中配置文件 `config.php` 可以包含以下内容,类似于应用程序配置中的内容。

<?php
return [
    'components' => [
        // list of component configurations
    ],
    'params' => [
        // list of parameters
    ],
];

模块中的控制器

在模块中创建控制器时,通常将控制器类放在模块类命名空间的 `controllers` 子命名空间下。这也意味着控制器类文件应该放在模块的基本路径内的 `controllers` 目录中。例如,要在上一节所示的 `forum` 模块中创建一个 `post` 控制器,您应该像下面这样声明控制器类

namespace app\modules\forum\controllers;

use yii\web\Controller;

class PostController extends Controller
{
    // ...
}

您可以通过配置yii\base\Module::$controllerNamespace 属性来自定义控制器类的命名空间。如果一些控制器不在此命名空间中,您可以通过配置yii\base\Module::$controllerMap 属性使它们可访问,类似于您在应用程序中所做的那样.

模块中的视图

模块中的视图应放置在模块 基本路径 内的 views 目录中。对于由模块中的控制器渲染的视图,它们应放置在目录 views/ControllerID 下,其中 ControllerID 指的是 控制器 ID。例如,如果控制器类为 PostController,则该目录将位于模块的 基本路径 内的 views/post 中。

模块可以指定一个 布局,该布局应用于由模块的控制器渲染的视图。默认情况下,布局应放在 views/layouts 目录中,您应该配置 yii\base\Module::$layout 属性以指向布局名称。如果您没有配置 layout 属性,则将使用应用程序的布局。

模块中的控制台命令

您的模块还可以声明命令,这些命令将在 控制台 模式下可用。

为了让命令行实用程序看到您的命令,您需要更改 yii\base\Module::$controllerNamespace 属性,当 Yii 在控制台模式下执行时,将其指向您的命令命名空间。

实现这一点的一种方法是在模块的 init() 方法中测试 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>

使用模块

要在应用程序中使用模块,只需通过在应用程序的 modules 属性中列出模块来配置应用程序。以下 应用程序配置 中的代码使用 forum 模块

[
    'modules' => [
        'forum' => [
            'class' => 'app\modules\forum\Module',
            // ... other configurations for the module ...
        ],
    ],
]

信息:要连接模块的控制台命令,您还需要将其包含在 控制台应用程序配置 中。

modules 属性接受一个模块配置数组。每个数组键代表一个模块 ID,该 ID 在应用程序中的所有模块中唯一标识该模块,相应的数组值为用于创建模块的 配置

路由

与访问应用程序中的控制器一样,路由 用于寻址模块中的控制器。模块内控制器的路由必须以模块 ID 开头,后跟 控制器 ID操作 ID。例如,如果应用程序使用一个名为 forum 的模块,则路由 forum/post/index 将代表该模块中 post 控制器的 index 操作。如果路由只包含模块 ID,则 yii\base\Module::$defaultRoute 属性(默认为 default)将决定使用哪个控制器/操作。这意味着路由 forum 将代表 forum 模块中的 default 控制器。

模块的 URL 管理器规则应在 yii\web\UrlManager::parseRequest() 被触发之前添加。这意味着在模块的 init() 中执行此操作将不起作用,因为模块将在路由已处理后初始化。因此,规则应在 引导阶段 添加。将模块的 URL 规则包装在 yii\web\GroupUrlRule 中也是一个好习惯。

如果模块用于 版本化 API,则其 URL 规则应直接在应用程序配置的 urlManager 部分中添加。

访问模块

在模块内,您可能经常需要获取 模块类 的实例,以便您可以访问模块 ID、模块参数、模块组件等。您可以使用以下语句执行此操作

$module = MyModuleClass::getInstance();

其中 MyModuleClass 指的是您感兴趣的模块类的名称。该 getInstance() 方法将返回当前请求的模块类实例。如果未请求模块,该方法将返回 null。请注意,您不希望手动创建模块类的实例,因为它将与 Yii 响应请求创建的实例不同。

信息:在开发模块时,您不应假设模块将使用固定的 ID。这是因为模块在应用程序或其他模块中使用时,可以与任意 ID 关联。为了获取模块 ID,您应该首先使用上述方法获取模块实例,然后通过 $module->id 获取 ID。

您还可以使用以下方法访问模块的实例

// get the child module whose ID is "forum"
$module = \Yii::$app->getModule('forum');

// get the module to which the currently requested controller belongs
$module = \Yii::$app->controller->module;

第一种方法仅在您知道模块 ID 时有用,而第二种方法最好用于您知道正在请求的控制器时。

获得模块实例后,您可以访问与模块注册的参数和组件。例如,

$maxPostCount = $module->params['maxPostCount'];

引导模块

某些模块可能需要针对每个请求运行。yii\debug\Module 模块就是一个例子。为此,请在应用程序的 bootstrap 属性中列出此类模块的 ID。

例如,以下应用程序配置确保始终加载 debug 模块

[
    'bootstrap' => [
        'debug',
    ],

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

嵌套模块

模块可以无限级地嵌套。也就是说,一个模块可以包含另一个模块,该模块又可以包含另一个模块。我们将前者称为父模块,而后者称为子模块。子模块必须在它们父模块的 modules 属性中声明。例如,

namespace app\modules\forum;

class Module extends \yii\base\Module
{
    public function init()
    {
        parent::init();

        $this->modules = [
            'admin' => [
                // you should consider using a shorter namespace here!
                'class' => 'app\modules\forum\modules\admin\Module',
            ],
        ];
    }
}

对于嵌套模块中的控制器,其路由应包含其所有祖先模块的 ID。例如,路由 forum/admin/dashboard/index 代表 admin 模块中的 dashboard 控制器的 index 操作,而 admin 模块是 forum 模块的子模块。

信息:getModule() 方法仅返回直接属于其父级的子模块。该 yii\base\Application::$loadedModules 属性保留一个已加载模块列表,包括直接子级和嵌套模块,并按其类名索引。

从模块内访问组件

自 2.0.13 版本起,模块支持 树遍历。这允许模块开发人员通过其模块的服务定位器来引用(应用程序)组件。这意味着最好使用 $module->get('db') 而不是 Yii::$app->get('db')。模块的用户能够指定要在模块中使用的特定组件,以防需要不同的组件(配置)。

例如,考虑此应用程序配置的片段

'components' => [
    'db' => [
        'tablePrefix' => 'main_',
        'class' => Connection::class,
        'enableQueryCache' => false
    ],
],
'modules' => [
    'mymodule' => [
        'components' => [
            'db' => [
                'tablePrefix' => 'module_',
                'class' => Connection::class
            ],
        ],
    ],
],

应用程序数据库表将以 main_ 为前缀,而所有模块表将以 module_ 为前缀。请注意,上面的配置不会合并;例如,模块的组件将启用查询缓存,因为这是默认值。

最佳实践

模块最适合用于可以将功能划分为多个组的大型应用程序,每个组都包含一组密切相关的功能。每个此类功能组都可以作为由特定开发人员或团队开发和维护的模块来开发。

模块也是在功能组级别重用代码的好方法。一些常用的功能,例如用户管理、评论管理,都可以用模块的形式开发,以便可以轻松地在未来的项目中重用它们。

发现错别字或您认为此页面需要改进?
在 github 上编辑它 !