Claude CodeとCursorで実現するPowerCMS Xプラグイン開発の効率化

公開

昨年秋頃からChatGPTは使用していたのですが、他の生成AIはなかなか試すことができずにいました。「本当にAIでコードが書けるのかな?」という疑いの気持ちが強かったからだと思います。しかし、今年の6月頃からCursorClaude Codeを使ってみたところ、「これはすごい!」とすっかりハマってしまいました。

そこからPowerCMS Xの開発においても積極的に生成AIを使った今年の夏でした。この夏を振り返りつつ、生成AIを活用したPowerCMS Xプラグイン開発の現況について少しお伝えしたいと思います。
Claude Codeでパスキー認証処理のプラグイン実装をしている様子

プラグインのコードには2つの要素がある

CMSによらない一般的な処理

例えばImage MagickやGDを用いた画像変換、パスキー認証処理などPowerCMS X以外でもコードの内容が変わらないごく一般的な処理を指します。このような処理は生成AIに適切なコンテキスト設定と指示を出すだけで、なかなかの精度で実装を行ってくれます。

CMS独特の処理

例えばPADOを用いた記事の抽出やログ・セッションの保存など、PowerCMS X独自のクラス・メソッドを用いて実装する必要のある処理です。生成AIがプロジェクト内のファイルを参照して似た実装をしてくれますが、学習量が少ないこともあってかどうしても生成されたコードの手直しが必要になるケースが多いと感じます。今後CLAUDE.md等を活用してAIにルールを伝え、精度向上を図っていきたいと考えています。

生成AIを活用したプラグイン開発の例

  • パスキー認証処理
  • アセットにアップロードした画像を1枚の画像にまとめる処理
  • アップロードしたファイルの検証処理
  • SaaSサービス用のユーザー・ワークスペース登録処理

特にパスキー認証処理を実現するプラグインは、ほぼ全てClaude Codeのプロンプトに指示するだけで開発し、人間はほとんどコードを記述していません。その他のプラグインはメソッド名を少し書いてコンテキストを整えた上で指示を出し、目的の機能を実装してもらうようにしました。

生成AIにより効率化を図ることができたところ

AIによる補完やリファクタリング

Cursorによる補完は強力です。人間の思考を先読みをしてコードを提案してくれるのが本当に楽です。変数名を変えた時なども変更が必要な箇所を示してくれるので、変更漏れが起こる可能性が減ります。さらに、実装したメソッドの説明(PHPDoc)などもほぼ自動生成でき、後回しになりがちなドキュメント化もバッチリです。

メソッドがちょっと長くなったな、という時もAIに指示をすれば適切にリファクタリングしてくれます。

> PasskeyAuthクラスが少し長くなったけど良いのかなぁ。

⏺ その通りですね!PasskeyAuthクラスが長くなりすぎています。単一責任の原則に従って、機能を分割し
  てより保守性の高い構造にしましょう。

⏺ まず、lib ディレクトリを再作成して、WebAuthn の核となる機能を分離したクラスを作成しましょう:

⏺ 完了しました!PasskeyAuthクラスのリファクタリングが完了しました。

  リファクタリングの結果

  以前(長いクラス)

  - 540行 の巨大なPasskeyAuthクラス
  - 複数の責任が混在(WebAuthn処理、セッション管理、プラグイン機能)
  - テストや保守が困難

  現在(分離されたクラス)

  1. PasskeyAuth.php (230行)
  - プラグインの基本機能に集中
  - ルーティングとエラーハンドリング
  - サービスクラスへの委譲

  2. WebAuthnService.php (約300行)
  - WebAuthn の核となる処理
  - 登録・認証オプションの生成
  - 署名検証と認証情報管理
  - PublicKeyCredentialSourceRepository の実装

  3. SessionManager.php (約120行)
  - チャレンジの保存・取得・削除
  - ユーザーセッション管理
  - セッションの有効期限チェック

  利点

  1. 単一責任の原則
  - 各クラスが明確な責任を持つ
  - 変更の影響範囲が限定的

  2. 保守性の向上
  - 機能ごとに独立してテスト可能
  - バグの特定と修正が容易

  3. 再利用性
  - WebAuthnServiceは他のプラグインでも利用可能
  - SessionManagerも汎用的に使える

  4. 読みやすさ
  - 各ファイルが適切なサイズ
  - 機能が明確に分離されている

  このリファクタリングにより、コードの品質と保守性が大幅に改善されました!

