composer require cleentfaar/decorator-bundle
config/bundles.php:
return [
// ...
Cleentfaar\DecoratorBundle\CLDecoratorBundle::class => ['all' => true],
];
services.yaml:
services:
App\Decorator\MyDecorator:
decorates: App\Service\MyService
arguments: ['@App\Decorator\MyDecorator.inner']
Decorate a Symfony service (e.g., UserService) to add logging or caching:
// src/Decorator/UserDecorator.php
namespace App\Decorator;
use App\Service\UserServiceInterface;
use Psr\Log\LoggerInterface;
class UserDecorator implements UserServiceInterface
{
private $decorated;
private $logger;
public function __construct(UserServiceInterface $decorated, LoggerInterface $logger)
{
$this->decorated = $decorated;
$this->logger = $logger;
}
public function findUser($id)
{
$this->logger->info('Fetching user', ['id' => $id]);
return $this->decorated->findUser($id);
}
}
Enable Twig extensions in config/packages/twig.yaml:
twig:
extensions:
- Cleentfaar\DecoratorBundle\Twig\DecoratorExtension
Use in templates:
{{ user|decorate('App\Decorator\UserDecorator') }}
Decorate Existing Services:
Use the decorates tag in services.yaml to wrap services with decorators.
services:
App\Service\MyService:
class: App\Service\MyService
App\Decorator\MyDecorator:
decorates: App\Service\MyService
arguments: ['@App\Decorator\MyDecorator.inner']
CacheDecorator wrapping a LoggerDecorator).Dynamic Decoration:
For runtime decoration (e.g., based on user roles), use the DecoratorFactory:
$decorator = $container->get('decorator.factory')->decorate(
$originalService,
'App\Decorator\DynamicDecorator',
['param1' => 'value']
);
Decorate Objects in Templates:
Use the decorate Twig filter to apply decorators dynamically:
{% set decoratedUser = user|decorate('App\Decorator\UserDecorator') %}
{{ decoratedUser.getFormattedName() }}
Pass Decorator Parameters:
{{ user|decorate('App\Decorator\UserDecorator', {'format': 'full'}) }}
Logging: Decorate services to log method calls without modifying original logic.
public function someMethod()
{
$this->logger->debug('Calling someMethod');
return $this->decorated->someMethod();
}
Caching: Wrap expensive operations with a caching decorator.
public function fetchData()
{
return $this->cache->get('data_key', function() {
return $this->decorated->fetchData();
});
}
Validation: Add validation logic around existing methods.
public function update($data)
{
if (!$this->validator->isValid($data)) {
throw new \InvalidArgumentException('Invalid data');
}
return $this->decorated->update($data);
}
Circular Dependencies: Avoid decorating a service that is itself a dependency of the decorator.
Twig Filter Scope:
The decorate Twig filter operates on the current object scope. If the object isn’t passed correctly, the decorator won’t work.
Outdated Package: Last updated in 2014, so:
autoconfigure) may not work as expected.services.yaml.Decorator Naming Conflicts: If two decorators implement the same interface, Symfony may fail to autowire.
decorates or arguments.Check Decorator Chain:
Use dump($service) to inspect the decorated object’s class hierarchy.
$service = $container->get('App\Service\MyService');
dump(get_class($service)); // Should show the decorator class
Twig Debugging:
If decorate fails silently, enable Twig strict mode:
twig:
strict_variables: true
Service Dump: List all services to verify decoration:
php bin/console debug:container | grep MyService
Custom Decorator Factory:
Extend DecoratorFactory to support dynamic decorator resolution:
class CustomDecoratorFactory extends \Cleentfaar\DecoratorBundle\Decorator\DecoratorFactory
{
public function resolveDecorator($original, $decoratorClass, array $args = [])
{
// Custom logic here
return parent::resolveDecorator($original, $decoratorClass, $args);
}
}
Register it in services.yaml:
services:
decorator.factory:
class: App\Decorator\CustomDecoratorFactory
decorates: cleentfaar_decorator.decorator_factory
Twig Extension Overrides: Override the Twig extension to add custom filters:
class CustomDecoratorExtension extends \Cleentfaar\DecoratorBundle\Twig\DecoratorExtension
{
public function getFilters()
{
return array_merge(parent::getFilters(), [
new \Twig\TwigFilter('custom_decorate', [$this, 'customDecorate']),
]);
}
}
Register it:
twig:
extensions:
- App\Twig\CustomDecoratorExtension
Service Provider Integration: If using in Laravel, bind the bundle’s services in a provider:
public function register()
{
$this->app->register(new \Cleentfaar\DecoratorBundle\CLDecoratorBundle());
$this->app->bind('decorator.factory', function($app) {
return new \Cleentfaar\DecoratorBundle\Decorator\DecoratorFactory();
});
}
Facade Support: Decorate facades by binding the decorated service:
Facade::bind('MyService', function($app) {
return $app->make('App\Decorator\MyDecorator');
});
How can I help you explore Laravel packages today?