craftcms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Craft 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
craft-site
skill.
For PHP coding standards and conventions, see the separate
craft-php-guidelines
skill.
通过插件与模块扩展 Craft CMS 5 的参考指南。覆盖从元素、服务到控制器、数据迁移、字段、事件的全部内容。
本技能的范围是扩展 Craft——构建插件、模块、自定义元素类型、字段类型以及后端集成。如果是站点/平台开发(内容建模、区块、条目类型、Twig 模板编写、插件选型),请参考
craft-site
技能。
关于 PHP 编码标准与规范,请参考单独的
craft-php-guidelines
技能。

Documentation

文档

Use
web_fetch
on specific doc pages when a reference file doesn't cover enough detail.
当参考文件涵盖的细节不足时,可使用
web_fetch
获取特定文档页面的内容。

Common Pitfalls (Cross-Cutting)

常见陷阱(通用)

  • Always use
    addSelect()
    in
    beforePrepare()
    — it's the Craft convention and safely additive when multiple extensions contribute columns.
  • Queue workers run in primary site context — use
    ->site('*')
    for cross-site queries.
  • Including
    id
    in
    getConfig()
    — project config uses UIDs, never database IDs.
  • Business logic in models or controllers — services are where logic belongs.
  • Modules need manual template root, translation, and controllerNamespace registration — nothing is automatic.
  • DateTimeHelper
    in elements/queries,
    Carbon
    in services — never mix in the same class.
  • beforePrepare()
    中始终使用
    addSelect()
    ——这是 Craft 的约定,当多个扩展新增列时可以安全叠加。
  • 队列 worker 运行在主站点上下文下——跨站点查询请使用
    ->site('*')
  • 不要在
    getConfig()
    中包含
    id
    ——项目配置使用 UID,绝不使用数据库 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.md
    +
    migrations.md
  • "Add a webhook endpoint" → read
    controllers.md
    +
    events.md
  • "Create a queue job that syncs elements" → read
    queue-jobs.md
    +
    elements.md
    +
    debugging.md
  • "Add a settings page with form fields" → read
    controllers.md
    +
    cp.md
    +
    architecture.md
  • "Register a custom field type" → read
    fields.md
    +
    events.md
  • "Fix PHPStan errors" → read
    quality.md
  • "Add a dashboard widget" → read
    events.md
    (Widget Types section)
  • "Expose template variables for plugin users" → read
    events.md
    (Twig Extensions section)
  • "Attach custom methods to entries" → read
    events.md
    (Behaviors section)
  • "Build a CP utility page" → read
    events.md
    (Utilities section) +
    cp.md
TaskRead
Element core: lifecycle, queries, status, authorization, drafts, revisions, propagation, field layouts, events
references/elements.md
Element index: sources, table/card attributes, sort, conditions, actions, exporters, sidebar, metadata
references/element-index.md
Services, models, records, project config, MemoizableArray, events, API clients, custom validators
references/architecture.md
Controllers: CP CRUD, webhooks, API endpoints, action routing, authorization
references/controllers.md
CP templates, form macros, admin changes, VueAdminTable, asset bundles, CP layout, permissions
references/cp.md
Database migrations, Install.php, foreign keys, indexes, idempotency, deployment
references/migrations.md
Queue jobs, BaseJob, TTR, retry, progress, batch jobs, site context
references/queue-jobs.md
Console commands, arguments, options, progress bars, output helpers, resave actions
references/console-commands.md
Debugging, performance, query strategy, profiling, Xdebug, caching, logging
references/debugging.md
PHPStan, Pest testing, code review checklist
references/quality.md
Field types, native fields, BaseNativeField, field layout elements, FieldLayoutBehavior
references/fields.md
Events: registration, lifecycle, naming conventions, custom events, behaviors, Twig extensions, utilities, widgets, filesystems, discovering events
references/events.md
GraphQL types, queries, mutations, directives, schema components, resolvers
references/graphql.md
根据你的任务阅读对应的参考文件,通常多个文件会同时适用。
任务示例:
  • "构建自定义元素类型" → 阅读
    elements.md
    +
    element-index.md
    +
    fields.md
    +
    migrations.md
  • "添加 Webhook 端点" → 阅读
    controllers.md
    +
    events.md
  • "创建同步元素的队列任务" → 阅读
    queue-jobs.md
    +
    elements.md
    +
    debugging.md
  • "添加带表单字段的设置页面" → 阅读
    controllers.md
    +
    cp.md
    +
    architecture.md
  • "注册自定义字段类型" → 阅读
    fields.md
    +
    events.md
  • "修复 PHPStan 错误" → 阅读
    quality.md
  • "添加仪表盘小部件" → 阅读
    events.md
    (小部件类型章节)
  • "为插件用户暴露模板变量" → 阅读
    events.md
    (Twig 扩展章节)
  • "为条目附加自定义方法" → 阅读
    events.md
    (行为章节)
  • "构建 CP 工具页面" → 阅读
    events.md
    (工具集章节) +
    cp.md
