web-dev-qa-db-fra.com

Comment injecter la @ demande dans un service?

Lorsque j'essaie d'injecter la @ demande dans l'un de mes services, j'obtiens cette exception:

ScopeWideningInjectionException: injection d'élargissement de la portée détectée: La définition "service.navigation" fait référence au service "demande" qui appartient à une portée plus étroite. Généralement, il est plus sûr de l’un ou l’autre déplacez "service.navigation" vers la portée "demande" ou utilisez le système le modèle de fournisseur en injectant le conteneur lui-même et en demandant le service "demande" à chaque fois que cela est nécessaire. Dans de rares cas spéciaux cependant, cela peut ne pas être nécessaire, vous pouvez alors définir la référence sur 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 dois-je NE PAS injecter le service de requête, mais plutôt le transmettre au service par le biais de mon contrôleur chaque fois que j'appelle les fonctions dont j'ai besoin?

Une autre possibilité serait d’injecter le noyau et de l’utiliser à partir de là, mais dans mon service, j’utilise uniquement @router et @request, l’injection de tout le noyau serait donc irrationnelle.

Merci!

50
Tony Bogdanov

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 directement la demande 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 dans yml

zayso_core.openid.rpx: 
    class: Zayso\CoreBundle\Component\OpenidRpx
    public: true
    scope: request

Ce n'est que dans des cas particuliers, tels que les extensions Twig, que vous devez injecter le conteneur.

Et le noyau n'est même pas mentionné dans la page sur les portées. L'injection du noyau est bien pire (conceptuellement) que l'injection d'un conteneur.

UPDATE: Pour la version 2.4 ou ultérieure, utilisez la réponse de @ Blowski ci-dessous.

31
Cerad

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 config.yml:

services:
    my.service:
        class: Acme\DemoBundle\MyService
        calls:
            - [setRequest, ["@request_stack"]]

La documentation complète est ici: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack

96
Dan Blows

Le meilleur moyen que j'ai trouvé pour faire en sorte qu'un service utilise le service de demande, ne repose pas sur le conteneur entier et ne soit toujours pas obligé d'avoir l'étendue de la demande, était de créer un service RequestInjector prenant le conteneur. alors vous injectez cela dans le service qui veut utiliser l'objet de requête

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 services.yml

request_injector:
    class: RequestInjector
    public: false
    arguments: ['@service_container']

some_service:
    class: SomeService
    arguments: ['@request_injector']
6
yderay

La façon dont j'ai trouvé, et je suis sûr que ce n'est probablement pas la meilleure façon (peut même pas être recommandé), est de définir le service de demande comme étant synthétique.

Edit: En effet, ce n'est pas recommandé, car il désactive les contrôles de validité de l'étendue. Ce fil contient une bonne explication de la raison pour laquelle Symfony lève 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>

Selon le docs, il est préférable de placer votre service dans l'étendue de la demande ou simplement d'injecter le conteneur de service.

5
simshaun

NB: Cette réponse a été écrite en 2012, lorsque Symfony 2.0 était disponible et que c'était la bonne façon de le faire! S'il vous plaît ne plus downvote :)


Aujourd'hui, j'ai vécu le même problème moi-même, alors voici mes 5 centimes. Selon 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 le conteneur kernel (l'injecter ne représente pas une surcharge importante, comme cela semble), puis accéder à request comme ceci:

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 bien lorsque vous accédez au service dans l'interface de ligne de commande (par exemple, lors des tests unitaires).

5
Anton Babenko

Si vous ne pouvez pas utiliser RequestStack directement, vous pouvez créer un service d'usine qui renvoie la demande 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 demande en cours à l’aide du service app.request.

4
mloberg

Je pense qu'il est plus important de chercher à obtenir la demande au lieu de la définir. Je ferais quelque chose de similaire à la solution de @ Blowski, excepté l'utilisation d'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 votre fichier de configuration services.yml.

services:
    newsletter_manager:
        class:     Acme\HelloBundle\Newsletter\NewsletterManager
        arguments: ["@request_stack"]

Maintenant, vous êtes toujours sûr d'obtenir la demande correcte et vous n'avez pas à vous soucier de la définition/modification de la demande.

1
GreeKatrina

une autre façon d’injecter currentRequest directement:

injection de poseur: 

calls:
     - ['setRequest', ['@=service("request_stack").getCurrentRequest()']]

ou injection de constricteur:

arguments:
     $request: '@=service("request_stack").getCurrentRequest()'
0
Farshadi