Zenn
🗂

【WordPress】CursorのAgentモードでカスタムブロックを作る

2025/03/24に公開

最近AIコーディングにはまっているので、今の私はこうやって作ってるというメモがてらひとつブロックを作ってみます。なお、これは個人的に試験的にやってるものです。

作ってみますとは言っても、案件で作ったのをもう一回やってるだけなので完成系は見えてます。

今回のリポジトリはこちら。

https://github.com/chiilog/term-meta-text-block

環境

  • Cursor 0.47.8
    • Agentモードでclaude 3.7 sonnet使用(Thinkingにしたりしなかったり)
  • ChatGPT(デスクトップアプリ)

Cursorはモデル色々ありますが基本的にclaude 3.7 sonnetしか使ってないです。MAXが増えてますがこっちはまだ手を出してません。
ChatGPTは「この関数は何?」って聞いたりするときに使ってます。Askモードでいいじゃんと思いはするものの、完全に好みの問題でデスクトップアプリの方が見やすいんです………………。
書かれたコードの部分的な解説は基本Askモードでやりますが、時々ChatGPTに聞いたりもしています。

準備

CursorのUser Rulesを整備

私が今設定しているのはこれです。

### 重要な注意事項:
- 全ての応答は日本語で行うこと(例外なく)。
- 特に指示がない場合は、カジュアルな口調で対応すること。
- 不明点がある場合は、作業開始前に必ず確認を取ること。
- 重要な判断が必要な場合は、その都度報告し、承認を得ること。
- 予期せぬ問題が発生した場合は、即座に報告し、対応策を提案すること。
- 同じエラーの解消に3回失敗した場合は、処理を中断し、どのエラーが解消できなかったのか詳細な説明を行うこと。
- 環境変数やAPIキー、認証情報などの秘匿情報はGitにコミットしないこと。安全な管理方法を提案できる場合は提案すること。
- 明示的な指示がない限り、コードや設定の変更を行わないこと。ただし、明らかに誤りがある場合は報告のうえ修正を提案すること。

### 振る舞い:
- TypeScript、Next.js(App Router)、React、Tailwind CSS、WordPressのエキスパートとして、最善の設計・実装方法を提案し、質問には的確な解答を行うこと。

### コメント:
- 全てのコードの先頭に適切なコメントを追加すること。  
  - JavaScript: JSDoc  
  - PHP: PHPDoc  
- コメント内では、スクリプトの概要、主な仕様、制限事項を記載すること。
- 全てのファイル、クラス、メソッド、プロパティに日本語のコメントを付け、適切なタグ(例: `@param`、`@return`)とデータ型を記載すること。

### コーディング:
- 効率よりも可読性を重視すること。
- プログラムの詳細は省略せず、冗長になっても可読性を優先すること。ただし、適切に抽象化できる場合は適用すること。
- 完了後、コード全体に矛盾がないか仕様と完全に一致しているかチェックすること。
- コードを提供する際は、私のPrettier設定(インデント、改行、クオートスタイルなど)を厳守すること。

これを設定していないと、Cursorは自分の好き勝手にコードを書きます。粗悪なコードではないけど、私が追いづらいのでコメントはちょっと詳しめに書いてもらうようにしています。いざという時直すのは私だから。

Project Rulesを設定

https://cursor.directory/wordpress-php-cursor-rules

WordPressで開発する時はこれをまるっと使わせてもらってます。
日本語にするとこんな感じらしい。

## 💡 You are an expert in WordPress, PHP, and related web development technologies.
**あなたは、WordPress、PHP、および関連するWeb開発技術の専門家です。**

---

## 🔑 Key Principles(基本原則)

- Write concise, technical responses with accurate PHP examples.  
  **簡潔かつ技術的な回答を心がけ、正確なPHPの例を示すこと。**

- Follow WordPress coding standards and best practices.  
  **WordPressのコーディング標準およびベストプラクティスに従うこと。**

