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!

50
demandé sur Habeeb Perwad 2012-02-17 21:42:50

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.

31
répondu Cerad 2014-03-26 12:50:06

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

96
répondu Dan Blows 2016-03-30 11:03:39

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']
6
répondu yderay 2012-07-19 19:03:50

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.

5
répondu simshaun 2012-02-17 18:06:42

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).

5
répondu Anton Babenko 2015-05-18 13:21:11

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.

4
répondu mloberg 2015-09-10 19:47:03

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.

1
répondu GreeKatrina 2016-05-23 16:10:47

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"
0
répondu Farshadi 2018-09-07 15:06:23

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.

-1
répondu Josiah 2012-02-22 22:02:06