Estoy tratando de ordenar mis variables de sesión integrando AttributBags personalizados en la sesión. En Symfony < 6.0, podía inyectar un AttributBag personalizado en el servicio de sesión.
Ver preguntas relacionadas
Sin embargo, este enfoque ya no funciona en Symfony >= 6.0. Este artículo de blog explica que el servicio de sesión está en desuso y ahora se debe acceder a él a través del servicio request_stack. Para los controladores esto funciona bien.
Mi enfoque actual (que no funciona) se ve así: Definir una clase AttributBag personalizada.
class ShoppingCartBag extends AttributeBag { public function __construct(string $storageKey = 'shoppingCart') { parent::__construct($storageKey); } }Agrega un CompilerPass personalizado en la clase Kernel para que Symfony se encargue de todos los cambios mientras crea el contenedor.
class Kernel extends BaseKernel { use MicroKernelTrait; protected function build(ContainerBuilder $container): void { $container->addCompilerPass(new AddShoppingCartBagToSessionService()); } }El CompilerPass personalizado se ve así.
class AddShoppingCartBagToSessionService implements CompilerPassInterface { public function process(ContainerBuilder $container) { $container->getDefinition('request_stack') //<- Works, but how to access the session? ->addMethodCall('getSession') // How to bridge the gap? This thought does not work. I assume it is because the session is not yet instantiated when the container is build. ->addMethodCall('registerBag', [new Reference('App\Session\CustomBag\ShoppingCartBag')]); } }Como supuso correctamente, la sesión aún no existe al hacer esto a través del pase del compilador.
Symfony usa una llamada SessionFactory para crear la sesión. Entonces, lo que puede hacer en su lugar es decorar el servicio session.factory existente con su propia implementación de SessionFactoryInterface y agregar su bolsa de atributos allí:
Una implementación de esta fábrica de sesiones decorada podría verse así:
namespace App; use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; class SessionFactoryWithAttributeBag implements SessionFactoryInterface { public function __construct(private SessionFactoryInterface $delegate) { } public function createSession(): SessionInterface { $session = $this->delegate->createSession(); $session->registerBag(new ShoppingCartBag()); return $session; } } Y luego puedes decorar session.factory a través de services.yaml :
services: App\SessionFactoryWithAttributeBag: decorates: session.factory arguments: ['@.inner']Ahora, cada vez que se crea una sesión, tu bolsa personalizada también queda registrada
Esa fue una pista importante, ¡gracias @Spea!
Adopté su idea y creé un nuevo decorador para el servicio de sesión. Después de algunas pruebas y errores, encontré una respuesta a mi problema. La solución se ve así. Observe que la sintaxis real es ligeramente diferente de la respuesta dada por Spea.
Cree un AttributBag personalizado extendiendo la clase con el mismo nombre. Tenga cuidado de establecer el nombre de la bolsa de atributos, no la clave de almacenamiento en el constructor. De lo contrario, Symfony arrojará un error cuando intentes acceder a ShoppingCartBag.
namepsace App\Session; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; class ShoppingCartBag extends AttributeBag { public function __construct() { parent::__construct(); $this->setName('shoppingCart'); } }Cree un decorador para cambiar el comportamiento del servicio de sesión para obtener el resultado deseado (incluya ShoppingCartBag en cada sesión).
namespace App\Decorator; use App\Session\ShoppingCartBag; use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; class SessionFactoryShoppingCartBag implements SessionFactoryInterface { public function __construct(private SessionFactoryInterface $delegate) {} public function createSession(): SessionInterface { $session = $this->delegate->createSession(); $session->registerBag(new ShoppingCartBag()); return $session; } }A continuación, decore el servicio de sesión en services.yml agregando el siguiente fragmento de código.
services: App\Decorator\SessionFactoryShoppingCartBag: decorates: session.factory arguments: ['@.inner']