- Use object-oriented programming when appropriate, focusing on modularity.  
  **適切な場合はOOP(オブジェクト指向)を使用し、モジュール化を重視すること。**

- Prefer iteration and modularization over duplication.  
  **コードの重複よりも、繰り返しやモジュール化を優先すること。**

- Use descriptive function, variable, and file names.  
  **関数名、変数名、ファイル名はわかりやすく記述すること。**

- Use lowercase with hyphens for directories (e.g., wp-content/themes/my-theme).  
  **ディレクトリ名は小文字とハイフンで命名する(例: `wp-content/themes/my-theme`)。**

- Favor hooks (actions and filters) for extending functionality.  
  **機能の拡張には、フック(アクション・フィルター)を使用すること。**

---

## 🐘 PHP / WordPress

- Use PHP 7.4+ features when appropriate (e.g., typed properties, arrow functions).  
  **必要に応じて、PHP 7.4以降の機能(型付きプロパティ、アロー関数など)を使用すること。**

- Follow WordPress PHP Coding Standards.  
  **WordPressのPHPコーディング規約に従うこと。**

- Use strict typing when possible: `declare(strict_types=1);`  
  **可能であれば厳格な型指定を使う:`declare(strict_types=1);`**

- Utilize WordPress core functions and APIs when available.  
  **可能な場合は、WordPressのコア関数やAPIを活用すること。**

- File structure: Follow WordPress theme and plugin directory structures and naming conventions.  
  **ファイル構造は、WordPressのテーマ・プラグインのディレクトリ構造や命名規則に従うこと。**

- Implement proper error handling and logging:  
  **適切なエラーハンドリングとログ出力を実装すること:**
  - Use WordPress debug logging features.  
    **WordPressのデバッグログ機能を使用する。**
  - Create custom error handlers when necessary.  
    **必要に応じてカスタムエラーハンドラを作成する。**
  - Use try-catch blocks for expected exceptions.  
    **想定される例外にはtry-catchブロックを使用する。**

- Use WordPress's built-in functions for data validation and sanitization.  
  **データのバリデーションやサニタイズにはWordPressの組み込み関数を使用する。**

- Implement proper nonce verification for form submissions.  
  **フォーム送信には適切なNonce(ワンタイムトークン)検証を実装する。**

- Utilize WordPress's database abstraction layer (`$wpdb`) for database interactions.  
  **データベース操作にはWordPressの抽象化レイヤー(`$wpdb`)を利用する。**

- Use `prepare()` statements for secure database queries.  
  **安全なクエリのために `prepare()` を使用すること。**

- Implement proper database schema changes using `dbDelta()` function.  
  **`dbDelta()` 関数を使って、適切にデータベーススキーマを変更すること。**

---

## 📦 Dependencies(依存関係)

- WordPress (latest stable version)  
  **WordPress(最新版の安定バージョン)**

- Composer for dependency management (when building advanced plugins or themes)  
  **高度なプラグインやテーマを構築する場合は、Composerで依存管理を行うこと。**

---

## 🏗 WordPress Best Practices(WordPressのベストプラクティス)

- Use WordPress hooks (actions and filters) instead of modifying core files.  
  **コアファイルの改変ではなく、フック(アクション・フィルター)を使用する。**

- Implement proper theme functions using `functions.php`.  
  **テーマの機能は `functions.php` に適切に実装する。**

- Use WordPress's built-in user roles and capabilities system.  
  **WordPressのユーザーロールと権限システムを使用する。**

- Utilize WordPress's transients API for caching.  
  **キャッシュにはTransients APIを活用する。**

- Implement background processing for long-running tasks using `wp_cron()`.  
  **長時間の処理には `wp_cron()` を使ってバックグラウンドで実行する。**

- Use WordPress's built-in testing tools (`WP_UnitTestCase`) for unit tests.  
  **単体テストには `WP_UnitTestCase` を使用する。**

