Agent skill

Basercms5 Plugin Development

by baserproject in baserproject/basercms

baserCMS 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bcplugin で bake」「管理画面を追加」「Table/Entity の書き方」「initialize でアソシエーション宣言」「validationDefault」「fetchTable と TableRegistry の使い分け」「BcAdminForm /…

MITAuto-check passedAgent Workflows

Install Basercms5 Plugin Development

skills CLI
$ npx skills add baserproject/basercms --skill basercms5-plugin-development -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install baserproject/basercms basercms5-plugin-development --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/baserproject/basercms.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/basercms5-plugin-development .claude/skills/basercms5-plugin-development && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
basercms5-plugin-development
GitHub stars
190
Token cost
~4.9k tokens
SKILL.md length
981 words
Files
1
Skills in repo
15
Repo updated
First seen
Licence
MIT

At a glance

baserCMS 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bcplugin で bake」「管理画面を追加」「Table/Entity の書き方」「initialize でアソシエーション宣言」「validationDefault」「fetchTable と TableRegistry の使い分け」「BcAdminForm /…

  • Works in 9 steps: 新規開発の始め方 → ORM / Table / Entity → コントローラ → …
  • Tasks that involve Hooks and plugins
  • SKILL.md covers 1. 新規開発の始め方, 2. ORM / Table / Entity, 3. コントローラ and 4. 管理画面フォーム・一覧・検索, plus 5 more sections
  • Calls composer, npm and npx

What it does

Basercms5 Plugin Development is an agent skill from baserproject/basercms. baserCMS 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bcplugin で bake」「管理画面を追加」「Table/Entity の書き方」「initialize でアソシエーション宣言」「validationDefault」「fetchTable と TableRegistry の使い分け」「BcAdminForm / control() の書き方」「検索フォーム element」「control の id/name 生成規則と JS 連携」「イベントリスナー(BcModelEventListener 等)」「adminNavigation の登録」「setting.php / config.php」「BcShortCode 登録」「paginate($query)」「redirect は return」「ids で belongsToMany 保存」「BcKeyValue」「afterSave の再入ガード」「$.bcUtil.adminBaseUrl」「Time::format の ICU パターン」「routes.php の…

Its SKILL.md is about 4.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Agent Workflows, covering Hooks and plugins. It works with PHP. The repository describes itself as: baserCMS : Based Website Development Project. The licence is MIT.

When your agent uses it

  • Tasks that involve Hooks and plugins

Example prompts

  • “Use the basercms5-plugin-development skill to basercm 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bcplugin で bake」「管理画面を追加」「Table/Entity…”
  • “/basercms5-plugin-development”

Workflow steps

9 steps, taken from the step headings in SKILL.md.

  1. 新規開発の始め方
  2. ORM / Table / Entity
  3. コントローラ
  4. 管理画面フォーム・一覧・検索
  5. イベント
  6. Vue / jQuery 連携
  7. 日付・数値・文字列
  8. ルーティング・ショートコード・管理画面CSS
  9. テスト

What it can do on your machine

Read from SKILL.md and the folder at commit 748b4f6. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • composer
    • npm
    • npx

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npm and npx, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Basercms5 Plugin Development loads about 4.9k tokens when it runs. Until then it costs about 182 tokens; SKILL.md has 981 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~182
When it runs · the whole SKILL.md, loaded when a task matches
~4.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from baserproject/basercms at commit 748b4f6, republished under its MIT licence (© baserproject). 981 words, ~4,863 tokens.

