craftcms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCraft CMS 5 — Extending (Plugins & Modules)
Craft CMS 5 — 扩展开发(插件与模块)
Reference for extending Craft CMS 5 through plugins and modules. Covers everything from elements and services to controllers, migrations, fields, and events.
This skill is scoped to extending Craft — building plugins, modules, custom element types, field types, and backend integrations. For site/platform development (content modeling, sections, entry types, Twig templating, plugin selection), see the skill.
craft-siteFor PHP coding standards and conventions, see the separate skill.
craft-php-guidelines通过插件与模块扩展 Craft CMS 5 的参考指南。覆盖从元素、服务到控制器、数据迁移、字段、事件的全部内容。
本技能的范围是扩展 Craft——构建插件、模块、自定义元素类型、字段类型以及后端集成。如果是站点/平台开发(内容建模、区块、条目类型、Twig 模板编写、插件选型),请参考 技能。
craft-site关于 PHP 编码标准与规范,请参考单独的 技能。
craft-php-guidelinesDocumentation
文档
- Extend guide: https://craftcms.com/docs/5.x/extend/
- Class reference: https://docs.craftcms.com/api/v5/
- Generator: https://craftcms.com/docs/5.x/extend/generator.html
Use on specific doc pages when a reference file doesn't cover enough detail.
web_fetch- 扩展指南:https://craftcms.com/docs/5.x/extend/
- 类参考:https://docs.craftcms.com/api/v5/
- 生成器:https://craftcms.com/docs/5.x/extend/generator.html
当参考文件涵盖的细节不足时,可使用 获取特定文档页面的内容。
web_fetchCommon Pitfalls (Cross-Cutting)
常见陷阱(通用)
- Always use in
addSelect()— it's the Craft convention and safely additive when multiple extensions contribute columns.beforePrepare() - Queue workers run in primary site context — use for cross-site queries.
->site('*') - Including in
id— project config uses UIDs, never database IDs.getConfig() - Business logic in models or controllers — services are where logic belongs.
- Modules need manual template root, translation, and controllerNamespace registration — nothing is automatic.
- in elements/queries,
DateTimeHelperin services — never mix in the same class.Carbon
- 在 中始终使用
beforePrepare()——这是 Craft 的约定,当多个扩展新增列时可以安全叠加。addSelect() - 队列 worker 运行在主站点上下文下——跨站点查询请使用 。
->site('*') - 不要在 中包含
getConfig()——项目配置使用 UID,绝不使用数据库 ID。id - 不要把业务逻辑写在模型或控制器中——服务才是存放逻辑的位置。
- 模块需要手动注册模板根目录、翻译、controllerNamespace——没有自动注册的功能。
- 元素/查询中使用 ,服务中使用
DateTimeHelper——不要在同一个类中混用。Carbon
Reference Files
参考文件
Read the relevant reference file(s) for your task. Multiple files often apply together.
Task examples:
- "Build a custom element type" → read +
elements.md+element-index.md+fields.mdmigrations.md - "Add a webhook endpoint" → read +
controllers.mdevents.md - "Create a queue job that syncs elements" → read +
queue-jobs.md+elements.mddebugging.md - "Add a settings page with form fields" → read +
controllers.md+cp.mdarchitecture.md - "Register a custom field type" → read +
fields.mdevents.md - "Fix PHPStan errors" → read
quality.md - "Add a dashboard widget" → read (Widget Types section)
events.md - "Expose template variables for plugin users" → read (Twig Extensions section)
events.md - "Attach custom methods to entries" → read (Behaviors section)
events.md - "Build a CP utility page" → read (Utilities section) +
events.mdcp.md
| Task | Read |
|---|---|
| Element core: lifecycle, queries, status, authorization, drafts, revisions, propagation, field layouts, events | |
| Element index: sources, table/card attributes, sort, conditions, actions, exporters, sidebar, metadata | |
| Services, models, records, project config, MemoizableArray, events, API clients, custom validators | |
| Controllers: CP CRUD, webhooks, API endpoints, action routing, authorization | |
| CP templates, form macros, admin changes, VueAdminTable, asset bundles, CP layout, permissions | |
| Database migrations, Install.php, foreign keys, indexes, idempotency, deployment | |
| Queue jobs, BaseJob, TTR, retry, progress, batch jobs, site context | |
| Console commands, arguments, options, progress bars, output helpers, resave actions | |
| Debugging, performance, query strategy, profiling, Xdebug, caching, logging | |
| PHPStan, Pest testing, code review checklist | |
| Field types, native fields, BaseNativeField, field layout elements, FieldLayoutBehavior | |
| Events: registration, lifecycle, naming conventions, custom events, behaviors, Twig extensions, utilities, widgets, filesystems, discovering events | |
| GraphQL types, queries, mutations, directives, schema components, resolvers | |
根据你的任务阅读对应的参考文件,通常多个文件会同时适用。
任务示例:
- "构建自定义元素类型" → 阅读 +
elements.md+element-index.md+fields.mdmigrations.md - "添加 Webhook 端点" → 阅读 +
controllers.mdevents.md - "创建同步元素的队列任务" → 阅读 +
queue-jobs.md+elements.mddebugging.md - "添加带表单字段的设置页面" → 阅读 +
controllers.md+cp.mdarchitecture.md - "注册自定义字段类型" → 阅读 +
fields.mdevents.md - "修复 PHPStan 错误" → 阅读
quality.md - "添加仪表盘小部件" → 阅读 (小部件类型章节)
events.md - "为插件用户暴露模板变量" → 阅读 (Twig 扩展章节)
events.md - "为条目附加自定义方法" → 阅读 (行为章节)
events.md - "构建 CP 工具页面" → 阅读 (工具集章节) +
events.mdcp.md
| 任务 | 参考文件 |
|---|---|
| 元素核心:生命周期、查询、状态、鉴权、草稿、修订版、传播、字段布局、事件 | |
| 元素索引:来源、表格/卡片属性、排序、条件、操作、导出器、侧边栏、元数据 | |
| 服务、模型、记录、项目配置、MemoizableArray、事件、API 客户端、自定义校验器 | |
| 控制器:CP CRUD、Webhook、API 端点、动作路由、鉴权 | |
| CP 模板、表单宏、管理员变更、VueAdminTable、资源包、CP 布局、权限 | |
| 数据库迁移、Install.php、外键、索引、幂等性、部署 | |
| 队列任务、BaseJob、TTR、重试、进度、批量任务、站点上下文 | |
| 控制台命令、参数、选项、进度条、输出助手、重保存动作 | |
| 调试、性能、查询策略、性能分析、Xdebug、缓存、日志 | |
| PHPStan、Pest 测试、代码评审检查清单 | |
| 字段类型、原生字段、BaseNativeField、字段布局元素、FieldLayoutBehavior | |
| 事件:注册、生命周期、命名规范、自定义事件、行为、Twig 扩展、工具集、小部件、文件系统、事件发现 | |
| GraphQL 类型、查询、变更、指令、schema 组件、解析器 | |
Plugin vs Module Differences
插件与模块的差异
Plugins and modules share the same architecture patterns. The differences are in bootstrapping and registration:
| Feature | Plugin | Module |
|---|---|---|
| CP template root | Automatic (by handle) | Manual via |
| Site template root | Manual via event | Same — manual for both |
| Translation category | Automatic (by handle) | Manual |
| Settings model | Built-in | Env vars, config files, or private plugin ( |
| Install migration | | Content migrations only |
| Console commands | Automatic | Must set before |
| CP nav section | | |
| Project config | Settings auto-tracked | Manual |
| Namespace alias | Automatic via Composer | Must call |
插件和模块的架构模式相同,差异在于引导和注册方式:
| 特性 | 插件 | 模块 |
|---|---|---|
| CP 模板根目录 | 自动(按 handle) | 通过 |
| 站点模板根目录 | 通过事件手动注册 | 两者一致——都需要手动注册 |
| 翻译分类 | 自动(按 handle) | 在 |
| 设置模型 | 内置 | 环境变量、配置文件,或者私有插件( |
| 安装迁移 | | 仅支持内容迁移 |
| 控制台命令 | 自动识别 | 必须在 |
| CP 导航栏项 | | 监听 |
| 项目配置 | 自动跟踪设置 | 仅支持手动调用 |
| 命名空间别名 | 通过 Composer 自动生成 | 必须调用 |
Module Template Root Registration
模块模板根目录注册
php
use craft\events\RegisterTemplateRootsEvent;
use craft\web\View;
Event::on(View::class, View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
function(RegisterTemplateRootsEvent $event) {
$event->roots['my-module'] = __DIR__ . '/templates';
}
);php
use craft\events\RegisterTemplateRootsEvent;
use craft\web\View;
Event::on(View::class, View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
function(RegisterTemplateRootsEvent $event) {
$event->roots['my-module'] = __DIR__ . '/templates';
}
);Module Translation Registration
模块翻译注册
php
Craft::$app->i18n->translations['my-module'] = [
'class' => \craft\i18n\PhpMessageSource::class,
'sourceLanguage' => 'en',
'basePath' => __DIR__ . '/translations',
'allowOverrides' => true,
];php
Craft::$app->i18n->translations['my-module'] = [
'class' => \craft\i18n\PhpMessageSource::class,
'sourceLanguage' => 'en',
'basePath' => __DIR__ . '/translations',
'allowOverrides' => true,
];Module Console Command Registration
模块控制台命令注册
php
public function init()
{
Craft::setAlias('@mymodule', __DIR__);
if (Craft::$app->getRequest()->getIsConsoleRequest()) {
$this->controllerNamespace = 'modules\\mymodule\\console\\controllers';
} else {
$this->controllerNamespace = 'modules\\mymodule\\controllers';
}
parent::init(); // MUST come after setting controllerNamespace
}The module must be bootstrapped in for console commands to be discoverable.
config/app.phpphp
public function init()
{
Craft::setAlias('@mymodule', __DIR__);
if (Craft::$app->getRequest()->getIsConsoleRequest()) {
$this->controllerNamespace = 'modules\\mymodule\\console\\controllers';
} else {
$this->controllerNamespace = 'modules\\mymodule\\controllers';
}
parent::init(); // MUST come after setting controllerNamespace
}模块必须在 中完成引导,控制台命令才能被识别。
config/app.php