- Implement proper internationalization and localization using WordPress i18n functions.  
  **i18n関数を使って、適切な国際化・ローカライズを行う。**

- Implement proper security measures (nonces, data escaping, input sanitization).  
  **Nonce、データエスケープ、入力サニタイズなどの適切なセキュリティ対策を実施する。**

- Use `wp_enqueue_script()` and `wp_enqueue_style()` for proper asset management.  
  **アセット管理には `wp_enqueue_script()` と `wp_enqueue_style()` を使用する。**

- Implement custom post types and taxonomies when appropriate.  
  **必要に応じて、カスタム投稿タイプやカスタム分類(タクソノミー)を実装する。**

- Use WordPress's built-in options API for storing configuration data.  
  **設定データの保存にはOptions APIを使用する。**

- Implement proper pagination using functions like `paginate_links()`.  
  **`paginate_links()` などを用いて適切なページネーションを実装する。**

---

## 📘 Key Conventions(主要な規約)

1. Follow WordPress's plugin API for extending functionality.  
   **機能拡張にはWordPressのプラグインAPIを使用すること。**

2. Use WordPress's template hierarchy for theme development.  
   **テーマ開発にはテンプレート階層を活用すること。**

3. Implement proper data sanitization and validation using WordPress functions.  
   **データのサニタイズとバリデーションはWordPress関数を用いること。**

4. Use WordPress's template tags and conditional tags in themes.  
   **テンプレートタグや条件タグをテーマ内で活用すること。**

5. Implement proper database queries using `$wpdb` or `WP_Query`.  
   **`$wpdb` や `WP_Query` を使って適切なデータベースクエリを実装すること。**

6. Use WordPress's authentication and authorization functions.  
   **認証・認可にはWordPressの関数を使用すること。**

7. Implement proper AJAX handling using `admin-ajax.php` or REST API.  
   **AJAX処理は `admin-ajax.php` または REST API を利用すること。**

8. Use WordPress's hook system for modular and extensible code.  
   **フックシステムを使って、モジュール化・拡張性のあるコードを書くこと。**

9. Implement proper database operations using WordPress transactional functions.  
   **WordPressのトランザクション関数で正しいデータベース操作を行うこと。**

10. Use WordPress's `WP_Cron` API for scheduling tasks.  
    **タスクスケジューリングには `WP_Cron` API を使うこと。**

カスタムブロックの開発だけど、サーバーサイドレンダリング使う時はPHPで書くことになるので念の為。

カスタムドキュメントを追加

WP Block API というドキュメント名で、以下のURLを追加。(確かここだったはず。もう一個上の階層かも?)

https://developer.wordpress.org/block-editor/reference-guides/

たまにblock.jsonの apiVersion: 2 とかで書いてくるからリファレンス指定は必要そう。
これで @WP Block API xxxを書いて って頼むと、指定しなかった時より精度高めに書いてくれる。(気がする)

環境構築

これは自分でやった方がいい。(執筆時点)だって、自分で使ってるいつものセットがあるから。
今回はwp-envで環境作ると長くなるし本質ではないので @wordpress/create-block を使って作ります。

PrettierとかESLintとかtsconfig.jsとかその他諸々の設定が抜けてるので、抜けてるものはいつも使ってるやつをもってきます。

TypeScriptの型については、ひとまず @types/wordpress__blocks@types/wordpress__block-editor を使って、適宜追加するといいと思います。

https://wp-kyoto.net/create-wordpress-custom-block-with-typescript/

今回はタクソノミーだったりタームだったりの型も使うので、wp-typesも入れておきます。(私も構築した時に教えてもらって知った)
こういうのは構築しながらあとで足すものだと思いますが今回は一度やってるから先入れてます。

https://www.npmjs.com/package/wp-types

TODO.mdを準備

これは直近で見たAI駆動のライブコーディングや、人から聞いてなるほど!と思ったので今回から準備することにしました。

  • 何を作るのか
  • それにあたって、どういう機能が必要なのか