Download SKILL.mdSave it as .claude/skills/basercms5-plugin-development/SKILL.md (or your agent's skills folder).
name
basercms5-plugin-development
description
baserCMS 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bc_plugin で bake」「管理画面を追加」「Table/Entity の書き方」「initialize でアソシエーション宣言」「validationDefault」「fetchTable と TableRegistry の使い分け」「BcAdminForm / control() の書き方」「検索フォーム element」「control の id/name 生成規則と JS 連携」「イベントリスナー(BcModelEventListener 等)」「adminNavigation の登録」「setting.php / config.php」「BcShortCode 登録」「paginate($query)」「redirect は return」「_ids で belongsToMany 保存」「BcKeyValue」「afterSave の再入ガード」「$.bcUtil.adminBaseUrl」「Time::format の ICU パターン」「routes.php の RouteBuilder」等で参照する。環境・ディレクトリ・命名・コアハック禁止などの共通ルールは basercms5-development、テーマ開発は basercms5-theme-development、4系→5系の移行作業は basercms-plugin-4-to-5-upgrade、テスト実行・テスト基盤導入は basercms-unittest を参照。
license
MIT

baserCMS 5系 プラグイン開発の正本パターン集

baserCMS 5系(CakePHP 5ベース)でプラグインを新規開発・改修するときのコーディングパターンを機能別にまとめたもの。環境・ディレクトリ構成・命名規則・コアハック禁止(vendor/baserproject・vendor/cakephp は読み取り専用)などの共通ルールは basercms5-development、テーマ(templates 中心)の開発は basercms5-theme-development、4系からの移行作業は basercms-plugin-4-to-5-upgrade、テストの実行・基盤導入は basercms-unittest、bc-custom-content(カスタムコンテンツ)を使った開発は basercms5-custom-content-development を参照する。

1. 新規開発の始め方

  • 雛形は bake で生成する: bin/cake bake bc_plugin {PluginName} でプラグイン雛形、bin/cake bake bc_all {ModelName} --plugin {PluginName} で Table/Entity/Controller/テンプレート一式を生成できる。既存プラグインへのテスト基盤の後付け手順は basercms-unittest を参照。
  • 標準ディレクトリ構成:
    • src/{PluginName}Plugin.php — プラグインクラス
    • src/Controller/(フロント)・src/Controller/Admin/(管理画面)
    • src/Model/Table/・src/Model/Entity/
    • src/View/Helper/・src/Event/(イベントリスナー。置くだけで自動 attach される)
    • config/(config.php・setting.php・routes.php・Migrations/)
    • templates/(フロント)・templates/Admin/(管理画面。検索フォームは templates/Admin/element/search/)
    • webroot/(js/css/img。Vue ソースは webroot/js/src)
  • プラグインクラスは class SamplePlugin extends \BaserCore\BcPlugin と書く。install(array $options = []): bool は return parent::install($options); を基本とし、bootstrap() は**動的な設定(実行時に決まる値の Configure 追加、ログ設定の登録など)**にのみ使う。
  • setting.php / config.php は「静的な return 配列」にする(実行型コードを書かない)。動的に決まる部分は bootstrap() に分離する。
    php
    // config/setting.php
    return [
        'BcApp' => [...],
        'BcShortCode' => ['Sample' => ['Sample.showList']],
    ];
  • adminNavigation(管理メニュー)は setting.php に登録する。'plugin'・'controller' はいずれも CamelCase で書く('controller' => 'SampleArticles')。snake_case で書くと全管理画面が404になる(メニュー描画時のURL解決に失敗するため対象画面以外も巻き込む)。
  • 管理画面URLの {controller} セグメントは実際にはプラグイン名をダッシュ区切り(kebab-case)にしたもの(例: BlogTagGroup → /baser/admin/blog-tag-group/{controller}/...)。他プラグインのURLパターンから類推せず、bin/cake routes | grep <PluginName>(大文字小文字を落として検索)で実際のルートを確認してから使う。
  • BcBlog(bc-blog)の管理画面「記事編集」URLは edit/{blogContentId}/{id} の2引数が必須(Admin\BlogPostsController::beforeFilter() が $this->request->getParam('pass.0') をブログコンテンツIDとして要求し、無いと BcException: コンテンツデータが見つかりません。 になる)。記事IDだけの edit/{id} ではアクセスできない。他プラグインが特定ブログ記事の編集画面へリンクを張る場合(例: 追加フィールドプラグインの編集導線)は、edit($blogContentId, $id) のシグネチャ通り両方を渡す。
  • 有効化は管理画面のプラグイン管理から行うか、plugins テーブルへ直接 INSERT(status=1)する。DBで有効化されたプラグインは自動で読み込まれるため、composer への登録や composer dump-autoload は不要。
  • サードパーティ製ライブラリ(例 phpoffice/phpspreadsheet)は composer require で導入する。雛形アセット(Excel テンプレート等)は templates/Admin/Excel/... に置き、\Cake\Core\Plugin::templatePath('Sample') . 'Admin' . DS . 'Excel' . DS . ... で参照する(templatePath() は末尾スラッシュ付き)。

2. ORM / Table / Entity

責務分離: バリデーション・ビジネスロジックは Table(src/Model/Table)、行データの振る舞いは Entity(src/Model/Entity)に書く。DB変更は Migration (phinx) を使う。

  • アソシエーションは initialize() で宣言し、エイリアスは複数形にする。className はプラグイン接頭辞付き・複数形で書く。エイリアスは contain()・条件キー・エンティティのプロパティ名すべてに波及する。
    php
    public function initialize(array $config): void
    {
        parent::initialize($config);
        $this->setTable('sample_articles');
        $this->addBehavior('BaserCore.BcUpload', [...]);
        $this->belongsTo('Users', ['className' => 'BaserCore.Users', 'foreignKey' => 'user_id']);
        $this->hasMany('SampleComments', ['className' => 'Sample.SampleComments', 'foreignKey' => 'article_id', 'dependent' => true]);
        $this->belongsToMany('SampleTags', [
            'className' => 'Sample.SampleTags',
            'joinTable' => 'sample_articles_sample_tags',
            'foreignKey' => 'article_id',
            'targetForeignKey' => 'tag_id',
        ]);
    }
    dependent は hasMany/hasOne のみ有効。
  • バリデーションは validationDefault() に書く。カスタムルールはシグネチャ ($value, $context) のメソッドを add() で登録する。
    php
    public function validationDefault(\Cake\Validation\Validator $validator): \Cake\Validation\Validator
    {
        $validator->notEmptyString('title', 'タイトルを入力してください');
        $validator->add('title', 'custom', ['rule' => [$this, 'customMethod'], 'message' => '...']);
        return $validator;
    }
  • 検索はクエリビルダで書く: $this->find()->where([...])->contain([...])->orderBy([...])->first()/all()。list は find('list', keyField: 'id', valueField: 'name')。
    • 配列値に IN は自動付与されない。複数値は必ず 'field IN' => (array)$values と明示する(書かないと Cannot convert value Array 例外)。空配列の IN () は例外になるので、空になりうる場合は先頭でガードして早期 return する。
    • null 一致は IS 演算子: ['field IS' => null]/否定は ['field IS NOT' => null]。
    • 関連テーブルの列で絞り込むときは contain() ではなく ->innerJoinWith('Assocs') か matching() を使う(contain は WHERE に使えない)。条件キーは複数形エイリアス(SampleComments.user_id)。常時 join すると関連なし行が除外される点に注意。
  • 保存はエンティティ経由: $entity = $table->newEntity($data);(新規)または $entity = $table->patchEntity($table->get($id), $data);(更新)→ $table->save($entity);。
  • belongsToMany の保存は _ids で書く: patchEntity($e, $data + ['sample_tags' => ['_ids' => [$id, ...]]], ['associated' => ['SampleTags']])。表示側は contain(['SampleTags'])。
  • name/value の KVS テーブルは initialize() に $this->addBehavior('BaserCore.BcKeyValue') を足すと saveKeyValue([$key => $value]) が使える。読み出しは find()->where(['name' => $key])->first()->value。
  • 集計は func()->sum() + ->first()->alias ?? 0 で書く:
    php
    $total = $this->find()
        ->where(['user_id' => $userId])
        ->select(['total' => $this->find()->func()->sum('amount')])
        ->first()->total ?? 0;
    • datetime カラムの MIN/MAX に集計関数を使わない。func()->min()/max() は結果が数値型にキャストされて壊れるため、->orderBy(['col' => 'ASC'])->first() で最古/最新行を取り、そのエンティティの日時プロパティを使う。
  • afterSave で他テーブルを save する(相互に save し合う)場合は再入ガードフラグを置く。save() に callbacks オプションは存在しない(渡しても黙って無視され afterSave は常に発火する)ため、抑止はフラグで行う。
    php
    private bool $inAfterSave = false;
    public function afterSave(EventInterface $e, EntityInterface $entity, \ArrayObject $o): void
    {
        if ($this->inAfterSave) return;
        $this->inAfterSave = true;
        try { /* 他テーブルの save 等 */ } finally { $this->inAfterSave = false; }
    }
  • 削除は get($id) → delete($entity) で書く(関連の dependent => true が cascade する)。deleteAll() は ORM の cascade を通さず子が孤児として残るので、子を連れて消す削除には使わない。
  • fetchTable() は Controller / Command 専用。Table・Lib・Helper・イベントリスナー内では \Cake\ORM\TableRegistry::getTableLocator()->get('Sample.SampleArticles') を使う(fetchTable を呼ぶと BadMethodCallException)。エイリアスは常に複数形。
  • **getControlSource()(フォーム選択肢の供給元)**は Table に定義し、テンプレートからは $this->BcAdminForm->getControlSource('Sample.SampleArticles.user_id') のように Plugin.複数形モデル.field で呼ぶ(単数形だと MissingTableClassException)。
  • disableHydration() を使うと結果はフラット連想配列になる。select には明示エイリアスを付けて(->select(['id' => 'SampleArticles.id', 'tag_name' => 'SampleTags.name']))$row['id'] でフラット参照する。
  • 生SQLが必要なときは $this->getConnection()->execute($sql)->fetchAll('assoc')(戻りはフラット連想配列)。トランザクションは $this->getConnection()->begin()/commit()/rollback()。
  • プラグイン共通のメソッドは共通基底 Table(\BaserCore\Model\Table\AppTable を継承した SampleAppTable 等)に置き、各 Table はそれを継承する。

3. コントローラ

管理画面コントローラは src/Controller/Admin/ に置き、プラグインの Admin 基底(SampleAdminAppController など、BcAdminAppController を継承)を経由して継承する。

  • テーブル取得は $this->fetchTable('Sample.SampleArticles')(複数形エイリアス)。
  • ページネーションはクエリビルダを組んで $this->paginate($query) に渡す(PaginatorComponent は存在しない。loadComponent('Paginator') は MissingComponentException)。必要な関連は contain()、手書き結合は leftJoinWith()/innerJoinWith()/matching()、->groupBy()/->having() も使える。並べ替え・ページはクエリ文字列 ?sort=&direction=&page= で自動処理される。
  • サイト設定値は \BaserCore\Utility\BcSiteConfig::get('admin_list_num') で取得する。ログインユーザーは \BaserCore\Utility\BcUtil::loginUser()(未ログイン時は false を返すので ?-> は効かない。(BcUtil::loginUser() ?: null)?->id か明示分岐で書く)。
  • フラッシュメッセージは $this->BcMessage->setSuccess('...') / setError('...')。
  • redirect() は必ず return $this->redirect([...]); と書く。redirect は Response を返すだけで後続コードが実行され続けるため、ガード節(引数チェック→リダイレクト)では return しないと直後の処理まで走って TypeError 等になる。例外: initialize()/beforeFilter() など : void 宣言のメソッド内では return を付けない(付けると TypeError)。
  • request は immutable。getter への代入は書けない。値を足すときは withQueryParams()/withData() で新しい request を作って setRequest() する。
    php
    $this->setRequest($this->getRequest()->withQueryParams(
        $this->getRequest()->getQueryParams() + ['begin' => $begin]
    ));
    読み取りは getData('key')/getQuery('key')/getParam('key')/getQueryParams()。ajax 判定は $this->getRequest()->is('ajax')。getParam('controller') は CamelCase('SampleArticles')で返る点に注意(分岐で snake_case と比較すると常に false)。
  • カスタム Component は public function initialize(array $config): void { parent::initialize($config); } のシグネチャで書き、コントローラ参照は $this->getController() で取得する。使用するコントローラの initialize() で $this->loadComponent('Sample.Excel') のように明示ロードする(自動ロードはされない)。
  • delete アクションの定石: $this->getRequest()->allowMethod(['post', 'delete']) → $entity = $table->get($id)(RecordNotFoundException を try/catch して「無効な処理です」→ index へ)→ $table->delete($entity)。一覧の bca-submit-token 経由の POST を受けるアクションは FormProtection の unlockedActions に登録する(§6)。
  • テンプレートが参照する計算値・設定値はコントローラで明示的に set() する。旧来のコールバックによる自動注入は無いので、行ごとの計算値は Table の計算メソッド(calcBalance($entity) 等)を paginate 後にループ適用する。プラグイン設定(Configure::read('Sample'))を多数のテンプレートが参照する場合は、プラグインの Admin 基底コントローラの beforeRender() で一括 set する。
    php
    public function beforeRender(\Cake\Event\EventInterface $event): void
    {
        parent::beforeRender($event);
        $this->set(\Cake\Core\Configure::read('Sample'));
    }
    各 Admin コントローラは BcAdminAppController 直継承ではなくこの基底を継承する(直継承だと設定ビュー変数が全画面で欠落する)。
  • エラー応答は例外で書く: throw new \Cake\Http\Exception\BadRequestException(...) / NotFoundException / InternalErrorException。レスポンス操作は $this->setResponse($this->getResponse()->withStatus(400))。
  • 検索インデックス・ヘルプは $this->setSearch('sample_articles_index') / $this->setHelp('...') で設定する。

4. 管理画面フォーム・一覧・検索

  • 管理画面のフォームは $this->BcAdminForm を使い、入力は必ず control() で出す(input() は無い)。フロント用 $this->BcForm/$this->Form や素のウィジェット(text()/select()/multiCheckbox())を直接呼ぶと bca-* の管理スタイルが当たらず崩れる。一覧の一括処理(ListTool.batch)・一括選択(ListTool.checkall)・submit()/button() も BcAdminForm で出す。
  • フォーム開始: エンティティがあるなら create($entity)、コンテキストレスなら create(null, ['valueSources' => ['data', 'context']]) と書く(GET検索フォームは ['query', 'context'])。文字列モデル名を渡すと No context provider found for value of type string。
  • 複数チェックボックスは options をループして個別 control で書く(['multiple' => 'checkbox'] や multiCheckbox() は崩れる):
    php
    <?php $selected = array_map('strval', (array)$this->getRequest()->getData('SampleArticle.tags')) ?>
    <?php foreach($options as $v => $label): ?>
      <?php echo $this->BcAdminForm->control('SampleArticle.tags[]', [
          'type' => 'checkbox', 'label' => $label, 'value' => $v,
          'checked' => in_array((string)$v, $selected, true), 'hiddenField' => false]) ?>
    <?php endforeach ?>
    受け側の条件は 'field IN' => (array)$values(§2)。
  • control() の label に HTML を渡すときは配列形式 ['text' => '<span...>', 'escape' => false] と書く(文字列だとエスケープされ生表示)。
  • 検索フォームは templates/Admin/element/search/{name}.php(単数 search/) に置く。コントローラの setSearch('sample_articles_index') がこのパスを解決する。検索は GET 送信が基本(条件がURLに残りページャ遷移でも保持)で、コントローラは getQueryParams() から条件を読む。入力値の再表示には valueSources に query を含める。
  • control() の DOM 生成規則を把握して JS と整合させる(描画は通るのに JS 連携が無言で死ぬ最頻出ポイント):
    • id は小文字ハイフン: control('SampleArticle.publish_date') → id="samplearticle-publish-date"(Text::slug による)。
    • name は Model[field] 形式: name="SampleArticle[publish_date]"。
    • バンドルJSが CamelCase id(#SampleArticlePublishDate)や別形式の name を掴む設計なら、JS 側を実生成の id/name に合わせるか、control(..., ['id' => 'SampleArticlePublishDate']) で明示 id を振る(その場合 label の for も合わせる。name は POST キーなので変えない)。フォーム全体への委譲 $("#FormId").on('change', 'select, input', ...) にすると id 依存自体をなくせる。
    • 統合テストで「JS が期待する id/name が HTML に実在する」ことを assert する番人パターンを置くと、この不整合の回帰を検出できる(描画200のテストでは検出できない。basercms-unittest 参照)。
  • 年月セレクトは control('Model.field.year', ['type' => 'select', 'options' => $years, 'label' => false]) のように自前 options で書く(year()/month() というメソッドは無い)。getData('Model.field') は ['year' =>, 'month' =>] で受かる。
  • 一覧 element とフォームの同居に注意: Paginator->sort() や element('pagination') を含む一覧 element を、ページネーションしない画面(編集フォームへの埋め込み等)で描画すると You must set a pagination instance using setPaginated() first で落ちる。ページャ系の描画は「専用一覧コントローラのときだけ」に getParam('controller') === 'SampleArticles'(CamelCase)でガードする。
  • 生SQL等のカスタムページネーションは、総件数+当ページ行を自前で取得し new \Cake\Datasource\Paging\PaginatedResultSet(new \ArrayIterator($rows), $params) をビュー変数に set する(PaginatorHelper が PaginatedInterface を自動検出する)。sort カラムはホワイトリストで ORDER BY を組み立てる。
  • フォーム拡張フックは $this->BcFormTable->dispatchBefore()/dispatchAfter()、$this->BcAdminForm->dispatchAfterForm() を使う。
  • BcBaser->link() に画像/HTML を渡すときは ['escape' => false]。アイコンボタンは ['class' => 'bca-btn-icon', 'data-bca-btn-type' => 'add', 'data-bca-btn-size' => 'lg'] の作法で書く。
Show full SKILL.md (368 more words)Show less

5. イベント

リスナーは src/Event/ に置く。プラグインの BcPlugin が自動 attach するため、リスナーが壊れているとイベント発火で即 Fatal になる点に注意して書く。

  • 自動 attach の実体・ファイル名の厳密な規則: BaserCore\Utility\BcEvent::registerPluginEvent()(BaserCorePlugin から有効化済み全プラグインに対して呼ばれる)が、src/Event/{Plugin名}{Controller|Model|View|Helper|Mailer}EventListener.php というファイル名と完全一致するクラスだけを自動生成・EventManager::instance()->on() で登録する。例えば <Plugin> プラグインでヘッダーを差し替えるビューイベントリスナーなら、ファイル名は必ず src/Event/<Plugin>ViewEventListener.php、クラス名も <Plugin>ViewEventListener にする(名前が1文字でもズレると静かに登録されず、イベントが一切発火しない)。
  • 基底クラス: モデル系は BcModelEventListener、コントローラ系は BcControllerEventListener、ビュー系は BcViewEventListener、ヘルパ系は BcHelperEventListener を継承する。
  • イベント名の命名規則: public $events = [...] に登録する。
    • 自プラグインのモデルイベント: 'SampleArticles.afterSave'(Table 名の複数形)。
    • 他プラグインを修飾する場合: 'BcBlog.BlogPosts.afterSave'(プラグイン名+Table複数形)。名前が実在イベントと一致しないとエラーも出ずに無発火になるため、発火側を grep -rn "dispatchLayerEvent\|createEvent" vendor/baserproject/... して実在するイベント名に合わせる。
    • View 系は BcViewEventDispatcher が標準イベントを {Plugin}.{ViewName}.afterRender として再ディスパッチする方式。'BcBlog.Blog.afterRender' のように View の getPlugin()/getName() に一致させる。
  • ハンドラ名はイベント名の CamelCase: 'BcBlog.BlogPosts.afterSave' → public function bcBlogBlogPostsAfterSave(EventInterface $event)。型ヒントは \Cake\Event\EventInterface。
  • イベントデータ: 読み取りは $event->getData('key')、書き込みは $event->setData('key', $v)、サブジェクト(Table/Controller/View)は $event->getSubject()。保存系イベントのエンティティは $event->getData('entity') で取る。
  • コンストラクタで Table を即時取得しない。リスナーは bootstrap 段階で生成されるため、コンストラクタ内の TableRegistry::...->get() は MissingTableClassException になる。ハンドラ内で必要になったときに遅延取得する。
  • Model.afterFind というイベントは存在しない。find 結果の加工は formatResults() かカスタム finder に書く。
  • View 系ハンドラでビュー変数を読むときは $view->get('varName') を使う($view->viewVars プロパティは無い)。$view->request プロパティも無い(未知プロパティ参照はヘルパの自動ロード扱いとなり xxxHelper could not be found で全画面を巻き込む)ので $view->getRequest()->getParam(...) で書く。
  • ハンドラからビューへ値を渡すときは $controller->set('x', $v)(request への代入は immutable のため無効)。

6. Vue / jQuery 連携

  • 管理画面の ajax / リンク URL をハードコードしない。配置(サブディレクトリ・prefix 変更)で壊れるため、baser が用意するグローバル $.bcUtil.adminBaseUrl(baseUrl + '/' + baserCorePrefix + '/' + adminPrefix + '/'、末尾スラッシュ付き)に連結して書く: $.bcUtil.adminBaseUrl + 'sample/sample_articles/edit/' + id。
    • Vue2 のテンプレート式からはグローバル $ を参照できないので、computed adminBaseUrl() { return $.bcUtil.adminBaseUrl; } を公開し :href="adminBaseUrl + 'sample/...'" と書く。
  • Vue バンドルはソース(webroot/js/src)を直して webpack で再ビルドする(コンパイル済み .bundle.js/.bundle.css を手で編集しない)。webpack.config.js があれば cd plugins/Sample && npm install && npx webpack。出力は webroot/js/[name].bundle.js、CSS は ../css/[name].bundle.css。再ビルド後に bundle へ反映されたか grep し、ブラウザは Cmd+Shift+R で確認する。
  • ajax アクションは Response を返す: return $this->getResponse()->withType('application/json')->withStringBody(json_encode($rows ?: []));。文字列を return しても出力されない。Vue が参照するフィールドはフラット配列に整形して返す。
  • Vue が動的生成するフォーム行(明細等)は FormProtection に弾かれる(Unexpected field '...' in POST data)。描画時に DOM に無いフィールドは送信トークンに含まれないため、動的フィールドを POST するアクションは beforeFilter() で $this->FormProtection->setConfig('unlockedActions', ['add', 'edit', ...]) に登録する(CSRF は別ミドルウェアなので維持される)。Vue 側の :name は 'Model[' + i + '][field]' 形式で書く(コントローラの getData('Model') で受かる形)。
  • prop に複数型が来るなら type: [String, Number] のように許容する。ajax レスポンスが JSON でなく文字列になると v-for が文字単位で反復する(Expected Array, got String が1文字ずつ大量に出たらサーバ側エラーを疑う)。

7. 日付・数値・文字列

  • $this->Time->format($d, 'yyyy-MM-dd') は ICU パターンで書く(PHP の date() 形式ではない。Y=週年・m=分・M=月なので 'Y-m-d' と書くと月が0になる)。スラッシュ区切りは 'yyyy/MM/dd'、時刻込みは 'yyyy-MM-dd HH:mm'。第2引数を省略すると日時表示になるため、日付のみは必ず指定する。BcTime->format も同様。
    • 一方、エンティティの日時プロパティの $v->format('Y/m/d') は PHP の date() 形式(DateTimeInterface::format)。TimeHelper 経由(ICU)と使い分ける。
    • null になりうる日付は $this->Time->format($e->x, 'yyyy-MM-dd')(helper は null で空文字)か $e->x?->format('Y-m-d') で書く。
  • Number::format() / NumberHelper::format() / currency() は第1引数 null 不可(TypeError)。nullable な値は format($x ?? 0, [...]) / currency($e->budget ?? 0, ...) とガードする。
  • Text::truncate() / BcText::truncate() も第1引数 string 必須。nullable カラムは truncate((string)$e->notes, 46) と (string) キャストする。
  • 独自の数値フォーマット登録(addFormat 相当)は無い。記号なし3桁区切りは $this->Number->format($v, ['places' => 0]) で書く(currency() の第2引数は ICU の ISO 通貨コード前提で、未知コードは接頭辞としてそのまま出力される)。

8. ルーティング・ショートコード・管理画面CSS

  • config/routes.php は RouteBuilder のクロージャで書く:
    php
    <?php
    use Cake\Routing\RouteBuilder;
    return function (RouteBuilder $routes) {
        $routes->connect('/sample/*', ['plugin' => 'Sample', 'controller' => 'SampleArticles', 'action' => 'view']);
    };
    コントローラ名は CamelCase。Router::getRequest() を routes.php で使う場合は CLI 実行時に null になるため if (!$request) return; のガードを必ず入れる(無いと bin/cake が全コマンド停止する)。
  • ショートコードは setting.php の 'BcShortCode' キーに登録する(§1のコード例参照)。登録元プラグインが無効だと未登録扱いとなり、コンテンツ中の [Sample.showList] がエラーなく生テキストのまま露出する(プラグイン有効化が前提)。
  • 横長テーブル(多数列)の管理画面では、管理テーマの .bca-container(flex) 子要素 .bca-main に min-width:0 が無いためページ全体が横スクロールする。その画面の CSS に .bca-main { min-width: 0; } を足し、テーブルは overflow:auto のスクロールラッパーで囲う(position:sticky の列固定もこれで効く)。CSSがバンドル内なら webpack 再ビルド(§6)。

9. テスト

テストの実行手順・プラグイン単体(スタンドアロン)テスト基盤の導入・Fixture Factory の書き方は basercms-unittest スキルを正本として参照する。開発時の要点だけ挙げると: 管理画面はログイン付きコントローラ統合テストで GET 描画200+POST フロー(DB 変化の assert)を固定する、描画200は warning を握り潰すのでテスト後に error.log の pristine を確認する、JS 連携がある画面は「JS が期待する DOM id/name が HTML に実在する」assert(§4の番人パターン)を置く、日付保存は値まで assert する(silently NULL を見逃さない)。

© baserproject, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/basercms5-plugin-development of baserproject/basercms.

Open the folder on GitHubat commit 748b4f6

Compare with similar skills

Basercms5 Plugin Development next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Basercms5 Plugin Development compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Basercms5 Plugin Development this skillbaserproject/basercms190—~4.9kAutomated safety check: PassMIT
Discord Php Interactionsdiscord-php/DiscordPHP1.1k—~1.5kAutomated safety check: PassMIT
Interaction Flow Keeperdiscord-php/DiscordPHP1.1k—~1.7kAutomated safety check: PassMIT
Wp Plugin Developmentjorgerosal/wordpress-skills100—~12kAutomated safety check: PassMIT
Hook Development for Claude Code Pluginsanthropics/claude-plugins-official37k11 repos~4.1kAutomated safety check: NotesApache-2.0
Claude Code Agent Developmentanthropics/claude-plugins-official37k8 repos~2.8kAutomated safety check: PassApache-2.0

Similar skills

  • Discord Php Interactions

    discord-php/DiscordPHP

    Build DiscordPHP slash commands and message components — global command trees with subcommands, user- AND guild-installable commands usable in DMs / group DMs / guild channels, Components V2…

    1.1k GitHub stars~1.5k tokensUpdated yesterday
    Agent WorkflowsAuto-check passed
  • Interaction Flow Keeper

    discord-php/DiscordPHP

    Maintain interaction flow — Interaction typing, resolved data caching, command routing, autocomplete, modal responses, and interaction builders.

    1.1k GitHub stars~1.7k tokensUpdated yesterday
    Agent WorkflowsAuto-check passed
  • Wp Plugin Development

    jorgerosal/wordpress-skills

    WordPress plugin architecture review and WordPress.org submission standards.

    100 GitHub stars~12k tokensUpdated 4 mo ago
    Frontend & DesignAuto-check passed
  • Hook Development for Claude Code Plugins

    anthropics/claude-plugins-official

    Official

    Explains how to write Claude Code plugin hooks, both prompt-based checks and bash commands, for events such as PreToolUse, Stop and SessionStart.

    37k GitHub starsUsed in 11 repos~4.1k tokens
    Agent WorkflowsAuto-check: notes
  • Claude Code Agent Development

    anthropics/claude-plugins-official

    Official

    Explains how to write agents for Claude Code plugins: the markdown file with YAML frontmatter, trigger descriptions, model and color settings, and system prompt design.

    37k GitHub starsUsed in 8 repos~2.8k tokens
    Agent WorkflowsAuto-check passed
  • Claude Code Skill Developer Guide

    diet103/claude-code-infrastructure-showcase

    A guide to creating and managing Claude Code skills with auto-activation: skill-rules.json triggers, hooks, enforcement levels, YAML frontmatter and progressive disclosure.

    10k GitHub starsUsed in 10 repos~3.5k tokens
    Agent WorkflowsAuto-check passed

More from baserproject/basercms

All 15 skills in this repo
  • Basercms Security Advisory

    baserproject/basercms

    baserCMS のリポジトリセキュリティアドバイザリ(GHSA・triage含む)対応を、一覧取得→指摘検証→課題別の修正→プライベートフォーク/ブランチ/PR作成→ローカル検証まで一気通貫で扱う手順とスクリプト。「セキュリティアドバイザリを確認」「triageの脆弱性を検証」「アドバイザリごとにフォークとPRを作って」「脆弱性修正をプルリクにまとめて」等のときに使う。Copilot/GHAは…

    190 GitHub stars~1.4k tokensUpdated 2 days ago
    Auto-check passed
  • Basercms Core Plugin Convert

    baserproject/basercms

    baserCMS の「通常プラグイン(サードパーティ/単体配布)」を monorepo の「コアプラグイン」に昇格させる手順。「コアプラグインに変更」「コアプラグイン化」「通常プラグインをコアに昇格」「monorepo に取り込む」等のときに参照する。プラグイン名の規約変更(bc- プレフィックス付与・CamelCase→ハイフン区切り)、.git/シンボリックリンク/standalone…

    190 GitHub stars~3.1k tokensUpdated 2 days ago
    Auto-check passed
  • Basercms Plugin 5x Update

    baserproject/basercms

    baserCMS プラグインを 5.2系 から 5.3系(PHP 8.5 / CakePHP 5.2.x ベース、開発中)へ移行する際の baserCMS 固有の破壊的変更・非推奨・テスト基盤対応のレシピ集。「プラグインを5.3に対応」「baserCMS 5.3 マイグレーション」「PluginCollection::create(): $config null given」「Plugin…

    190 GitHub stars~2.3k tokensUpdated 2 days ago
    Auto-check passed
  • Basercms Release Note

    baserproject/basercms

    baserCMS の plugins/baser-core/VERSION.txt に、リリース分の変更履歴(NEW/CHG/BUG)をコミットログから生成して追記する手順。「VERSION.txt を更新して」「リリースノートを作って」「変更履歴をまとめて」「今回のリリース分の変更点を書き出して」「前回リリースからの差分を VERSION.txt…

    190 GitHub stars~1.6k tokensUpdated 2 days ago
    Auto-check passed
  • Basercms Unittest

    baserproject/basercms

    baserCMS(CakePHP5 / PHPUnit)のユニットテストをローカル Docker 環境で実行・調査する手順。「ユニットテストを実行して」「全テストを走らせて」「このテストだけ流して」「テスト失敗を調べて」「プラグイン単体でテストを動かしたい」「プラグインにテスト環境を導入したい」等のときに参照する。コンテナ名・実行コマンド・権限自動承認のためのコマンド整形・失敗の集計と切り分け方…

    190 GitHub stars~4.9k tokensUpdated 2 days ago
    Auto-check passed
  • Basercms4 Development

    baserproject/basercms

    baserCMS 4系(CakePHP 2.10ベース)+ jQuery プロジェクトの開発ルール集。「baserCMS 4 で開発」「4系のプラグインを修正」「CakePHP 2系のコードを書く」「app/Plugin 配下の Controller/Model/View」「テーマの…

    190 GitHub stars~877 tokensUpdated 2 days ago
    Auto-check passed

Works with

Categories

Questions about Basercms5 Plugin Development

What does Basercms5 Plugin Development do?

baserCMS 5系(CakePHP 5ベース)のプラグインを新規開発・改修する際の正本パターン集。「プラグインを作成」「bcplugin で bake」「管理画面を追加」「Table/Entity の書き方」「initialize でアソシエーション宣言」「validationDefault」「fetchTable と TableRegistry の使い分け」「BcAdminForm /…. Basercms5 Plugin Development is an agent skill from baserproject/basercms.

When should I use Basercms5 Plugin Development?

Basercms5 Plugin Development fits situations like: tasks that involve Hooks and plugins.

How do I install Basercms5 Plugin Development in Claude Code?

Run `npx skills add baserproject/basercms --skill basercms5-plugin-development -a claude-code`. Or copy the skill folder (.agents/skills/basercms5-plugin-development in baserproject/basercms) into .claude/skills/basercms5-plugin-development in your project. Claude Code loads it when a task matches its description.

How do I install Basercms5 Plugin Development in Codex?

Run `npx skills add baserproject/basercms --skill basercms5-plugin-development -a codex`. Or copy the skill folder (.agents/skills/basercms5-plugin-development in baserproject/basercms) into .agents/skills/basercms5-plugin-development in your project. Codex loads it when a task matches its description.

Can I use Basercms5 Plugin Development in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add baserproject/basercms --skill basercms5-plugin-development -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/basercms5-plugin-development, .gemini/skills/basercms5-plugin-development, .github/skills/basercms5-plugin-development and .opencode/skills/basercms5-plugin-development in your project.

What does Basercms5 Plugin Development need to run?

Going by SKILL.md and its folder, Basercms5 Plugin Development needs the command-line tools its instructions call (composer, npm and npx).

Does Basercms5 Plugin Development access the network?

SKILL.md contains no URLs. Its commands use npm and npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Basercms5 Plugin Development safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Basercms5 Plugin Development use?

Basercms5 Plugin Development is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Basercms5 Plugin Development use?

About 4.9k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Basercms5 Plugin Development?

Skills that share tags, products or a category with Basercms5 Plugin Development: Discord Php Interactions (discord-php/DiscordPHP, 1.1k stars), Interaction Flow Keeper (discord-php/DiscordPHP, 1.1k stars), Wp Plugin Development (jorgerosal/wordpress-skills, 100 stars) and Hook Development for Claude Code Plugins (anthropics/claude-plugins-official, 37k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Basercms5 Plugin Development?

baserproject (a GitHub organization) maintains it in baserproject/basercms, which has 190 GitHub stars. The repository holds 15 skills in this directory. The repository was last updated on October 5, 2026.

Source: baserproject/basercms on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.