symfony - services - ¿Cómo inyectar la @request en un servicio?
symfony service factory (8)
Cuando trato de insertar @request en cualquiera de mis servicios, obtengo esta excepción:
ScopeWideningInjectionException: Scope Widening Injection detected: la definición "service.navigation" hace referencia a la "solicitud" de servicio que pertenece a un alcance más restringido. En general, es más seguro mover el "servicio.navegación" a la "solicitud" del alcance o, alternativamente, confiar en el patrón del proveedor inyectando el propio contenedor y solicitando la "solicitud" del servicio cada vez que sea necesario. Sin embargo, en casos excepcionales y especiales que tal vez no sean necesarios, puede establecer la referencia en strict = false para deshacerse de este error.
¿Cuál es la mejor manera de proceder? ¿Debo tratar de establecer esto strict=false
y cómo, o NO debo inyectar el servicio de solicitud, sino pasarlo al servicio a través de mi controlador cada vez que llamo a funciones que necesito?
Otra posibilidad sería inyectar el kernel y tomarlo desde allí, pero en mi servicio solo estoy usando @router y @request, por lo que inyectar todo el kernel sería irracional.
¡Gracias!
Como @simshaun establece su mejor práctica para colocar su servicio en el alcance de la solicitud. Esto hace que el propósito del servicio sea bastante claro.
Tenga en cuenta que esto hará que su servicio no esté disponible en otros ámbitos, como la línea de comandos. Sin embargo, si su servicio se basa en la solicitud, no debe utilizarlo en la línea de comando de todos modos (porque no hay una solicitud disponible en la línea de comandos.
Creo que es más importante concentrarse en obtener la solicitud en lugar de establecerla. Haría algo similar a la solución de @ Blowski, excepto que usaría un getter. Esto es muy similar al ejemplo de la documentation .
namespace Acme/HelloBundle/Newsletter;
use Symfony/Component/HttpFoundation/RequestStack;
class NewsletterManager
{
protected $requestStack;
public function __construct(RequestStack $requestStack)
{
$this->requestStack = $requestStack;
}
protected function getRequest()
{
return $this->requestStack->getCurrentRequest();
}
public function foo()
{
$request = $this->getRequest();
// Do something with the request
}
}
Y su archivo de configuración services.yml.
services:
newsletter_manager:
class: Acme/HelloBundle/Newsletter/NewsletterManager
arguments: ["@request_stack"]
Ahora siempre está seguro de que está obteniendo la solicitud correcta, y no tiene que preocuparse por establecer / volver a establecer la solicitud.
Creo que puede haber algún malentendido sobre lo que dice la documentación oficial. En la mayoría de los casos, desea insertar la solicitud directamente con un atributo scope="request"
en el elemento de servicio. Esto hace que la Ampliación del Alcance desaparezca.
<service
id="zayso_core.openid.rpx"
class="Zayso/CoreBundle/Component/OpenidRpx" public="true" scope="request">
o en yml
zayso_core.openid.rpx:
class: Zayso/CoreBundle/Component/OpenidRpx
public: true
scope: request
Solo en casos especiales específicos, como las extensiones de Twig, donde debe inyectar el contenedor.
Y kernel ni siquiera se menciona en la página de ámbitos. Inyectar el kernel es mucho peor (conceptualmente) que inyectar un contenedor.
ACTUALIZACIÓN: Para S2.4 y más reciente, use la respuesta de @ Blowski a continuación.
En Symfony 2.4, esto ha cambiado. Ahora, puedes inyectar el servicio ''request_stack''.
Por ejemplo:
use Symfony/Component/HttpFoundation/RequestStack;
class MyService
{
protected $request;
public function setRequest(RequestStack $request_stack)
{
$this->request = $request_stack->getCurrentRequest();
}
}
En tu config.yml:
services:
my.service:
class: Acme/DemoBundle/MyService
calls:
- [setRequest, ["@request_stack"]]
La documentación completa está aquí: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
La mejor forma que encontré para hacer que un servicio use el servicio de solicitud, no confiar en todo el contenedor y aún no ser requerido para tener el alcance de la solicitud, fue hacer un servicio RequestInjector que toma el contenedor. luego lo inyectas en el servicio que quiere usar el objeto de solicitud
class RequestInjector{
protected $container;
public function __construct(Container $container){
$this->container = $container;
}
public function getRequest(){
return $this->container->get(''request'');
}
}
class SomeService{
protected $requestInjector;
public function __construct(RequestInjector $requestInjector){
$this->requestInjector = $requestInjector;
}
}
para services.yml
request_injector:
class: RequestInjector
public: false
arguments: [''@service_container'']
some_service:
class: SomeService
arguments: [''@request_injector'']
Si no puede usar RequestStack directamente, puede crear un servicio de fábrica que devuelva la solicitud actual utilizando RequestStack.
# services.yml
app.request:
class: Symfony/Component/HttpFoundation/RequestStack
factory: [ @request_stack, getCurrentRequest ]
Luego puede acceder a la solicitud actual utilizando el servicio app.request
.
La forma en que he encontrado, y estoy seguro de que probablemente no sea la mejor (ni siquiera se recomienda), es definir el servicio de solicitud como sintético.
Editar: De hecho, esto no es recomendable, porque desactiva las comprobaciones de cordura del alcance. Este hilo contiene una buena explicación de por qué Symfony está arrojando esa excepción: http://groups.google.com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749
En su services.xml
:
<service id="request" synthetic="true" />
<service id="my_service" class="......">
<argument type="service" id="request" />
</service>
Según los docs , es mejor si coloca su servicio en el alcance de la solicitud o simplemente inserta el contenedor de servicio.
NB: ¡ Esta respuesta fue escrita en 2012, cuando Symfony 2.0 había salido y entonces era la mejor forma de hacerlo! Por favor, no vuelvas a votar :)
Hoy pasé por el mismo problema, así que aquí están mis 5 centavos. De acuerdo con la documentación oficial, por lo general no es necesario que request
sus servicios. En su clase de servicio puede pasar el contenedor del kernel
(inyectarlo no es una gran sobrecarga, como suena), y luego acceder a la request
esta manera:
public function __construct(/AppKernel $kernel)
{
$this->kernel = $kernel;
}
public function getRequest()
{
if ($this->kernel->getContainer()->has(''request'')) {
$request = $this->kernel->getContainer()->get(''request'');
} else {
$request = Request::createFromGlobals();
}
return $request;
}
Este código también funciona bien cuando se accede al servicio en la CLI (por ejemplo, durante la prueba de la unidad).