Модули представляют собой автономные программные модули, которые состоят из моделей, представлений, контроллеров и других вспомогательных компонентов. Конечные пользователи могут обращаться к контроллерам модуля, когда он установлен в приложении. По этим причинам модули часто рассматриваются как мини-приложения. Модули отличаются от приложений, в которых модули не могут быть развернуты в одиночку и должны находиться в приложениях.

Создание модуля

Модуль организован как каталог, который называется базовым путем модуля. Внутри каталога есть подкаталоги, такие как controllers, models, views, которые содержат контроллеры, модели, представления и другой код, как и в приложении. В следующем примере показано содержимое внутри модуля:

forum/
    Module.php                   файл класса модуля
    controllers/                 каталог с файлами классов контроллера
        DefaultController.php    файла класса контроллера по умолчанию
    models/                      каталог с файлами классов моделей
    views/                       каталог с файлами отображения и макетов
        layouts/                 каталог файлов макетов  
        default/                 каталог содержащий файлы представлений для DefaultController
            index.php            index файл отображения

Классы модулей

Каждый модуль должен иметь уникальный класс модуля, который простирается от yii\base\Module. Класс должен располагаться непосредственно под базовым путем модуля и должен быть автозагружаемым. При обращении к модулю будет создан один экземпляр соответствующего класса модуля. Подобно экземплярам приложения, экземпляры модулей используются для совместного использования данных и компонентов для кода внутри модулей.

Ниже приведен пример, как класс модуля может выглядеть следующим образом:

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' => [
        // список параметров
    ],
];

Контроллеры в модулях

При создании контроллеров в модуле соглашение должно помещать классы контроллера в пространство подпространств контроллеров пространства имен класса модуля. Это также означает, что файлы класса контроллера должны быть помещены в каталог контроллеров в базовом пути модуля. Например, чтобы создать пост-контроллер в модуле форума, показанном в предыдущем подразделе, вы должны объявить класс контроллера следующим образом:

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 ссылается на идентификатор контроллера. Например, если класс контроллера - PostController, то каталог будет views/post в базовом пути модуля.

Модуль может определять layouts, который применяется к представлениям, отображаемым контроллерами модуля. Макет следует поместить в каталог views/layouts по умолчанию, и вы должны настроить свойство yii\base\Module::$layout, чтобы указать имя макета. Если вы не настроите свойство layout, вместо него будет использоваться макет приложения.

Команды консоли в модулях

Ваш модуль может также объявлять команды, которые будут доступны через консольный режим.

Чтобы утилита из командной строки увидела ваши команды, вам нужно будет изменить свойство yii\base\Module::$controllerNamespace, когда Yii будет выполняться в консольном режиме и указать его на ваше пространство имен команд.

Один из способов достижения этого - проверить тип экземпляра приложения Yii в методе init() модуля:

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 принимает массив конфигураций модулей. Каждый ключ массива представляет собой идентификатор модуля, который однозначно идентифицирует модуль среди всех модулей в приложении, а соответствующее значение массива представляет собой конфигурацию для создания модуля.

Routes

Подобно доступу к контроллерам в приложении, маршруты (routes) используются для адресации контроллеров в модуле. Маршрут для контроллера внутри модуля должен начинаться с идентификатора модуля, за которым следует ID контроллера и ID действия. Например, если приложение использует модуль с именем forum, тогда forum/post/index маршрут будет представлять собой index действие post контроллера в модуле. Если маршрут содержит только идентификатор модуля, тогда свойство yii\base\Module::$defaultRoute, которое по умолчанию используется default, определит, какой контроллер/действие следует использовать. Это означает, что forum маршрутизации будет представлять собой контроллер default в модуле forum.

Доступ к модулям

Внутри модуля вам часто может потребоваться получить экземпляр класса модуля, чтобы вы могли получить доступ к идентификатору модуля, параметрам модуля, компонентам модуля и т. д. Вы можете сделать это, используя следующий оператор:

$module = MyModuleClass::getInstance();

где MyModuleClass ссылается на имя интересующего вас класса модуля. Метод getInstance() возвратит запрошенный экземпляр класса модуля. Если модуль не запрошен, метод вернет null. Обратите внимание, что вам не нужно вручную создавать новый экземпляр класса модуля, потому что он будет отличаться от того, который был создан Yii в ответ на запрос.

Вы также можете получить доступ к экземпляру модуля, используя следующие подходы:

// получение дочернего модуля с ID "forum"
$module = \Yii::$app->getModule('forum');

// получить модуль, к которому принадлежит запрашиваемый контроллер
$module = \Yii::$app->controller->module;

Первый подход полезен только тогда, когда вы знаете идентификатор модуля, в то время как второй подход лучше всего использовать, когда вы знаете о запросах контроллеров.

Когда у вас есть экземпляр модуля, вы можете получить доступ к параметрам и компонентам, зарегистрированным в модуле. Например:

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

Модуль начальной загрузки (Bootstrapping Modules)

Для каждого запроса может потребоваться запуск некоторых модулей. Модуль отладки является таким примером. Для этого перечислите идентификаторы таких модулей в свойстве начальной загрузки приложения.

Например, следующая конфигурация приложения гарантирует, что модуль 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' => [
                // здесь нужно использовать более короткое пространство имен
                'class' => 'app\modules\forum\modules\admin\Module',
            ],
        ];
    }
}

Для контроллера внутри вложенного модуля его маршрут должен включать в себя идентификаторы всех его модулей-предков. Например, forum/admin/dashboard/index представляет собой index действие контроллера dashboard в модуле admin, который является дочерним модулем модуля forum.