Extension points
title: Extension points description: A table of all the places a module can attach to the core: attributes, interfaces, tags and YAML files; which are automatic and which are tagged by hand.
Below are all the ways to extend without changing the core. There are three mechanisms:
- Attribute: you add
#[...]to a class or method; it is collected at build time. - Interface: you implement an interface. Some interfaces are tagged automatically; for others you need to give an
_instanceoftag in the module'sservices.yamlfile. - YAML file: you put it under a specific name; the core reads it.
If the "Automatic" column in the table is yes, implementing the interface is enough (autoconfigure: true). If no, add the following to your module's services.yaml file:
services:
_instanceof:
App\Core\Queue\AsyncJobHandlerInterface:
tags: ['cp.async_job_handler']
Attributes
| Attribute | Where it goes | What it does | Details |
|---|---|---|---|
#[CpAdminMenu] |
Controller method | Studio/AACP menu item | Menu and permissions |
#[CpSetting] |
Empty carrier class (repeatable) | Setting definition | Settings |
#[CpResource] |
Doctrine entity | Business record: capability, audit, tenant, workflow | Content model |
#[CpField] |
Entity property | Field presentation in a CpResource form |
Content model |
#[CpEntityType] |
Entity | Opens up to platform services such as fields and revisions | Field system |
#[CpFieldType] |
Field type class | A new field type | Field system |
#[CpHook] |
Service method (repeatable) | Attaching to a hook point | Hooks, cron, queue |
#[CpCronJob] |
Service method | Scheduled job | Hooks, cron, queue |
#[CpApi] |
Service method | REST endpoint (/api/...) |
API and webhooks |
#[Auditable], #[Publishable], #[SoftDeletable], #[NotTrashable] |
Entity | Audit log, publication status, soft delete, excluded from trash | Content model |
Interfaces
Tagged automatically
| Interface | What it adds |
|---|---|
SearchProviderInterface |
A site search group |
UpdateHookInterface |
A data job that runs once on a version upgrade |
DoctorCheckInterface |
A cp:doctor check |
RebuilderInterface |
A "recount" job that runs in batches; mandatory for a module that produces content (Rebuild) |
MailTemplateProviderInterface |
Editable email templates |
AccountProfileExtensionInterface |
A section on the account page |
SettingVariantProviderInterface |
Supplying a setting's options dynamically |
WidgetTypeInterface, WidgetAreaProviderInterface, WidgetAreaRendererInterface |
Widget type, area and rendering |
StudioDashboardStatsProviderInterface |
Statistics for the Studio dashboard |
StudioShellBrandingInterface |
Studio branding |
SystemWidgetProviderInterface |
A system widget for the AACP dashboard |
SeoHeadRendererInterface |
Full <head> SEO rendering |
MigrationInterface and related |
Data migration (cp:migrate) |
PluginInterface |
A sub-feature inside a module (plugin) |
Tagged by hand with _instanceof
| Interface | Tag | What it adds |
|---|---|---|
AsyncJobHandlerInterface |
cp.async_job_handler |
Background job handler |
AdminSearchProviderInterface |
cpalius.admin_search.provider |
A group for the panel live search |
NotificationChannelInterface |
cpalius.notification.channel |
Notification channel |
ConfigProviderInterface |
cp.config_provider |
A configuration document for cp:config |
TokenValueProviderInterface |
cp.token_value_provider |
A [type:property] token type |
TextFilterInterface |
cp.text_filter |
A text format filter |
InboxPulseChannelInterface |
cpalius.inbox_pulse_channel |
Inbox badge source |
CspSourceProviderInterface |
cp.csp_source_provider |
An allowed source for CSP |
TrashPurgeHandlerInterface |
cp.trash_purge_handler |
Extra cleanup on permanent deletion from the trash |
InboundWebhookHandlerInterface |
contributions.yaml → inbound_webhooks |
Incoming webhook endpoint |
A class without a tag is simply not collected; it does not give an error. The first check for the "my provider does not work" problem is the tag. php cp-core/bin/console cp:debug and debug:container --tag=<tag> list what was collected.
YAML files
| File | Location | Contents |
|---|---|---|
capabilities.yaml |
Resources/config/ |
The module's capabilities |
routes.yaml |
Resources/config/ |
Route loaders |
services.yaml |
Resources/config/ |
Service definitions and tags |
contributions.yaml |
Resources/config/ |
Contributions to the core (content type routes, schema.org, home page modes, Studio shortcuts…) |
importmap.php |
Resources/config/ |
JavaScript entry points |
*.sql |
Resources/migrations/ |
Schema migrations |
messages+intl-icu.<lang>.yaml |
Resources/translations/ |
Interface translations |
user.role.*.yaml |
cp-content/config/sync/ |
Role definition (the installer can add) |
workflow/*.yaml |
cp-content/config/sync/ |
Workflow definition |
path_pattern.*.yaml |
cp-content/config/sync/ |
Address pattern |
Hooks/<hook_point>.php |
Module root | A file hook without DI |
Hooks/cron.<name>.php |
Module root | A file cron job without DI |
The core's own YAMLs (cp-core/config/): capabilities.yaml, text_formats.yaml, tokens.yaml, view_modes.yaml.
Hook points
The list of hook points offered by the core and modules and how to write them is on the Hooks, cron, queue page. A short list: entity.<type>.pre_save | post_insert | post_update | pre_delete | post_delete, entity.any.<moment>, entity.<type>.access, workflow.<name>.transitioned, theme.*, studio.body.end (end of Studio pages), page.render.content, blog.render.sidebar.
Which one to choose?
| Need | Way |
|---|---|
| Do something on every record | An entity.any.post_insert hook |
| Adding HTML to a page | A theme.* hook point or a widget |
| Notify an external system | Webhook (WebhookEventEmitter) |
| Receive data from outside | #[CpApi] or an incoming webhook |
| Periodic job | #[CpCronJob] |
| Slow job | AsyncJob queue |
| Appear in search | SearchProviderInterface |
| A new field type | #[CpFieldType] |
| A manageable setting | #[CpSetting] |
| Email text | MailTemplateProviderInterface |
| Data fix (on upgrade) | UpdateHookInterface |