[5.9] Add EVENT_DEFINE_BASEPATHS
Description
This PR adds a new event to the craft\web\View class named EVENT_DEFINE_BASEPATHS.
The event is triggered right before Craft resolves a template via _resolveTemplate(), and exposes the array of base template paths that Craft will search when locating templates.
Why this event is needed
Craft currently provides no way for modules or plugins to modify the base template paths that are used during template resolution. All template lookup logic is fixed in TemplateLoader, which means:
- You cannot inject additional template roots dynamically
- You cannot reorder lookup priorities
- You cannot introduce theme-based overrides without modifying core behavior
- Template fallback patterns cannot be implemented cleanly
This PR introduces an officially supported extension point that allows developers to define custom template path strategies using a simple event listener.
What this enables
With EVENT_DEFINE_BASEPATHS, a developer can intercept and modify the list of base paths Craft uses to find templates. This makes it possible to create more flexible, DRY, and maintainable template architectures.
For example, a project may support a theme system where templates should be resolved in the following order:
- /templates/{theme}/
- /templates/base/
- /templates/
Using the event, a listener can detect the current theme (via project config, environment settings, user preferences, etc.) and dynamically inject or reorder basepaths before template resolution takes place.
This enables a “theme override” mechanism similar to what many CMS platforms provide, while preserving full compatibility with Craft’s existing template loader.
How it works
The event listener receives an array of basepaths and is free to:
- Add new paths
- Remove unwanted paths
- Reorder paths to change lookup priority
Craft then proceeds to resolve the requested template using the modified basepath list.
This approach gives developers complete control over template resolution strategy, without requiring changes to the loader, Twig environment, or existing Craft configuration.
Example use case
Event::on(
View::class,
View::EVENT_DEFINE_BASEPATHS,
function (Event $event) {
$basePaths = [];
$site = Craft::$app->getSites()->getCurrentSite();
$globals = \craft\elements\GlobalSet::find()->site($site)->one();
if ($globals->theme) {
$basePaths[] = Craft::getAlias('@templates') . DIRECTORY_SEPARATOR . $globals->theme;
}
$basePaths[] = Craft::getAlias('@templates') . DIRECTORY_SEPARATOR . 'base';
$event->basePaths = array_merge($basePaths, $event->basePaths);
}
);