saltar hacia el contenido

Buscar

Servicios decoradores en Symfony 7

3 min read Updated:

Decorando servicios en Symfony 7

# symfony-7
Sin post relacionado

Decorando Servicios en Symfony 7

En proyectos profesionales de Symfony, a menudo necesitamos extender la funcionalidad de un servicio sin modificar su código original. Para ello, Symfony 7 ofrece la anotación #[AsDecorator], que nos permite sustituir automáticamente un servicio por su decorador sin necesidad de configuración manual en services.yaml.

En este post, vamos a implementar un decorador de un servicio de notificaciones para agregar logging extra antes y después del envío de correos electrónicos.


Caso de uso

Tenemos un servicio NotificationService que envía correos electrónicos. Queremos decorarlo con LoggingNotificationService para que registre logs antes y después de enviar el email, sin modificar el código original.


1. Servicio Original (NotificationService)

namespace App\Service;
 
use Psr\Log\LoggerInterface;
 
class NotificationService
{
    private LoggerInterface $logger;
 
    public function __construct(LoggerInterface $logger)
    {
        $this->logger = $logger;
    }
 
    public function sendEmail(string $to, string $message): void
    {
        // Simulación del envío de correo
        echo "Enviando email a $to: $message";
 
        // Registrar que el correo se ha enviado
        $this->logger->info("Correo enviado a {$to}");
    }
}

2. Crear el Decorador (LoggingNotificationService)

Ahora creamos un decorador con #[AsDecorator] que añade logs adicionales antes y después de llamar al servicio original:

namespace App\Service;
 
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
 
#[AsDecorator(decorates: 'App\Service\NotificationService')]
class LoggingNotificationService
{
    private NotificationService $notificationService;
    private LoggerInterface $logger;
 
    public function __construct(NotificationService $notificationService, LoggerInterface $logger)
    {
        $this->notificationService = $notificationService;
        $this->logger = $logger;
    }
 
    public function sendEmail(string $to, string $message): void
    {
        // Agregar logging antes de enviar
        $this->logger->info("Preparando para enviar un correo a {$to}");
 
        // Llamamos al servicio original
        $this->notificationService->sendEmail($to, $message);
 
        // Agregar logging después de enviar
        $this->logger->info("Correo enviado correctamente a {$to}");
    }
}

3. Uso en un Controlador (NotificationController)

Ahora probemos el decorador en un controlador:

namespace App\Controller;
 
use App\Service\NotificationService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
 
class NotificationController extends AbstractController
{
    #[Route('/send-notification', name: 'send_notification')]
    public function sendNotification(NotificationService $notificationService): Response
    {
        $notificationService->sendEmail('usuario@example.com', '¡Bienvenido a nuestro servicio!');
 
        return new Response('Correo enviado y logueado correctamente.');
    }
}

4. Resultado esperado

Si accedemos a http://127.0.0.1/send-notification, el sistema generará estos logs:

Preparando para enviar un correo a usuario@example.com
Enviando email a usuario@example.com: ¡Bienvenido a nuestro servicio!
Correo enviado correctamente a usuario@example.com

Y en el navegador veremos:

Correo enviado y logueado correctamente.

Beneficios de usar #[AsDecorator]

  • Extiende la funcionalidad sin modificar el código original.
  • Automatiza la sustitución del servicio sin configurar services.yaml.
  • Separa responsabilidades: el servicio original solo envía emails y el decorador solo agrega logs.
  • Fácil mantenimiento y escalabilidad.