Events & Modules

Hook into lifecycle events and extend the core with first-class modules.

Category: Core Architecture

Event system #

VelvetCMS\Core\EventDispatcher is a tiny pub/sub utility:

$events->listen('page.saved', function(Page $page) {
    // fire webhooks, invalidate external cache, etc.
}, priority: 10);
  • Listeners are stored per event with a priority (higher runs first).
  • dispatch() iterates listeners, allowing them to mutate and return a new payload.
  • fire() is an alias when you do not care about the return value.
  • Core fires events such as app.booting, page.loading, page.saved, cache.hit, markdown.parsed, exception.reporting, etc.

Module lifecycle #

The lifecycle is still discover → compile → load → register → boot, but all of the manifest formats, CLI workflows, and troubleshooting steps now live under Module Basics and State & Tooling. This section concentrates on how modules interact with events: once compiled, ModuleManager instantiates each entry class, calls register() so you can bind services/listeners, then boot() so you can attach routes, commands, and theme paths. Example: the Docs module adds documentation routes under its configurable base path (defaults to /docs) and injects its template path via $theme->addPath($module->path('theme')).

Manifests #

Every module ships a module.json defining metadata, dependencies, and config. See Module Basics for the full schema.

ModuleManager::validate() ensures:

  • Entry class exists and implements VelvetCMS\Contracts\Module.
  • Version constraints against core, php, and other modules pass via VersionRegistry::satisfies() (uses composer/semver).
  • Conflicts list is respected.

BaseModule helper #

Extend VelvetCMS\Core\BaseModule for conveniences:

  • $this->path('theme') resolves files relative to the module root.
  • name(), version(), description() read from manifest.
  • manifest() exposes the raw JSON data.

Enabling/disabling modules #

Refer to State & Tooling for the exact CLI commands. Architecturally, toggling state only edits storage/modules.json; compilation reads that file to determine which manifests stay in the load order.

Version registry #

VelvetCMS\Core\VersionRegistry aggregates version metadata for the core and modules. It loads config/version.php, merges compiled module data, and gives you:

$registry = VersionRegistry::instance();
$registry->getVersion('core');
$registry->isCompatible('docs', 'core');
$registry->getModulesRequiringCore('^1.0');

CLI commands like version and list use it to display readable status lines.

Use events + modules to customize VelvetCMS without forking the core—the entire architecture was built for that.