任务参考文件
元素核心:生命周期、查询、状态、鉴权、草稿、修订版、传播、字段布局、事件
references/elements.md
元素索引:来源、表格/卡片属性、排序、条件、操作、导出器、侧边栏、元数据
references/element-index.md
服务、模型、记录、项目配置、MemoizableArray、事件、API 客户端、自定义校验器
references/architecture.md
控制器:CP CRUD、Webhook、API 端点、动作路由、鉴权
references/controllers.md
CP 模板、表单宏、管理员变更、VueAdminTable、资源包、CP 布局、权限
references/cp.md
数据库迁移、Install.php、外键、索引、幂等性、部署
references/migrations.md
队列任务、BaseJob、TTR、重试、进度、批量任务、站点上下文
references/queue-jobs.md
控制台命令、参数、选项、进度条、输出助手、重保存动作
references/console-commands.md
调试、性能、查询策略、性能分析、Xdebug、缓存、日志
references/debugging.md
PHPStan、Pest 测试、代码评审检查清单
references/quality.md
字段类型、原生字段、BaseNativeField、字段布局元素、FieldLayoutBehavior
references/fields.md
事件:注册、生命周期、命名规范、自定义事件、行为、Twig 扩展、工具集、小部件、文件系统、事件发现
references/events.md
GraphQL 类型、查询、变更、指令、schema 组件、解析器
references/graphql.md

Plugin vs Module Differences

插件与模块的差异

Plugins and modules share the same architecture patterns. The differences are in bootstrapping and registration:
FeaturePluginModule
CP template rootAutomatic (by handle)Manual via
EVENT_REGISTER_CP_TEMPLATE_ROOTS
Site template rootManual via eventSame — manual for both
Translation categoryAutomatic (by handle)Manual
PhpMessageSource
in
init()
Settings modelBuilt-in
createSettingsModel()
Env vars, config files, or private plugin (
_
prefix)
Install migration
migrations/Install.php
Content migrations only
Console commandsAutomatic
controllerNamespace
Must set before
parent::init()
, must be bootstrapped
CP nav section
$hasCpSection = true
EVENT_REGISTER_CP_NAV_ITEMS
Project configSettings auto-trackedManual
ProjectConfig::set()
only
Namespace aliasAutomatic via ComposerMust call
Craft::setAlias()
插件和模块的架构模式相同,差异在于引导和注册方式:
特性插件模块
CP 模板根目录自动(按 handle)通过
EVENT_REGISTER_CP_TEMPLATE_ROOTS
手动注册
站点模板根目录通过事件手动注册两者一致——都需要手动注册
翻译分类自动(按 handle)
init()
中手动注册
PhpMessageSource
设置模型内置
createSettingsModel()
环境变量、配置文件,或者私有插件(
_
前缀)
安装迁移
migrations/Install.php
仅支持内容迁移
控制台命令自动识别
controllerNamespace
必须在
parent::init()
之前设置,且必须完成引导
CP 导航栏项
$hasCpSection = true
监听
EVENT_REGISTER_CP_NAV_ITEMS
项目配置自动跟踪设置仅支持手动调用
ProjectConfig::set()
命名空间别名通过 Composer 自动生成必须调用
Craft::setAlias()

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
config/app.php
for console commands to be discoverable.
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
}
模块必须
config/app.php
中完成引导,控制台命令才能被识别。