design_engine section in config/ibexa/settings.yaml:
ibexa:
system:
default:
design_engine:
template_fallback_order: [custom, system]
asset_fallback_order: [region_specific, global]
// In a custom bundle's DependencyInjection/Configuration.php
$builder->appendNode('template_fallback_order')
->arrayPrototype()
->beforeNormalization()
->ifString()
->then(function ($value) { return [$value]; });
Ibexa\DesignEngine\TemplatePathRegistry and Ibexa\DesignEngine\AssetFallbackResolver for template and asset resolution logic.config/ibexa/settings.yaml for default fallback orders and override them as needed.Ibexa\DesignEngine\Twig\Extension\DesignEngineExtension for Twig template path resolution hooks.Template Fallback Chains:
custom → system → fallback).{% extends %} or {% include %}:
{% extends 'custom:template.html.twig' with {'fallback': ['system:template.html.twig', 'fallback:template.html.twig']} %}
TemplatePathRegistry service to dynamically resolve paths:
$templatePath = $templatePathRegistry->getTemplatePath(
'custom:template',
['fallback' => ['system:template', 'fallback:template']]
);
Asset Fallback for Media:
settings.yaml:
ibexa:
system:
default:
design_engine:
asset_fallback_order: [localized, default]
AssetFallbackResolver to resolve assets with fallbacks:
$asset = $assetFallbackResolver->resolve(
$content->getField('image')->getValue(),
['localized', 'default']
);
Dynamic Fallback Logic:
TemplatePathRegistry or AssetFallbackResolver to add custom fallback logic (e.g., language-based fallbacks):
// Custom resolver for language-specific assets
$resolver = new CustomAssetFallbackResolver(
$originalResolver,
$languageService
);
Integration with Content Types:
ContentType configuration:
# config/contenttypes/my_content_type.yaml
fields:
image:
fallback_assets: [eu_logo, us_logo]
Local Development:
config/packages/dev/ibexa.yaml for testing:
ibexa:
system:
default:
design_engine:
template_fallback_order: [debug, custom, system]
debug prefix to log fallback resolutions during development.Deployment:
staging vs. production):
# config/packages/prod/ibexa.yaml
ibexa:
system:
default:
design_engine:
asset_fallback_order: [cdn, local]
CI/CD:
public function testTemplateFallbackOrder()
{
$registry = $this->createMock(TemplatePathRegistry::class);
$registry->expects($this->once())
->method('getTemplatePath')
->with('custom:template', ['fallback' => ['system:template']])
->willReturn('/path/to/template.html.twig');
$this->assertTrue(true); // Add assertions for fallback logic
}
Symfony Dependency Injection:
TemplatePathRegistry and AssetFallbackResolver in controllers/services:
public function __construct(
private TemplatePathRegistry $templatePathRegistry,
private AssetFallbackResolver $assetFallbackResolver
) {}
Twig Extensions:
class CustomDesignEngineExtension extends \Twig\Extension\AbstractExtension
{
public function getFunctions()
{
return [
new \Twig\TwigFunction('resolve_template', [$this->templatePathRegistry, 'getTemplatePath']),
];
}
}
{% set templatePath = resolve_template('custom:template', ['fallback': ['system:template']]) %}
Event Listeners:
Ibexa\DesignEngine\Event\TemplateFallbackEvent to modify fallback behavior dynamically:
public function onTemplateFallback(TemplateFallbackEvent $event)
{
if ($event->getTemplateName() === 'custom:template') {
$event->addFallback('admin:template');
}
}
Circular Fallbacks:
A → B → A). The TemplatePathRegistry throws a CircularReferenceException if detected.ibexa:
system:
default:
design_engine:
debug: true
Missing Assets/Templates:
ResourceNotFoundException. Handle gracefully in your code:
try {
$asset = $assetFallbackResolver->resolve($assetId, ['localized', 'default']);
} catch (ResourceNotFoundException $e) {
$asset = $this->getDefaultAsset();
}
Configuration Overrides:
settings.yaml can be overridden by environment-specific configs. Ensure consistency across environments:
# Validate configs in CI
php bin/console ibexa:validate-config
Caching:
php bin/console cache:clear
Log Fallback Resolutions:
config/packages/dev/monolog.yaml:
handlers:
ibexa_design_engine:
type: stream
path: "%kernel.logs_dir%/design_engine.log"
channels: ["ibexa_design_engine"]
public function onTemplateFallback(TemplateFallbackEvent $event)
{
$this->logger->debug(
'Resolving template {template}. Fallbacks: {fallbacks}',
['template' => $event->getTemplateName(), 'fallbacks' => $event->getFallbacks()]
);
}
Common Issues:
templates/custom/template.html.twig).ContentService permissions).Performance Optimization:
localized before default for assets).AssetFallbackResolver::resolveWithCache() to reduce redundant resolutions.Testing:
TemplatePathRegistry and AssetFallbackResolver in unit tests:
$registry = $this->createMock(TemplatePathRegistry::class);
$registry->method('getTemplatePath')
->willReturnMap([
['custom:template', ['fallback' => ['system:template']], '/path/to/custom.html.twig'],
['system:template', [], '/path/to/system.html.twig'],
]);
Extending Functionality:
AbstractFallbackResolver:
class LanguageAwareAssetFallbackResolver extends AbstractFallbackResolver
{
public function __construct(
private LanguageService $languageService,
iterable $fallbackResolvers
) {
parent::__construct($fallbackResolvers);
How can I help you explore Laravel packages today?