web-dev-qa-db-ja.com

@requestをサービスに注入する方法は?

サービスに@requestを挿入しようとすると、次の例外が発生します。

ScopeWideningInjectionException:スコープ拡大インジェクションの検出:定義「service.navigation」は、より狭いスコープに属するサービス「リクエスト」を参照しています。通常、「service.navigation」をスコープ「request」に移動するか、コンテナ自体をインジェクトし、必要なたびにサービス「request」を要求することでプロバイダーパターンに依存する方が安全です。ただし、まれな特殊なケースで必要ではない場合は、参照をstrict = falseに設定してこのエラーを取り除くことができます。

続行する最善の方法は何ですか?このstrict=falseを設定しようとする必要がありますか、または要求サービスを注入するのではなく、必要な関数を呼び出すたびにコントローラーを介してサービスに渡しますか?

他の可能性は、カーネルをインジェクトしてそこから取得することですが、私のサービスでは@routerと@requestのみを使用しているため、カーネル全体をインジェクトするのは非合理的です。

51
Tony Bogdanov

公式文書に書かれていることについて誤解があったのではないかと思います。ほとんどの場合、サービス要素のscope="request"属性を使用してリクエストを直接挿入する必要があります。これにより、スコープの拡大がなくなります。

<service 
    id="zayso_core.openid.rpx" 
    class="Zayso\CoreBundle\Component\OpenidRpx" public="true" scope="request">

またはyml

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

Twigエクステンションなど、コンテナをインジェクトする必要がある特定の特別な場合のみです。

また、スコープのページでもカーネルについて言及されていません。カーネルの注入は、コンテナの注入よりも(概念的に)はるかに悪いです。

更新:S2.4以降では、以下の@Blowskiの回答を使用してください。

31
Cerad

Symfony 2.4では、これが変更されました。これで、「request_stack」サービスを注入できます。

例えば:

use Symfony\Component\HttpFoundation\RequestStack;

class MyService
{

    protected $request;

    public function setRequest(RequestStack $request_stack)
    {
        $this->request = $request_stack->getCurrentRequest();
    }

}

Config.ymlで:

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

完全なドキュメントはこちら: http://symfony.com/blog/new-in-symfony-2-4-the-request-stack

98
Dan Blows

コンテナ全体に依存せず、リクエストスコープを持つ必要がないサービスにリクエストサービスを使用させる最良の方法は、コンテナを取得するRequestInjectorサービスを作成することでした。次に、リクエストオブジェクトを使用したいサービスにそれを注入します

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;

    }
}     

services.ymlの

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

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

NB:この回答は、Symfony 2.0がリリースされた2012年に書き戻され、それが良い方法でした!これ以上降格しないでください:)


今日、私は同じ問題を自分で経験したので、ここに私の5セントがあります。 公式ドキュメント によると、通常、requestをサービスに挿入する必要はありません。サービスクラスでは、kernelコンテナを渡すことができます(インジェクトは大きなオーバーヘッドではないようですが)。次のようにrequestにアクセスします。

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;
}

このコードは、CLIでサービスにアクセスするとき(たとえば、単体テスト中)でも正常に機能しています。

5
Anton Babenko

私が見つけた方法は、おそらくそれが最善の方法ではないことを確信しています(推奨されないこともあります)、要求サービスを合成として定義することです。

編集:確かに、これは推奨されません。スコープの健全性チェックを無効にするためです。このスレッドには、Symfonyがその例外をスローする理由の説明が含まれています: http://groups.google .com/group/symfony-devs/browse_thread/thread/a7207406c82ef07a/e2626c00f5cb9749

あなたのservices.xml

<service id="request" synthetic="true" />

<service id="my_service" class="......">
    <argument type="service" id="request" />
</service>

docsに従って、サービスをリクエストスコープに配置するか、単にサービスコンテナを挿入する方が良いでしょう。

4
simshaun

RequestStackを直接使用できない場合は、RequestStackを使用して現在の要求を返すファクトリサービスを作成できます。

# services.yml
app.request:
    class: Symfony\Component\HttpFoundation\RequestStack
    factory: [ @request_stack, getCurrentRequest ]

その後、app.requestサービスを使用して現在のリクエストにアクセスできます。

4
mloberg

リクエストを設定するのではなく、リクエストを取得することに集中することがより重要だと思います。ゲッターを使用することを除いて、@ Blowskiのソリューションと同様のことを行います。これは 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
    }
}

そして、services.yml設定ファイル。

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

これで、正しいリクエストを取得していることを常に確認でき、リクエストの設定/再設定について心配する必要がなくなりました。

1
GreeKatrina

currentRequestを直接注入する別の方法:

セッター注入:

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

またはコンストラクター注入:

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