Comment injecter la requête @dans un service?
Lorsque j'essaie d'injecter la requête @dans l'un de mes services, j'obtiens cette exception:
ScopeWideningInjectionException: L'élargissement de la portée de l'Injection détectée: La définition " service.navigation "référence le service" demande" qui appartient à une portée plus étroite. Généralement, il est plus sûr de soit déplacer "de service.navigation "à portée" demande " ou encore compter sur le modèle de fournisseur en injectant le conteneur lui-même, et en demandant le service "à la demande" à chaque fois elle est nécessaire. Dans de rares cas particuliers cependant, cela pourrait ne pas être nécessaire, alors vous pouvez définir la référence à strict=false pour se débarrasser de cette erreur.
Quelle est la meilleure façon de procéder? Devrais-je essayer de définir ce strict=false
et comment, ou ne devrais-je pas injecter le service de requête, mais plutôt le transmettre au service via mon contrôleur chaque fois que j'appelle des fonctions dont j'ai besoin?
Une autre possibilité serait d'injecter le noyau et de le prendre à partir de là, mais dans mon service je n'utilise que @ router et @ request, donc injecter tout le noyau serait irrationnel.
Merci!
9 réponses
Je pense qu'il y a peut-être eu un malentendu sur ce que dit la documentation officielle. Dans la plupart des cas, vous souhaitez injecter la requête directement avec un attribut scope="request"
sur l'élément de service. Cela fait disparaître l'élargissement de la portée.
<service
id="zayso_core.openid.rpx"
class="Zayso\CoreBundle\Component\OpenidRpx" public="true" scope="request">
Ou yml
zayso_core.openid.rpx:
class: Zayso\CoreBundle\Component\OpenidRpx
public: true
scope: request
C'est seulement dans des cas spéciaux spécifiques tels que les extensions de brindilles où vous devez injecter le conteneur.
Et le noyau n'est même pas mentionné dans la page sur les étendues. L'injection du noyau est loin pire (conceptuellement) que l'injection d'un conteneur.
Mise à jour: pour S2.4 et plus récent, utilisez la réponse de @Blowski ci-dessous.
Dans Symfony 2.4, cela a changé. Maintenant, vous pouvez injecter le service' request_stack'.
Par exemple:
use Symfony\Component\HttpFoundation\RequestStack;
class MyService
{
protected $request;
public function setRequest(RequestStack $request_stack)
{
$this->request = $request_stack->getCurrentRequest();
}
}
Dans votre configuration.yml:
services:
my.service:
class: Acme\DemoBundle\MyService
calls:
- [setRequest, ["@request_stack"]]
Documentation Complète est ici: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack
La meilleure façon que j'ai trouvée pour qu'un service utilise le service de requête, ne pas compter sur l'ensemble du conteneur et ne pas être obligé d'avoir la portée de la requête, était de créer un service RequestInjector qui prend le conteneur. ensuite, vous injectez cela dans le service qui veut utiliser l'objet request
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;
}
}
Pour les services.yml
request_injector:
class: RequestInjector
public: false
arguments: ['@service_container']
some_service:
class: SomeService
arguments: ['@request_injector']
la façon dont j'ai trouvé, et je suis sûr que ce n'est probablement pas le meilleur moyen (peut-être même pas recommandé), est de définir le service de requête comme synthétique.
Edit: en effet, ce n'est pas recommandé, car il désactive les contrôles de santé mentale de la portée. Ce fil contient une bonne explication de la raison pour laquelle Symfony lance cette exception: http://groups.google.com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749
Dans votre services.xml
:
<service id="request" synthetic="true" />
<service id="my_service" class="......">
<argument type="service" id="request" />
</service>
Par le docs, Il est préférable de placer votre service dans la portée de la requête ou d'injecter simplement le conteneur de service.
NB: Cette réponse a été écrite en 2012, quand Symfony 2.0 était sorti et c'était la bonne façon de le faire! Veuillez ne plus downvote:)
Aujourd'hui, je suis passé par le même problème moi-même, alors voici mes 5 cents. Selon le la documentation officielle, il n'est généralement pas nécessaire d'injecter request
dans vos services. Dans votre classe de service, vous pouvez passer kernel
container (l'injection n'est pas une grosse surcharge, comme cela semble), puis accéder à request
comme ce:
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;
}
Ce code fonctionne également correctement lorsque le service est accessible en CLI (par exemple, pendant les tests unitaires).
Si vous ne pouvez pas utiliser RequestStack directement, Vous pouvez créer un service d'usine qui renvoie la requête en cours à L'aide de RequestStack.
# services.yml
app.request:
class: Symfony\Component\HttpFoundation\RequestStack
factory: [ @request_stack, getCurrentRequest ]
Ensuite, vous pouvez accéder à la requête en cours en utilisant le service app.request
.
Je pense qu'il est plus important de se concentrer sur l'obtention de la requête au lieu de la définir. Je ferais quelque chose de similaire à la solution de @Blowski, sauf en utilisant un getter. Ceci est très similaire à l'exemple de 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
}
}
Et vos services.fichier de configuration yml.
services:
newsletter_manager:
class: Acme\HelloBundle\Newsletter\NewsletterManager
arguments: ["@request_stack"]
Maintenant, vous êtes toujours sûr que vous obtenez la demande correcte, et vous n'avez pas à vous soucier de définir/re-définir la demande.
Une Autre façon d'injecter currentRequest
directement:
Injection par Mutateur:
calls:
- ['setRequest', ["@=service('request_stack').getCurrentRequest"]]
Ou injection de contrainte:
arguments:
$request: "@=service('request_stack').getCurrentRequest"
Comme @simshaun indique sa meilleure pratique pour placer votre service dans la portée de la requête. Cela rend le but du service assez clair.
Notez que cela rendra votre service indisponible dans d'autres étendues telles que la ligne de commande. Cependant, si votre service repose sur la demande, vous ne devriez pas l'utiliser sur la ligne de commande de toute façon (car il n'y a pas de demande disponible sur la ligne de commande.