SQL on activate, async work, and uninstall.
Migrations
Place .sql files in migrations/; they run once per site on activation, recorded in cms_plugin_migrations. Document tables in plugin.json → database.tables.
Example migration
CREATE TABLE IF NOT EXISTS cms_my_plugin_items (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
created_at DATETIME NOT NULL
);
Background jobs
Register handlers in boot():
$context->registerJobHandler('my-plugin.rebuild', function (Job $job, JobHandlerContext $ctx): array {
// $job->payload, $ctx->pdo, $ctx->projectRoot
return ['ok' => true, 'message' => 'Done'];
});
$context->enqueueJob('my-plugin.rebuild', ['full' => true]);
Handler return shape: ['ok' => bool, 'message' => '…', 'retry' => bool?, 'chain' => list?].
Cron
*/15 * * * * php bin/cms.php jobs:dispatch && php bin/cms.php jobs:work --limit=20
Built-in job types include schedule.publish_due, media.compress_batch, sitemap.warm, maintenance.purge_scheduled.
Composer dependencies
- Root metapackage — Add shared libs to repo root
composer.json - Per-plugin vendor —
composer plugin-deps:prodin each plugin dir
See docs/plugins-dependencies.md for CI and strict checks.
Uninstall
If the plugin root contains uninstall.sql, it runs on delete-from-disk, then cms_plugin_migrations rows for that slug are removed so reinstall can replay migrations.
Circuit breaker
Set PLUGIN_BOOT_CIRCUIT_BREAKER=1 in .env to auto-deactivate plugins that throw during boot.