Craft CMS のモジュールを作ってみよう

エントリ一覧のサブナビゲーションには、常に「すべてのエントリ」リンクが表示されます。
「直近で更新されたエントリがあるか?」を確認するのに便利ではあるものの、セクションを跨いだ情報なこともあり、あまり利用しないケースがほとんどです。

そこで、Craft Generator が生成したひな形から「すべてのエントリ」リンクを非表示にするカスタムモジュールを作成してみたいと思います。

これは Craft CMS Advent Calendar 2022 24日目の記事です。

モジュールのひな形を作成

はじめに、モジュールのひな形を生成します。Craft Generator をインストールした状態で、次のコマンドを実行します。

./craft make module

モジュールに必要なの3つの項目を入力します。

CLI コマンドの実行サンプル

項目説明入力サンプル
Module IDモジュール ID(ケバブケース)sample-module
Module locationモジュールの生成先modules/sampleModule
Should the module be loaded during app initialization?Craft の初期化時にモジュールを読み込むようにするか?yes

Module location が生成先のパスと同時にモジュールの名前空間として利用されるため、Module ID にハイフンを含める場合は Module location をキャメルケースで記述しましょう。

config/app.php の確認

Should the module be loaded during app initialization?yes と答えた場合、自動的にアップデートされます。

<?php
use craft\helpers\App;
use modules\sampleModule\Module;

return [
    'id' => App::env('APP_ID') ?: 'CraftCMS',
    'modules' => [
        'my-module' => \modules\Module::class, 'sample-module' => Module::class,
    ],
    // (中略)
    'bootstrap' => ['sample-module'],
];

sampleModule または sample-module を含む3箇所が、Craft Generator によって書き加えられています。カスタムモジュールが不要になった場合は、これらの除去と modules/sampleModule/ ディレクトリの削除が必要です。

「すべてのエントリ」を非表示にする

先に完成形のコードを貼っておきます。

<?php

namespace modules\sampleModule;

use Craft;
use yii\base\Module as BaseModule;

use craft\elements\Entry;
use craft\events\RegisterElementSourcesEvent;
use yii\base\Event;

/**
 * sample-module module
 *
 * @method static Module getInstance()
 */
class Module extends BaseModule
{
    public function init()
    {
        // Set the controllerNamespace based on whether this is a console or web request
        if (Craft::$app->getRequest()->getIsConsoleRequest()) {
            $this->controllerNamespace = 'modules\\sampleModule\\console\\controllers';
        } else {
            $this->controllerNamespace = 'modules\\sampleModule\\controllers';
        }

        parent::init();

        // Defer most setup tasks until Craft is fully initialized
        Craft::$app->onInit(function() {
            $this->attachEventHandlers();
            // ...
        });
    }

    private function attachEventHandlers(): void
    {
        Event::on(
            Entry::class,
            Entry::EVENT_REGISTER_SOURCES,
            function (RegisterElementSourcesEvent $event) {
                if ($event->context === 'index') {
                    foreach ($event->sources as $i => $source) {
                        if (isset($source['key']) && ('*' == $source['key'])) {
                            unset($event->sources[$i]);
                        }
                    }
                }
            }
        );
    }
}

このサンプルで、実際に追記したのは次の2箇所です。

必要なクラスの読み込み

今回は「エントリ一覧のソース(サブナビゲーションの表示項目)の登録イベント」を操作したいため、次のクラスが必要です。

use craft\elements\Entry;
use craft\events\RegisterElementSourcesEvent;
use yii\base\Event;

公式ドキュメントの Event Code Generatorsource と入力すると表示される候補から、目的のものを選択してサンプルコードを確認するとよいでしょう。

Events | Craft CMS Documentation | 4.x
https://craftcms.com/docs/4.x/extend/events.html

craft\elements\Entry::EVENT_REGISTER_SOURCES のサンプルコード

なお、craft\elements\Category::EVENT_REGISTER_SOURCES を選択し、カテゴリ一覧を対象にするといったカスタマイズも可能です。

イベント処理

ここでは、サブナビゲーションの表示内容が確定する EVENT_REGISTER_SOURCES イベントのソース($event->sources の配列)から、「すべてのエントリ」を除去しています。

Event::on(
    Entry::class,
    Entry::EVENT_REGISTER_SOURCES,
    function (RegisterElementSourcesEvent $event) {
        if ($event->context === 'index') {
            foreach ($event->sources as $i => $source) {
                if (isset($source['key']) && ('*' == $source['key'])) {
                    unset($event->sources[$i]);
                }
            }
        }
    }
);

なお、Ray での $event の出力結果のキャプチャも貼っておきます。

$event を Ray で確認

これを見ると「isset($source['key']) で見出しを除外」し、「'*' == $source['key'] ですべてのエントリのみ対象」としているのが判りますね。

あとは、管理画面でエントリ一覧を表示して「すべてのエントリ」リンクが非表示になっていることを確認できれば OK です。(コード変更直後は反映までに数秒かかる場合があります。)

最後に

今回は Craft Generator で簡単なカスタムモジュールを作成してみました。
ベースとなる部分を簡単に用意できると、必要な処理をどう実装するかに集中できるのでやはり便利ですね。

とはいえ、標準機能や既成プラグインの組み合わせで賄えることも多いため、実際に自分でカスタムプラグインやモジュールを用意する必要性がないかもしれませんが。。。

comments powered by Disqus