をmdファイルにまとめておきます。

設定されたタームメタを取得して、値を表示するWordPressのカスタムブロックを作ります。  
なお、タームメタを登録する仕組みは別のプラグインで行うので、今回の実装範囲には含みません。

## 基本事項

JavaScriptはTypeScript(.ts, .tsx)を使用してください。
型の情報については、以下のパッケージを追加しているので、適宜使用してください。

-   @types/wordpress\_\_block-editor
-   @types/wordpress\_\_blocks
-   wp-types

## 必要な選択項目

### block.json関連

#### attributes

必要なattributesは

-   taxonomy
-   termId
-   termMetaKey

の3点です。他に必要なものが出た場合は、適宜提案してください。

#### supports

カスタムオプションは

-   テキストカラー
-   背景色
-   フォントサイズ
-   フォントファミリー
-   フォントの外観
-   行間

が有効になるようにしてください。

### 設定パネル

-   [ ] タクソノミー
-   [ ] セレクトボックスで一覧から選択できるようにする
-   [ ] 指定されたタクソノミーにあるターム
    -   [ ] セレクトボックスで一覧から選択できるようにする
    -   [ ] タクソノミーが指定されていなければ表示されない
    -   [ ] タクソノミーが変更されたら初期化する
-   [ ] タームメタ
    -   [ ] テキストボックスで自由入力

## 表示に関して

### 管理画面

管理画面上ではタームメタの値の表示は行わず、

-   選択されたタクソノミー
-   ターム
-   タームメタ

を段落ブロック(pタグ)で表示してください。  
block.jsonで指定したsupportsを使用したカスタムオプションが適切に設定されるようにしてください。

### フロント

段落ブロック(pタグ)でタームメタの値が出力されるようにしてください。  
block.jsonで指定したsupportsを使用したカスタムオプションが適切に設定されるようにしてください。

上から順番に埋めていくわけではなく、どういう表示にしたいのか、何が必要なのか、この辺は自分で仕様を書いて「あ、これが不足してるかも。足しておこう」とバラバラに書き足しています。
例えば、

block.jsonで指定したsupportsを使用したカスタムオプションが適切に設定されるようにしてください。

と書いたから、じゃあblock.jsonの項目の指定もいるよね。とか。
とりあえず思いつく限りのことを細かく細かく書いていきます。書かなくていい情報はない。
むしろ、書かないとAIは懇切丁寧にいらない機能まで実装してくれます。

「タームメタ表示を表示するカスタムブロック作るからblock.jsonだけ作って」ってお願いしたら、block.jsonだけじゃなくedit.tsxやらsave.tsxやら全部やってました。適切な情報がない状態で作ってるのでしっちゃかめっちゃかいらない記述が山盛り。

ここまで書けたら、一旦ChatGPTに添削をお願いします。AIが理解できるかどうかはAIに判断してもらいます。

  • 指示としては十分に伝わる
  • より明確に、誤解が出にくいように調整してみたよ!

ということで、調整してもらったのがこちら。

## 概要

設定されたタームメタを取得し、その値を表示する WordPress のカスタムブロックを作成します。  
※タームメタの登録機能は別プラグインが担うため、本実装には含まれません。

## 1. 技術スタック・前提

- JavaScriptは TypeScript(.ts / .tsx)を使用
- 以下の型定義パッケージがインストール済みです。必要に応じて使用してください。

  - @types/wordpress__block-editor
  - @types/wordpress__blocks
  - wp-types

## 2. block.json の設定

### 2.1 attributes

以下の3つの attributes を必須とします:

- `taxonomy`(string)
- `termId`(number)
- `termMetaKey`(string)

※他に必要な attributes がある場合は、適宜提案してください。

### 2.2 supports

以下のカスタムオプションを有効にしてください(例: `"supports": { ... }` に含める):

- テキストカラー
- 背景色
- フォントサイズ
- フォントファミリー
- フォントの外観(appearance)
- 行間(line height)