メソッド探索

プラグイン開発時に製品の実装例を参照して実装することも多いのですが、自然言語でAIに指示するだけで人間が行うコード探索処理をAIが代わりに実行してくれます。例えば子カテゴリ・孫カテゴリを全て収集したい時、Claude Code + Serena(MCP)がファイルを探索してPTUtil::get_childrenメソッドを使えば良いことを示してくれました。

> categoryモデルはid, label, parent_idを持っています。あるカテゴリを選択したとき、その子・孫を再帰
  的にたどることはできますか?

⏺ PowerCMS Xのcategoryモデルの構造を確認して、親子関係を再帰的にたどる方法を調査します。

⏺ PTUtilクラスも階層構造を扱うメソッドを持っているようです。

⏺ serena - search_for_pattern (MCP)(substring_pattern: "get_children|children", relative_path:
                                   "src/powercmsx/lib/Prototype/class.PTUtil.php",
                                   restrict_search_to_code_files: true, context_lines_after: 5,
                                   context_lines_before: 2)
  ⎿ {                                                                                          
      "src/powercmsx/lib/Prototype/class.PTUtil.php": [
        "... 994:    }\n... 995:\n  > 996:    public static function remove_dir ( $dir, $childr
    … +76 lines (ctrl+r to expand)

⏺ PTUtil::get_children メソッドを詳しく見てみます。

⏺ Read(src/powercmsx/lib/Prototype/class.PTUtil.php)
  ⎿  Read 15 lines (ctrl+r to expand)

⏺ はい、PowerCMS
  Xではcategoryモデルの子・孫を再帰的にたどることができます。以下の方法があります:

  2. PTUtil::get_childrenメソッドを使用(推奨)

  $category = $app->db->model('category')->load($category_id);
  $children = [];
  PTUtil::get_children($category, 0, $children);
  // $depth = 0 は無制限、数値を指定すると階層の深さを制限できます

ローカライズ処理

PowerCMS Xでは画面に表示するメッセージを記述する際、$app->translate()メソッドや<mt:trans phrase="" />タグに英語でメッセージを書き、locale/ja.csvに英語メッセージの日本語訳を用意するのが基本ですが、AIを使っていればこれも自動で対応してくれます。(できていなかった場合も下記のように後から指示を出せば上手く処理してもらえます。)慣れない英語を考えたり、わざわざ翻訳ファイルを開く必要がなくなり、スムーズに実装を進めることができます。

> @plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl 
  メッセージが日本語で書かれているため、mt:transを利用して国際化対応してください

⏺ ログインテンプレートのメッセージを国際化対応します。

⏺ Read(plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl)
  ⎿  Read 203 lines (ctrl+r to expand)

⏺ 日本語のハードコードされたメッセージを確認しました。以下の箇所を国際化対応します:
  - 155行目: 'パスキー認証ライブラリが読み込まれていません'
  - 160行目: 'このブラウザはWebAuthnに対応していません'
  - 183行目: 'ログインに失敗しました'
  - 194行目: 'パスキーログインに失敗しました: '

⏺ Update(plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl)
  ⎿  Updated                                                                                   
     plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl with 2
      additions and 2 removals
       152    function startPasskeyLogin(returnUrl) {
       153        // WebAuthn対応チェック
       154        if (!window.PasskeyAuth) {
       155 -          alert('パスキー認証ライブラリが読み込まれていません');
       155 +          alert('');
       156            return;
       157        }
       158    
       159        if (!window.PasskeyAuth.checkSupport()) {
       160 -          alert('このブラウザはWebAuthnに対応していません');
       160 +          alert('');
       161            return;
       162        }
       163    

⏺ Update(plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl)
  ⎿  Updated                                                                                   
     plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/alt-tmpl/login.tmpl with 1
      addition and 1 removal
       180                window.location.href = response.url;
       181            } else {
       182                return response.text().then(text => {
       183 -                  throw new Error('ログインに失敗しました');
       183 +                  throw new Error('');
       184                });
       185            }
       186        })

