モジュール

モジュールは、モデルビューコントローラ、およびその他の支援コンポーネントから構成される自己充足的なソフトウェアのユニットです。 モジュールが アプリケーション にインストールされている場合、エンド・ユーザはモジュールのコントローラにアクセスする事が出来ます。 これらのことを理由として、モジュールは小さなアプリケーションと見なされることがよくあります。 しかし、モジュールは単独では配備できず、アプリケーションの中に存在しなければならないという点で アプリケーション とは異なります。

モジュールを作成する

モジュールは、モジュールの yii\base\Module::basePath と呼ばれるディレクトリとして編成されます。 このディレクトリの中に、ちょうどアプリケーションの場合と同じように、controllersmodelsviews のようなサブ・ディレクトリが存在して、コントローラ、モデル、ビュー、その他のコードを収納しています。 次の例は、モジュール内の中身を示すものです。

forum/
    Module.php                   モジュール・クラス・ファイル
    controllers/                 コントローラ・クラス・ファイルを含む
        DefaultController.php    デフォルトのコントローラ・クラス・ファイル
    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 となります。

モジュールは、そのモジュールのコントローラによってレンダリングされるビューに適用される レイアウト を指定することが出来ます。 レイアウトは、デフォルトでは views/layouts ディレクトリに置かれなければならず、また、 yii\base\Module::$layout プロパティがレイアウトの名前を指すように構成しなければなりません。 layout プロパティを構成しない場合は、アプリケーションのレイアウトが代りに使用されます。

モジュール内のコンソールコマンド

コンソール モードで使用する事が出来るコマンドをモジュール内で宣言することも可能です。

あなたのコマンドがコマンド・ライン・ユーティリティから見えるようにするためには、Yii がコンソール・モードで実行されたときに yii\base\Module::$controllerNamespace を変更して、コマンドの名前空間を指し示すようにする必要があります。

それを達成する一つの方法は、モジュールの 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>

モジュールを使う

アプリケーションの中でモジュールを使うためには、アプリケーションの yii\base\Application::modules プロパティのリストにそのモジュールを載せてアプリケーションを構成するだけで大丈夫です。 アプリケーションの構成情報 の中の次のコードは、forum モジュールを使うようにするものです。

[
    'modules' => [
        'forum' => [
            'class' => 'app\modules\forum\Module',
            // ... モジュールのその他の構成情報 ...
        ],
    ],
]

Info: モジュール内のコマンドを使用するためには、 モジュールを コンソール・アプリケーション設定 にも含める必要があります。

yii\base\Application::modules プロパティは、モジュールの構成情報の配列を取ります。 各配列のキーは、アプリケーションの全てのモジュールの中でそのモジュールを特定するためのユニークな モジュール ID を表します。 そして、対応する配列の値は、そのモジュールを作成するための 構成情報 です。

ルート

アプリケーションの中のコントローラをアクセスするのと同じように、ルート がモジュールの中のコントローラを指し示すために使われます。 モジュール内のコントローラのルートは、モジュール ID で始まり、コントローラ IDアクション ID と続くものでなければなりません。 例えば、アプリケーションが forum という名前のモジュールを使用している場合、forum/post/index というルートは、 forum モジュール内の post コントローラの index アクションを表します。 ルートがモジュール ID だけを含む場合は、yii\base\Module::$defaultRoute プロパティ (デフォルト値は default です) が、どのコントローラ・アクションが使用されるべきかを決定します。 これは、forum というルートは forum モジュール内の default コントローラを表すという意味です。

モジュールのための URL マネージャの規則は yii\web\UrlManager::parseRequest() が起動される前に追加されなくてはなりません。 すなわち、モジュールの init() の中で規則を追加しても動作しない、ということです。なぜなら、モジュールの初期化はルートの処理が済んだ後になるからです。 従って、URL 規則は ブートストラップ段階 で追加される必要があります。 また、モジュールの URL 規則を yii\web\GroupUrlRule に入れるのも良い方法です。

モジュールが バージョン管理された API で使用される場合は、その URL 規則はアプリケーション構成情報の urlManager のセクションに直接に追加されなければなりません。

