Craft CMS で日本語タイトルをローマ字変換して slug にセットする(後編)

前編に引き続き、Craft CMS で日本語タイトルをローマ字変換して slug にセットする方法について考えていきますが、 実装にあたって欲しい機能は次の3つです。

  • エントリのタイトルをローマ字変換して slug にセットする
  • エントリの初回保存時のみ、実行する
  • slug に日本語が含まれない場合は、何もしない

このくらいであれば、モジュールとして用意するのが手軽で良さそうですね。

では、具体的な手順を見ていきましょう。
これは Craft CMS Advent Calendar 2023 20日目の記事です。

モジュールの作成

はじめに、モジュールを作成しましょう。

モジュールの作成は、Craft Generator を利用します。
Craft Generator の導入方法については、Craft CMS のプラグインやモジュールのひな形を作るを参考にしてください。

Craft CMS 本体のインストールディレクトリに移動して、次のコマンドを実行します。

./craft make module

ここでは、Craft CMS のモジュールを作ってみようと同じ内容で作成しました。

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

Should the module be loaded during app initialization?yes と答えているため、自動的に config/app.php にも変更が加えられます。

ユーティリティ > システム情報のサンプル

管理画面の ユーティリティ > システム情報 にアクセスすると、モジュールが読み込まれていることを確認できますね。

実行ファイルの配置

次に、前編で作成した実行ファイル convert2hepburn.py を Craft CMS 本体のインストールディレクトリ直下にある modules/sampleModule/ 内に配置します。

実行ファイル配置後のサンプル

なお、Craft CMS の実行環境で python3pykakasi が使える前提となりますので、必要に応じてインストールしておいてください。

カスタムモジュールにイベント処理を追加

最後に、modules/sampleModule/Module.php を開き、次のように修正します。

<?php

namespace modules\sampleModule;

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

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

/**
 * sample-module module
 *
 * @method static Module getInstance()
 */
class Module extends BaseModule
{
    public function init(): void
    {
        Craft::setAlias('@modules/sampleModule', __DIR__);

        // Set the controllerNamespace based on whether this is a console or web request
        if (Craft::$app->request->isConsoleRequest) {
            $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_BEFORE_SAVE,
            function (ModelEvent $event) {
                $entry = $event->sender;

                // 初回セーブ、かつ、$entry->slug に日本語を含む場合のみ実行
                if ($entry->firstSave && preg_match('/[ぁ-ん]+|[ァ-ヴ]+|[一-龠]+/u', $entry->slug)) {
                    // 実行ファイルのパスをセット
                    $filePath = CRAFT_BASE_PATH . '/modules/sampleModule/convert2hepburn.py';

                    // コマンドを整形して、実行
                    $command = 'python3 ' . $filePath . ' ' . escapeshellarg($entry->title);
                    exec($command, $output);

                    // ローマ字変換された文字列を $entry->slug にセット
                    $entry->slug = $output[0];
                }
            }
        );
    }
}

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

必要なクラスの読み込み

今回は「エントリの保存前イベント」を操作したいため、次のクラスが必要です。

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

イベント処理

ここでは、自動保存を除くエントリの初回保存時に slug へ日本語を含む場合のみ、ローマ字変換を行うようにしています。

Event::on(
    Entry::class,
    Entry::EVENT_BEFORE_SAVE,
    function (ModelEvent $event) {
        $entry = $event->sender;

        // 初回セーブ、かつ、$entry->slug に日本語を含む場合のみ実行
        if ($entry->firstSave && preg_match('/[ぁ-ん]+|[ァ-ヴ]+|[一-龠]+/u', $entry->slug)) {
            // 実行ファイルのパスをセット
            $filePath = CRAFT_BASE_PATH . '/modules/sampleModule/convert2hepburn.py';

            // コマンドを整形して、実行
            $command = 'python3 ' . $filePath . ' ' . escapeshellarg($entry->title);
            exec($command, $output);

            // ローマ字変換された文字列を $entry->slug にセット
            $entry->slug = $output[0];
        }
    }
);

exec() で実行するにあたり、エントリのタイトルを escapeshellarg() でエスケープしています。
また、実行結果は $output[0] に格納されるため、これを $entry->slug にセットしています。

サンプルとしてザックリ実装しているため、対象セクションを制限したり、エラーハンドリングを追加するなど、必要に応じて調整してください。

動作を確認してみる

では、実際に動作を確認してみましょう。

エントリ編集画面のサンプル

自動保存の状態では、slug は日本語表示のままです。

エントリ編集画面のサンプル(変換処理実行後)

command + S で明示的に保存したところ、slug がローマ字変換されました。

これで完成ですね。

まとめ

2回にわたって、Craft CMS で日本語タイトルをローマ字変換して slug にセットする方法について考えてきました。

込み入った実装でなければ、モジュールとして用意するのが手軽で良さそうですね。
また、(邪道かとは思いつつ)PHP に拘り過ぎないことも大事かな?と、改めて感じました。

たまたま目にしたポストがきっかけだったものの、実際に作ってみるとなかなか面白い機能で、個人的にも勉強になりました。

という訳で。
ハッシュタグ #craftcms でポストいただければ、勝手に試してブログ記事にさせてもらう・・・かもしれません。