Types of Injection

Making a class's dependencies explicit and requiring that they be injected into it is a good way of making a class more reusable, testable and decoupled from others.

クラスの依存関係を明示的に作成し、依存関係を依存関係に注入することを要求することは、クラスをより再利用可能でテスト可能にし、他のクラスから切り離す良い方法です。

There are several ways that the dependencies can be injected. Each injection point has advantages and disadvantages to consider, as well as different ways of working with them when using the service container.

依存関係を注入する方法はいくつかあります。各インジェクション ポイントには、考慮すべき長所と短所があり、サービス コンテナーを使用する際にそれらを操作するさまざまな方法があります。

Constructor Injection

The most common way to inject dependencies is via a class's constructor. To do this you need to add an argument to the constructor signature to accept the dependency:

依存関係を注入する最も一般的な方法は、クラスのコンストラクターを使用することです。これを行うには、コンストラクター シグネチャに引数を追加して、依存関係を受け入れる必要があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Mail/NewsletterManager.php
namespace App\Mail;

// ...
class NewsletterManager
{
    private $mailer;

    public function __construct(MailerInterface $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

You can specify what service you would like to inject into this in the service container configuration:

これに注入するサービスをサービス コンテナー構成で指定できます。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
# config/services.yaml
services:
    # ...

    App\Mail\NewsletterManager:
        arguments: ['@mailer']

Tip

ヒント

Type hinting the injected object means that you can be sure that a suitable dependency has been injected. By type-hinting, you'll get a clear error immediately if an unsuitable dependency is injected. By type hinting using an interface rather than a class you can make the choice of dependency more flexible. And assuming you only use methods defined in the interface, you can gain that flexibility and still safely use the object.

注入されたオブジェクトをヒントするタイプは、適切な依存関係が注入されたことを確認できることを意味します。タイプヒンティングにより、不適切な依存関係が注入された場合、すぐに明確なエラーが発生します。クラスではなくインターフェイスを使用した型ヒントにより、依存関係の選択をより柔軟にすることができます。また、インターフェイスで定義されたメソッドのみを使用すると仮定すると、その柔軟性が得られ、オブジェクトを安全に使用できます。

There are several advantages to using constructor injection:

コンストラクター インジェクションを使用することには、いくつかの利点があります。
  • If the dependency is a requirement and the class cannot work without it then injecting it via the constructor ensures it is present when the class is used as the class cannot be constructed without it.
    依存関係が要件であり、それなしではクラスが機能しない場合は、コンストラクターを介してそれを注入することで、クラスが使用されるときにそれが存在することを保証します。
  • The constructor is only ever called once when the object is created, so you can be sure that the dependency will not change during the object's lifetime.
    コンストラクターは、オブジェクトの作成時に一度だけ呼び出されるため、オブジェクトの存続期間中に依存関係が変更されないことを確認できます。

These advantages do mean that constructor injection is not suitable for working with optional dependencies. It is also more difficult to use in combination with class hierarchies: if a class uses constructor injection then extending it and overriding the constructor becomes problematic.

これらの利点は、コンストラクター注入がオプションの依存関係を扱うのに適していないことを意味します。クラス階層と組み合わせて使用​​することもより困難です。クラスがコンストラクター注入を使用する場合、それを拡張してコンストラクターをオーバーライドすると問題が発生します。

Immutable-setter Injection

Another possible injection is to use a method which returns a separate instance by cloning the original service, this approach allows you to make a service immutable:

別のインジェクションとして、元のサービスを複製して別のインスタンスを返すメソッドを使用することもできます。このアプローチにより、サービスを不変にすることができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Mail/NewsletterManager.php
namespace App\Mail;

// ...
use Symfony\Component\Mailer\MailerInterface;

class NewsletterManager
{
    private $mailer;

    /**
     * @required
     * @return static
     */
    public function withMailer(MailerInterface $mailer): self
    {
        $new = clone $this;
        $new->mailer = $mailer;

        return $new;
    }

    // ...
}

In order to use this type of injection, don't forget to configure it:

このタイプのインジェクションを使用するには、忘れずに設定してください。
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
# config/services.yaml
services:
     # ...

     app.newsletter_manager:
         class: App\Mail\NewsletterManager
         calls:
             - withMailer: !returns_clone ['@mailer']

Note

ノート

If you decide to use autowiring, this type of injection requires that you add a @return static docblock in order for the container to be capable of registering the method.

オートワイヤリングを使用する場合、このタイプのインジェクションでは、コンテナがメソッドを登録できるように @return static docblock を追加する必要があります。

This approach is useful if you need to configure your service according to your needs, so, here's the advantages of immutable-setters:

このアプローチは、ニーズに応じてサービスを構成する必要がある場合に役立ちます。したがって、不変セッターの利点は次のとおりです。
  • Immutable setters works with optional dependencies, this way, if you don't need a dependency, the setter doesn't need to be called.
    不変のセッターはオプションの依存関係で動作します。このように、依存関係が必要ない場合は、セッターを呼び出す必要はありません。
  • Like the constructor injection, using immutable setters force the dependency to stay the same during the lifetime of a service.
    コンストラクター注入と同様に、不変のセッターを使用すると、サービスの存続期間中、依存関係が同じままになります。
  • This type of injection works well with traits as the service can be composed, this way, adapting the service to your application requirements is easier.
    このタイプのインジェクションは、サービスを構成できるため、トレイトでうまく機能します。このようにして、サービスをアプリケーション要件に適応させることがより簡単になります。
  • The setter can be called multiple times, this way, adding a dependency to a collection becomes easier and allows you to add a variable number of dependencies.
    セッターは複数回呼び出すことができます。このようにして、依存関係をコレクションに追加することが容易になり、可変数の依存関係を追加できるようになります。

The disadvantages are:

欠点は次のとおりです。
  • As the setter call is optional, a dependency can be null when calling methods of the service. You must check that the dependency is available before using it.
    セッター呼び出しはオプションであるため、サービスのメソッドを呼び出すときに依存関係を null にすることができます。使用する前に、依存関係が利用可能であることを確認する必要があります。
  • Unless the service is declared lazy, it is incompatible with services that reference each other in what are called circular loops.
    サービスが遅延宣言されていない限り、いわゆる循環ループで相互に参照するサービスとは互換性がありません。

Setter Injection

Another possible injection point into a class is by adding a setter method that accepts the dependency:

クラスへの別の可能な注入ポイントは、依存関係を受け入れる setter メソッドを追加することです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Mail/NewsletterManager.php
namespace App\Mail;

// ...
class NewsletterManager
{
    private $mailer;

    /**
     * @required
     */
    public function setMailer(MailerInterface $mailer): void
    {
        $this->mailer = $mailer;
    }

    // ...
}
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
# config/services.yaml
services:
    # ...

    app.newsletter_manager:
        class: App\Mail\NewsletterManager
        calls:
            - setMailer: ['@mailer']

This time the advantages are:

今回のメリットは以下のとおりです。
  • Setter injection works well with optional dependencies. If you do not need the dependency, then do not call the setter.
    セッター注入は、オプションの依存関係でうまく機能します。依存関係が必要ない場合は、セッターを呼び出さないでください。
  • You can call the setter multiple times. This is particularly useful if the method adds the dependency to a collection. You can then have a variable number of dependencies.
    セッターは複数回呼び出すことができます。これは、メソッドが依存関係をコレクションに追加する場合に特に役立ちます。その後、可変数の依存関係を持つことができます。
  • Like the immutable-setter one, this type of injection works well with traits and allows you to compose your service.
    immutable-setter と同様に、このタイプのインジェクションはトレイトとうまく連携し、サービスを構成できます。

The disadvantages of setter injection are:

セッター注入の欠点は次のとおりです。
  • The setter can be called more than once, also long after initialization, so you cannot be sure the dependency is not replaced during the lifetime of the object (except by explicitly writing the setter method to check if it has already been called).
    セッターは、初期化後も何度も呼び出すことができるため、オブジェクトの存続期間中に依存関係が置き換えられないことを確認することはできません (セッター メソッドを明示的に記述して、既に呼び出されているかどうかを確認する場合を除きます)。
  • You cannot be sure the setter will be called and so you need to add checks that any required dependencies are injected.
    セッターが呼び出されることを確認できないため、必要な依存関係が注入されていることを確認する必要があります。

Property Injection

Another possibility is setting public fields of the class directly:

別の可能性は、クラスのパブリック フィールドを直接設定することです。
1
2
3
4
5
6
7
// ...
class NewsletterManager
{
    public $mailer;

    // ...
}
  • YAML
    YAML
  • XML
    XML
  • PHP
    PHP
1
2
3
4
5
6
7
8
# config/services.yaml
services:
    # ...

    app.newsletter_manager:
        class: App\Mail\NewsletterManager
        properties:
            mailer: '@mailer'

There are mainly only disadvantages to using property injection, it is similar to setter injection but with these additional important problems:

プロパティ注入の使用には主に欠点しかありません。セッター注入と似ていますが、次の重要な問題が追加されています。
  • You cannot control when the dependency is set at all, it can be changed at any point in the object's lifetime.
    依存関係がいつ設定されるかを制御することはまったくできません。オブジェクトの存続期間中の任意の時点で変更できます。
  • You cannot use type hinting so you cannot be sure what dependency is injected except by writing into the class code to explicitly test the class instance before using it.
    型ヒントを使用できないため、使用する前にクラス インスタンスを明示的にテストするようにクラス コードに記述しない限り、どの依存関係が注入されているかを確認できません。

But, it is useful to know that this can be done with the service container, especially if you are working with code that is out of your control, such as in a third party library, which uses public properties for its dependencies.

ただし、これはサービス コンテナーで実行できることを知っておくと便利です。特に、依存関係にパブリック プロパティを使用するサード パーティ ライブラリなど、制御できないコードを操作している場合は便利です。