モジュールにアクセスする

モジュール内において、モジュール ID や、モジュールのパラメータ、モジュールのコンポーネントなどにアクセスするために、モジュール・クラス のインスタンスを取得する必要があることがよくあります。 次の文を使ってそうすることが出来ます。

$module = MyModuleClass::getInstance();

ここで MyModuleClass は、当該モジュール・クラスの名前を指すものです。 getInstance() メソッドは、現在リクエストされているモジュール・クラスのインスタンスを返します。 モジュールがリクエストされていない場合は、このメソッドは null を返します。モジュール・クラスの新しいインスタンスを手動で作成しようとしてはいけないことに注意してください。 手動で作成したインスタンスは、リクエストに対するレスポンスとして Yii によって作成されたインスタンスとは別のものになります。

Info: モジュールを開発するとき、モジュールが固定の ID を使うと仮定してはいけません。 なぜなら、モジュールは、アプリケーションや他のモジュールの中で使うときに、任意の ID と結び付けることが出来るからです。 モジュール ID を取得するためには、上記の方法を使って最初にモジュールのインスタンスを取得し、 そして $module->id によって ID を取得しなければなりません。

モジュールのインスタンスにアクセスするためには、次の二つの方法を使うことも出来ます。

// ID が "forum" である子モジュールを取得する
$module = \Yii::$app->getModule('forum');

// 現在リクエストされているコントローラが属するモジュールを取得する
$module = \Yii::$app->controller->module;

最初の方法は、モジュール ID を知っている時しか役に立ちません。一方、第二の方法は、 リクエストされているコントローラについて知っている場合に使うのに最適な方法です。

いったんモジュールのインスタンスをとらえれば、モジュールに登録されたパラメータやコンポーネントにアクセスすることが可能になります。例えば、

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

モジュールをブートストラップする

いくつかのモジュールは、全てのリクエストで毎回走らせる必要があります。デバッグ・モジュールがその一例です。 そうするためには、そのようなモジュールをアプリケーションの 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',
            ],
        ];
    }
}

入れ子にされたモジュールの中にあるコントローラのルートは、全ての祖先のモジュールの ID を含まなければなりません。 例えば、forum/admin/dashboard/index というルートは、forum モジュールの子モジュールである admin モジュールの dashboard コントローラの index アクションを表します。

Info: getModule() メソッドは、親モジュールに直接属する子モジュールだけを返します。 yii\base\Application::$loadedModules プロパティがロードされた全てのモジュールのリストを保持しています。 このリストには、直接の子と孫以下の両方のモジュールが含まれ、クラス名によってインデックスされています。

モジュール内からコンポーネントにアクセスする

バージョン 2.0.13 以降、モジュールは ツリー走査 をサポートしています。 これによって、モジュールの開発者は(アプリケーション)コンポーネントを参照するのに、 サービス・ロケータとして自身のモジュールを使うことが出来るようになりました。 これが意味することは、Yii::$app->get('db') よりも $module->get('db') を使う方が良い、ということです。 モジュールのユーザは、異なるコンポーネント(構成)が要求される場合は、モジュールで使用する特定のコンポーネントを指定することが出来ます。

例えば、次のような部分的なアプリケーション構成が可能です。

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

アプリケーションのデータベース・テーブルは main_ という接頭辞を持つ一方で、モジュールのデータベース・テーブルは module_ という接頭辞を持ちます。 上記の構成はマージされないということに注意して下さい。例えば、モジュールのコンポーネントではクエリ・キャッシュはデフォルト値に従って有効なままになります。

ベスト・プラクティス

モジュールは、それぞれ密接に関係する一連の機能を含む数個のグループに分割できるような、 規模の大きなアプリケーションに最も適しています。 そのような機能グループをそれぞれモジュールとして、特定の個人やチームによって開発することが出来ます。

モジュールは、また、機能グループ・レベルでコードを再利用するための良い方法でもあります。 ある種のよく使われる機能、例えばユーザ管理やコメント管理などは、全て、将来のプロジェクトで容易に再利用できるように、 モジュールの形式で開発することが出来ます。