## 3. ブロックの設定パネル(InspectorControls)

以下のUIを用意してください:

- **タクソノミー**
  - セレクトボックスで選択
- **ターム**
  - 指定されたタクソノミーに紐づくタームをセレクトボックスで選択
  - タクソノミーが未選択の場合は非表示
  - タクソノミーが変更された場合は選択を初期化
- **タームメタキー**
  - テキストボックスで自由入力

## 4. 表示仕様

### 4.1 管理画面(エディター)

- タームメタの**値そのものは表示しない**
- 以下の情報を段落ブロック(`<p>`)で表示:
  - 選択されたタクソノミー
  - ターム名(またはID)
  - メタキー名
- `block.json`で指定した supports を使用して、スタイルのカスタマイズが反映されるようにしてください

### 4.2 フロントエンド

- `<p>` タグで、指定されたタームIDとメタキーに対応する **タームメタの値** を表示してください
- supports のスタイルが適用されるようにしてください

これにちょっと漏れてたなと思った仕様を追加しました。
これでコーディングしてもらうことにします。

AIにコーディングしてもらう

はてさて一発でどれくらいできるでしょう。

一発目にたくさん書いてもらうのでThinkingでお願いしました。
今回はコミットログに初回コード残しておこうかなと思うので何も手をつけずに一旦pushしました。

※ Lintでエラーが出るので、一旦lint-stagedからJSとPHPを抜いてます。

https://github.com/chiilog/term-meta-text-block/pull/2

ちなみに、コードレビューにgemini-code-assistを入れてるのでレビューもAIがやります。

https://github.com/marketplace/gemini-code-assist

本来はGeminiに通す前に自分でレビューするのでこの状態では通しませんが。

レビューしてみる

index.tsx

index.tsxですでにツッコミポイントがありますね。

https://github.com/chiilog/term-meta-text-block/blob/9e5d2ecd48724f38da1754b14bfbcf0ff637594a/src/term-meta-text-block/index.tsx#L22-L23

なんでわざわざrequireにしたんだ?
と思ってimportに戻したら、なるほどTypeScriptのエラー。
これはいつも @ts-ignoreで無視しているので、これは仕様書に書いておくべきことでした。(追記)

また、謎に

import type { BlockConfiguration } from '@wordpress/blocks';

というのがインポートされている(使われてない)ので削除します。

https://github.com/chiilog/term-meta-text-block/blob/9e5d2ecd48724f38da1754b14bfbcf0ff637594a/src/term-meta-text-block/index.tsx#L7

edit.tsx

まず、

https://github.com/chiilog/term-meta-text-block/blob/9e5d2ecd48724f38da1754b14bfbcf0ff637594a/src/term-meta-text-block/edit.tsx#L50-L56

BlockEditProps が使われてない。TODOに型定義のパッケージを書いてるけど、ここはやっぱりまだ具体的に指示しないとだめかも。具体的に指示したら書けるのか?って思ったのでちょっとやってみてもらいました。

attributes と setAttributesの型を定義していますが、型提供があります。
@types/wordpress__blocks からBlockEditPropsを使ってください

で書き直してもらいました。props と書くか { attributes, setAttributes } と書くかは好みの問題?と思ったのでそこはスルー。
指示したらちゃんと直してくれたので、やっぱり具体的に何を使ってね、と書いた方がよさそう。
TODOに最初から書いてたところで読んでくれるかは未検証。今度見てみよう。

そして、useSelect 関連の型エラーがたくさんあるので、これもTODOに ts-ignore してねと書く&作業してもらいました。こちらもTODOに指示を残します。

https://github.com/chiilog/term-meta-text-block/blob/9e5d2ecd48724f38da1754b14bfbcf0ff637594a/src/term-meta-text-block/edit.tsx#L85-L90

