mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-02 04:37:42 +08:00
285 lines
19 KiB
Markdown
285 lines
19 KiB
Markdown
Модули
|
||
=======
|
||
|
||
Модули - это законченные программные блоки, состоящие из [моделей](structure-models.md), [представлений](structure-views.md), [контроллеров](structure-controllers.md) и других вспомогательных компонентов. При установке модулей в [приложение](structure-applications.md), конечный пользователь получает доступ к их контроллерам. По этой причине модули часто рассматриваются как миниатюрные приложения. В отличии от [приложений](structure-applications.md), модули нельзя развертывать отдельно. Модули должны находиться внутри приложений.
|
||
|
||
|
||
## Создание модулей <span id="creating-modules"></span>
|
||
|
||
Модуль помещается в директорию, которая называется [[yii\base\Module::basePath|базовым путем]] модуля. Так же как и в
|
||
директории приложения, в этой директории существуют поддиректории `controllers`, `models`, `views` и другие, в которых
|
||
размещаются контроллеры, модели, представления и другие элементы. В следующем примере показано примерное содержимое модуля:
|
||
|
||
```
|
||
forum/
|
||
Module.php файл класса модуля
|
||
controllers/ содержит файлы классов контроллеров
|
||
DefaultController.php файл класса контроллера по умолчанию
|
||
models/ содержит файлы классов моделей
|
||
views/ содержит файлы представлений контроллеров и шаблонов
|
||
layouts/ содержит файлы представлений шаблонов
|
||
default/ содержит файлы представления контроллера DefaultController
|
||
index.php файл основного представления
|
||
```
|
||
|
||
|
||
### Классы модулей <span id="module-classes"></span>
|
||
|
||
Каждый модуль объявляется с помощью уникального класса, который наследуется от [[yii\base\Module]]. Этот класс должен
|
||
быть помещен в корне [[yii\base\Module::basePath|базового пути]] модуля и поддерживать [автозагрузку](concept-autoloading.md).
|
||
Во время доступа к модулю будет создан один экземпляр соответствующего класса модуля. Как и
|
||
[экземпляры приложения](structure-applications.md), экземпляры модулей нужны, чтобы код модулей мог получить общий
|
||
доступ к данным и компонентам.
|
||
|
||
Приведем пример того, как может выглядеть класс модуля:
|
||
|
||
```php
|
||
namespace app\modules\forum;
|
||
|
||
class Module extends \yii\base\Module
|
||
{
|
||
public function init()
|
||
{
|
||
parent::init();
|
||
|
||
$this->params['foo'] = 'bar';
|
||
// ... остальной инициализирующий код ...
|
||
}
|
||
}
|
||
```
|
||
|
||
Если метод `init()` стал слишком громоздким из-за кода, который задает свойства модуля, эти свойства можно сохранить
|
||
в виде [конфигурации](concept-configurations.md), а затем загрузить в методе `init()` следующим образом:
|
||
|
||
```php
|
||
public function init()
|
||
{
|
||
parent::init();
|
||
// инициализация модуля с помощью конфигурации, загруженной из config.php
|
||
\Yii::configure($this, require __DIR__ . '/config.php');
|
||
}
|
||
```
|
||
|
||
При этом в конфигурационном файле `config.php` может быть код следующего вида, аналогичный
|
||
[конфигурации приложения](structure-applications.md#application-configurations):
|
||
|
||
```php
|
||
<?php
|
||
return [
|
||
'components' => [
|
||
// список конфигураций компонентов
|
||
],
|
||
'params' => [
|
||
// список параметров
|
||
],
|
||
];
|
||
```
|
||
|
||
|
||
### Контроллеры в модулях <span id="controllers-in-modules"></span>
|
||
|
||
При создании контроллеров модуля принято помещать классы контроллеров в подпространство `controllers` пространства
|
||
имён класса модуля. Это также подразумевает, что файлы классов контроллеров должны располагаться в директории `controllers`
|
||
[[yii\base\Module::basePath|базового пути]] модуля. Например, чтобы описать контроллер `post` в модуле `forum` из
|
||
предыдущего примера, класс контроллера объявляется следующим образом:
|
||
|
||
```php
|
||
namespace app\modules\forum\controllers;
|
||
|
||
use yii\web\Controller;
|
||
|
||
class PostController extends Controller
|
||
{
|
||
// ...
|
||
}
|
||
```
|
||
|
||
Изменить пространство имен классов контроллеров можно задав свойство [[yii\base\Module::controllerNamespace]]. Если
|
||
какие-либо контроллеры выпадают из этого пространства имен, доступ к ним можно осуществить, настроив свойство
|
||
[[yii\base\Module::controllerMap]], аналогично тому, [как это делается в приложении](structure-applications.md#controller-map).
|
||
|
||
|
||
### Представления в модулях <span id="views-in-modules"></span>
|
||
|
||
Представления модуля также следует поместить в поддиректорию `views` [[yii\base\Module::basePath|базового пути]]
|
||
модуля. Виды, которые рендерит контроллер модуля, должны располагаться в директории `views/ControllerID`, где `ControllerID`
|
||
соответствует [идентификатору контроллера](structure-controllers.md#routes). Например, если контроллер реализуется
|
||
классом `PostController`, представления следует разместить в поддиректории `views/post`
|
||
[[yii\base\Module::basePath|базового пути]] модуля.
|
||
|
||
В модуле можно задать [шаблон](structure-views.md#layouts), который будет использоваться для рендеринга всех представлений
|
||
контроллерами модуля. По умолчанию шаблон помещается в директорию `views/layouts`, а свойство [[yii\base\Module::layout]]
|
||
должно указывать на имя этого шаблона. Если не задать свойство `layout`, модуль будет использовать шаблон, заданный
|
||
в приложении.
|
||
|
||
### Консольные команды в модулях <span id="console-commands-in-modules"></span>
|
||
|
||
Ваш модуль также может объявлять команды, которые будут доступны через [консоль](tutorial-console.md).
|
||
|
||
Для того, чтобы команда стала доступна, надо изменить свойство [[yii\base\Module::controllerNamespace]] для консольного
|
||
режима так, чтобы оно содержало пространство имён ваших команд.
|
||
|
||
Этого можно добиться проверяя класс экземпляра приложения Yii в методе `init` модуля:
|
||
|
||
```php
|
||
public function init()
|
||
{
|
||
parent::init();
|
||
if (Yii::$app instanceof \yii\console\Application) {
|
||
$this->controllerNamespace = 'app\modules\forum\commands';
|
||
}
|
||
}
|
||
```
|
||
|
||
Ваши команды будут доступны из командной строки как:
|
||
|
||
```
|
||
yii <module_id>/<command>/<sub_command>
|
||
```
|
||
|
||
|
||
|
||
## Использование модулей <span id="using-modules"></span>
|
||
|
||
Чтобы задействовать модуль в приложении, достаточно включить его в свойство [[yii\base\Application::modules|modules]]
|
||
в конфигурации приложения. Следующий код в [конфигурации приложения](structure-applications.md#application-configurations)
|
||
задействует модуль `forum`:
|
||
|
||
```php
|
||
[
|
||
'modules' => [
|
||
'forum' => [
|
||
'class' => 'app\modules\forum\Module',
|
||
// ... другие настройки модуля ...
|
||
],
|
||
],
|
||
]
|
||
```
|
||
|
||
Свойству [[yii\base\Application::modules|modules]] присваивается массив, содержащий конфигурацию модуля. Каждый ключ массива
|
||
представляет собой *идентификатор модуля*, который однозначно определяет модуль среди других модулей приложения,
|
||
а соответствующий массив - это [конфигурация](concept-configurations.md) для создания модуля.
|
||
|
||
|
||
### Маршруты <span id="routes"></span>
|
||
|
||
Как маршруты приложения используются для обращения к контроллерам приложения, [маршруты](structure-controllers.md#routes)
|
||
модуля используются, чтобы обращаться к контроллерам этого модуля. Маршрут контроллера в модуле должен начинаться с
|
||
идентификатора модуля, за которым следуют [идентификатор контроллера](structure-controllers.md#controller-ids) и
|
||
[идентификатор действия](structure-controllers.md#action-ids). Например, если в приложении задействован модуль `forum`,
|
||
то маршрут `forum/post/index` соответствует действию `index` контроллера `post` этого модуля. Если маршрут состоит только
|
||
из идентификатора модуля, то контроллер и действие определяются исходя из свойства [[yii\base\Module::defaultRoute]],
|
||
которое по умолчанию равно `default`. Таким образом, маршрут `forum` соответствует контроллеру `default` модуля `forum`.
|
||
|
||
Правила в URL manager для модулей должны быть добавленны перед началом работы [[yii\web\UrlManager::parseRequest()]], что не
|
||
позволяет размещать код добавления правил модуля в `init()`, так как инициализация происходит уже после обработки маршрутов. Таким образом, добавление маршрутов необходимо осуществить в [предзагрузке
|
||
модуля](structure-extensions.md#bootstrapping-classes). Хорошей практикой, также, будет объединение всех правил модуля
|
||
при помощи [[\yii\web\GroupUrlRule]].
|
||
|
||
Если же вы используете модуль для [версионирования API](rest-versioning.md), URL правила необходимо добавлять
|
||
непосредственно в конфигурации `urlManager` приложения.
|
||
|
||
### Получение доступа к модулям <span id="accessing-modules"></span>
|
||
|
||
Зачастую внутри модуля может потребоваться доступ к экземпляру [класса модуля](#module-classes), через который получаются
|
||
идентификатор модуля, его параметры, компоненты, и т. п. Это можно сделать с помощью следующей конструкции:
|
||
|
||
```php
|
||
$module = MyModuleClass::getInstance();
|
||
```
|
||
|
||
где `MyModuleClass` соответствует имени класса модуля, доступ к которому нужно получить. Метод `getInstance()` возвращает
|
||
запрошенный в данный момент экземпляр класса модуля. Если модуль не запрошен, метод вернет `null`. Учтите, что обычно
|
||
экземпляры класса модуля вручную не создаются, так как созданный вручную экземпляр будет отличаться от экземпляра,
|
||
созданного Yii в качестве ответа на запрос.
|
||
|
||
> Info: При разработке модуля нельзя исходить из предположения, что модулю будет назначен конкретный идентификатор.
|
||
Это связано с тем, что идентификатор, назначаемый модулю при использовании в приложении или в другом модуле, может быть
|
||
выбран совершенно произвольно. Чтобы получить идентификатор модуля, нужно вначале выбрать экземпляр модуля, как это
|
||
описано выше, а затем получить доступ к идентификатору через свойство `$module->id`.
|
||
|
||
Доступ к экземпляру модуля можно получить следующими способами:
|
||
|
||
```php
|
||
// получение дочернего модуля с идентификатором "forum"
|
||
$module = \Yii::$app->getModule('forum');
|
||
|
||
// получение модуля, к которому принадлежит запрошенный в настоящее время контроллер
|
||
$module = \Yii::$app->controller->module;
|
||
```
|
||
|
||
Первый подход годится только если известен идентификатор модуля, а второй подход наиболее полезен, если известно,
|
||
какой контроллер запрошен.
|
||
|
||
Имея экземпляр модуля можно получить доступ к параметрам и компонентам, зарегистрированным в модуле. Например,
|
||
|
||
```php
|
||
$maxPostCount = $module->params['maxPostCount'];
|
||
```
|
||
|
||
|
||
### Предзагрузка модулей <span id="bootstrapping-modules"></span>
|
||
|
||
Может потребоваться запускать некоторые модули при каждом запросе. Модуль [[yii\debug\Module|debug]] - один из таких
|
||
модулей. Для этого список идентификаторов таких модулей необходимо указать в свойстве
|
||
[[yii\base\Application::bootstrap|bootstrap]] приложения.
|
||
|
||
Например, следующая конфигурация приложения обеспечивает загрузку модуля `debug` при каждом запросе:
|
||
|
||
```php
|
||
[
|
||
'bootstrap' => [
|
||
'debug',
|
||
],
|
||
|
||
'modules' => [
|
||
'debug' => 'yii\debug\Module',
|
||
],
|
||
]
|
||
```
|
||
|
||
|
||
## Вложенные модули <span id="nested-modules"></span>
|
||
|
||
Модули могут вкладываться друг в друга без ограничений по глубине. Иными словами, в модуле содержится модуль, в который
|
||
входит еще один модуль, и т. д. Первый модуль называется *родительским*, остальные - *дочерними*. Дочерние модули
|
||
объявляются в свойстве [[yii\base\Module::modules|modules]] родительских модулей. Например,
|
||
|
||
```php
|
||
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`.
|
||
|
||
> Info: Метод [[yii\base\Module::getModule()|getModule()]] возвращает только те дочерние модули, которые
|
||
принадлежат родительскому модулю непосредственно. В свойстве [[yii\base\Application::loadedModules]] содержится
|
||
список загруженных модулей, в том числе прямых и косвенных потомков, с индексированием по имени класса.
|
||
|
||
|
||
## Лучшие практики<span id="best-practices"></span>
|
||
|
||
Модули лучше всего подходят для крупных приложений, функционал которых можно разделить на несколько групп, в каждой из
|
||
которых функции тесно связаны между собой. Каждая группа функций может разрабатываться в виде модуля, над которым работает
|
||
один разработчик или одна команда.
|
||
|
||
Модули - это хороший способ повторно использовать код на уровне групп функций. В виде модулей можно реализовать такую
|
||
функциональность, как управление пользователями или управление комментариями, а затем использовать эти модули в будущих
|
||
разработках.
|