⏺ 次に、これらの新しいフレーズを日本語の翻訳ファイルに追加します。

⏺ Read(plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/locale/ja.csv)
  ⎿  Read 29 lines (ctrl+r to expand)

⏺ Update(plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/locale/ja.csv)
  ⎿  Updated plugin_development/pcmsx-plugin-passkey-auth/plugins/PasskeyAuth/locale/ja.csv wit
      4 additions
       26    "Composer autoload file not found. Please set composer_autoload.","Composerのaut
            oloadファイルが見つかりません。composer_autoloadを設定してください。"
       27    "web-auth/webauthn-lib is not installed. Please run 
            composer.","web-auth/webauthn-lib がインストールされていません。composer 
            を実行してください。"
       28    "Failed to initialize WebAuthn 
            library.","WebAuthnライブラリの初期化に失敗しました。"
       29 +  "Passkey authentication library is not 
          + loaded","パスキー認証ライブラリが読み込まれていません"
       30 +  "This browser does not support 
          + WebAuthn","このブラウザはWebAuthnに対応していません"
       31 +  "Login failed","ログインに失敗しました"
       32 +  "Passkey login failed","パスキーログインに失敗しました"

⏺ ログインテンプレートの国際化対応が完了しました。

例外処理の実装

人間は意識を強く持っていないと正しく動く前提でプログラムを書き、そこで満足してしまうところがあります。AIを活用するとメソッドの存在チェックや変数の内容チェックをきちんと書いてくれるので、より質の高いプラグインへとブラッシュアップすることができます。

/**
 * Plugin activation handler
 *
 * Validates plugin requirements before activation.
 *
 * @param Prototype $app      The application instance
 * @param object    $plugin   The plugin object
 * @param string    $version  The plugin version
 * @param array     &$errors  Reference to errors array
 * @return bool               True if activation successful, false otherwise
 */
public function activate( $app, $plugin, $version, &$errors ): bool {
    if ( ! $app->passkeyauth_host ) {
        $errors[] = $app->translate( 'The environment variable passkeyauth_host is not set.' );
        return false;
    }

    // WebAuthn requires HTTPS (except for localhost/development)
    if ( ! $app->is_secure ) {
        // Allow HTTP for localhost and 127.0.0.1
        $host = strtolower( $app->passkeyauth_host );
        $is_localhost = ( $host === 'localhost' ||
                            $host === '127.0.0.1' ||
                            strpos( $host, 'localhost:' ) === 0 ||
                            strpos( $host, '127.0.0.1:' ) === 0 );

        if ( ! $is_localhost ) {
            $errors[] = $app->translate( 'PasskeyAuth requires HTTPS. Please enable SSL/TLS for your site.' );
            return false;
        }
    }

    // Composer autoload check
    if ( ! $app->composer_autoload || ! file_exists( $app->composer_autoload ) ) {
        $errors[] = $app->translate( 'Composer autoload file not found. Please set composer_autoload.' );
        return false;
    }

    // Try loading WebAuthn library classes
    try {
        require_once $app->composer_autoload;
        if ( ! class_exists( '\\Webauthn\\PublicKeyCredential' ) ) {
            $errors[] = $app->translate( 'web-auth/webauthn-lib is not installed. Please run composer.' );
            return false;
        }
    } catch ( \Exception $e ) {
        $errors[] = $app->translate( 'Failed to initialize WebAuthn library.' );
        return false;
    }

    return true;
}

まとめ

AIの活用によりPowerCMS Xのプラグイン開発作業の効率化に留まらず、品質の向上にもつながっていると考えます。最近はプラグイン作成に不慣れな方でもAIに指示することでプラグインが作成できるようにならないかな?と考えています。いつかどこかでPowerCMS Xの開発に適したCLAUDE.md等を紹介したいと考えていますのでご期待ください。