もっと複雑な仕様であれば(タクソノミー変えたら常に何かしらを選択させるようにしたいとか?)useEffectの方が適しているかもしれないけど、今回はただtermId: '0'にしてくれたらいいので、タクソノミーのonChangeで処理することにします。これも指示が厳密ではなかったなという反省。(初期化、だったら十分useEffectの使用が想定されそう)

render.php

https://github.com/chiilog/term-meta-text-block/blob/9e5d2ecd48724f38da1754b14bfbcf0ff637594a/src/term-meta-text-block/render.php

よ、読みづらい………。
早期リターンを使ってシンプルに書いてもらうことにします。また、値は文字列で固定なのでその辺もTODOに追記しておきます。(空の場合の処理も含めて)

そして、そろそろわざと抜いていたJSとPHPのLintを復活させておきます。
lint-phpを行うと、ざっくり書くと以下のエラーが出ました。

- ファイルコメントの短い説明が不足
- 最初のdocコメントに@packageタグが不足
- WordPressのグローバル変数オーバーライド
- インデントがスペースではなくタブを使用する必要がある
- $wrapper_attributesのエスケープが必要

エスケープは無視させるので、これもTODOファイルに指示を追記しておきます。
このエラー文をそのままチャットに貼り付け、解決してもらいます。

この辺りのLintチェックもTODOに含めておいたほうがいいですね。

コードレビューも済んだので一旦ビルドして、動作するか検証します。
今回はWP Term Imagesを使います。
本来は画像を出したいところですが、今回は画像のidが表示できていればOKということになります。

6.1というカテゴリーに画像追加、ヨシ!

term-meta-text-block.phpの中をregister_block_typeに書き換えたりしました。

ブロック、ある!

タクソノミー選択、OK!

ターム選択、OK!

メタキー、入力ヨシ!

出力、ヨシ!!!!!!!!!!!!!!!!

ということで、無事ブロックが完成しました。

再度Geminiのレビューを依頼すると、String( termId )はなんでストリング型にしてるん?と突っ込まれたので、これは確かにコメントが必要だねと思ったので追記しました。
レビューの様子はPRに残っています。

https://github.com/chiilog/term-meta-text-block/pull/2

総括

誰相手だろうと、自分以外の人が書くための仕様書の重要性・厳密性がよーーーくわかりました。
今回、「ここもっと厳密に書いてたらもっと理想のコードに近かったかも」というポイントがいくつかみられたと思います。こうやってどんどん勘所を養っていきたいですね。

また、構築する上で自分が知っている前提知識も事前の仕様書に盛り込んでおいた方がよさそうです。
例えば、今回の例だとuseSelectts-ignore指定とか。Editに使う型を具体的に書いておくとか。
型定義まわりはProject Rulesに別途つけてもいいかもと思いました。カスタムブロックに関する仕様は今回単発の仕様ではないですからね。こういうのを積み重ねたらもっと時短できそうです。

また、書かせるための知識は間違いなく必要です。
例えば、私は今回事前にTypeScriptのいくつかの型について事前に知識を持っていたのでTODOに盛り込めていますが、この辺りの知識がないともっとコードは読めないものが上がっていた可能性があります。

自分は今回ほとんどコードを書いてない(自分で簡単に手直しできるところをちょっと書いただけ)ですが、これだけのコードが初回で出してきたら結構十分ではないかなあと思いました。誰がやろうが初回で完璧なものなんてできませんし。まだまだリファクタリングの余地はあるので、今後同じリポジトリで何かするとき用に残しておきます。

今後のお仕事の進め方について思うところ
  • 地味にTODOを細かく埋めていく作業が面白く感じた
  • 品質という面でテストは結構重要なのでは?という所感
  • 仕様を難しく考えてこんがらがってしまいがちなので、今回みたいにTODOにちょっとずつ切り出していくとなんだか整理できていいかも💡

というのがあって、とりあえずテストを書けるようになろう!!と、目下テスト勉強中!!!!今度は同じリポジトリでリファクタリング+テスト追加をやってみようかなーなんて思ってます。

Discussion

ログインするとコメントできます