Readouble

Laravel 6.x サービスコンテナ

イントロダクションIntroduction

Laravelのサービスコンテナは、クラス間の依存を管理する強力な管理ツールです。依存注入というおかしな言葉は主に「コンストラクターか、ある場合にはセッターメソッドを利用し、あるクラスをそれらに依存しているクラスへ外部から注入する」という意味で使われます。The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods.

シンプルな例を見てみましょう。Let's look at a simple example:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\User;

class UserController extends Controller
{
    /**
     * ユーザーリポジトリの実装
     *
     * @var UserRepository
     */
    protected $users;

    /**
     * 新しいコントローラインスタンスの生成
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * 指定ユーザーのプロフィール表示
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        $user = $this->users->find($id);

        return view('user.profile', ['user' => $user]);
    }
}

この例中で、UserControllerはデータソースからユーザーを取得する必要があります。そのため、ユーザーを取得できるサービスを注入しています。このようなコンテキストでは、データベースからユーザー情報を取得するために、ほとんどの場合でEloquentが使われるでしょう。しかしながら、リポジトリは外部から注入されているため、他の実装へ簡単に交換できます。さらに、「モック」することも簡単ですし、アプリケーションのテストでUserRepositoryのダミー実装を作成することもできます。In this example, the UserController needs to retrieve users from a data source. So, we will inject a service that is able to retrieve users. In this context, our UserRepository most likely uses Eloquent[/docs/{{version}}/eloquent] to retrieve user information from the database. However, since the repository is injected, we are able to easily swap it out with another implementation. We are also able to easily "mock", or create a dummy implementation of the UserRepository when testing our application.

パワフルで大きなアプリケーションを構築するのと同時に、Laravelコア自体に貢献するためにも、Laravelのサービスコンテナを深く理解することは重要です。A deep understanding of the Laravel service container is essential to building a powerful, large application, as well as for contributing to the Laravel core itself.

結合Binding

結合の基礎Binding Basics

ほとんどのサービスコンテナの結合は、サービスプロバイダの中で行われています。そのため、ほとんどの例はコンテナ中で使用される状況をデモンストレートしています。Almost all of your service container bindings will be registered within service providers[/docs/{{version}}/providers], so most of these examples will demonstrate using the container in that context.

lightbulb">Tip!! インターフェイスに依存していないのであれば、コンテナでクラスを結合する必要はありません。コンテナにオブジェクトを結合する方法を指示する必要はなく、リフレクションを使用してそのオブジェクトを自動的に依存解決します。{tip} There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects, since it can automatically resolve these objects using reflection.

シンプルな結合Simple Bindings

サービスプロバイダの中からは、いつでも$this->appプロパティにより、コンテナへアクセスできます。bindメソッドへ登録したいクラス名かインターフェイス名と、クラスのインスタンスを返す「クロージャ」を引数として渡せば、結合を登録できます。Within a service provider, you always have access to the container via the $this->app property. We can register a binding using the bind method, passing the class or interface name that we wish to register along with a Closure that returns an instance of the class:

$this->app->bind('HelpSpot\API', function ($app) {
    return new \HelpSpot\API($app->make('HttpClient'));
});

コンテナ自身がリゾルバの引数として渡されることに注目してください。これで構築中の依存を解決するためにも、コンテナを利用できます。Note that we receive the container itself as an argument to the resolver. We can then use the container to resolve sub-dependencies of the object we are building.

シングルトン結合Binding A Singleton

singletonメソッドは、クラスやインターフェイスが一度だけ依存解決されるようにコンテナに登録します。一度シングルトン結合が解決されるとそれ以降、この結合が参照されるたび、コンテナは同じオブジェクトインスタンスを返します。The singleton method binds a class or interface into the container that should only be resolved one time. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container:

$this->app->singleton('HelpSpot\API', function ($app) {
    return new \HelpSpot\API($app->make('HttpClient'));
});

インスタンス結合Binding Instances

すでに存在するオブジェクトのインスタンスをinstanceメソッドを用いて、コンテナに結合することもできます。指定されたインスタンスが、以降のコンテナで呼び出されるたびに返されます。You may also bind an existing object instance into the container using the instance method. The given instance will always be returned on subsequent calls into the container:

$api = new \HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);

プリミティブ結合Binding Primitives

クラスでは依存注入により、インスタンスだけでなく、整数のようなプリミティブな値を受け取る必要もあります。それが必要なクラスへ、どんな値でも、コンテキストによる結合を使用すれば簡単に実現できます。Sometimes you may have a class that receives some injected classes, but also needs an injected primitive value such as an integer. You may easily use contextual binding to inject any value your class may need:

$this->app->when('App\Http\Controllers\UserController')
          ->needs('$variableName')
          ->give($value);

インターフェイスと実装の結合Binding Interfaces To Implementations

サービスコンテナーのとても強力な機能は、インターフェイスを指定された実装に結合できることです。たとえばEventPusherインターフェイスとRedisEventPusher実装があるとしましょう。このインターフェイスのRedisEventPusherを実装し終えたら、以下のようにサービスコンテナに登録できます。A very powerful feature of the service container is its ability to bind an interface to a given implementation. For example, let's assume we have an EventPusher interface and a RedisEventPusher implementation. Once we have coded our RedisEventPusher implementation of this interface, we can register it with the service container like so:

$this->app->bind(
    'App\Contracts\EventPusher',
    'App\Services\RedisEventPusher'
);

上記の例は、EventPusherの実装クラスが必要な時に、コンテナがRedisEventPusherを注入するという意味です。ではコンストラクタか、もしくはサービスコンテナが依存を注入できる場所で、EventPusherインターフェイスをタイプヒントで指定してみましょう。This statement tells the container that it should inject the RedisEventPusher when a class needs an implementation of EventPusher. Now we can type-hint the EventPusher interface in a constructor, or any other location where dependencies are injected by the service container:

use App\Contracts\EventPusher;

/**
 * 新しいインスタンスの生成
 *
 * @param  EventPusher  $pusher
 * @return void
 */
public function __construct(EventPusher $pusher)
{
    $this->pusher = $pusher;
}

コンテキストによる結合Contextual Binding

時には、同じインターフェイスを使用した2つのクラスがあり、クラスごとに異なった実装を注入しなくてはならない場合もあるでしょう。たとえば、2つのコントローラが異なったIlluminate\Contracts\Filesystem\Filesystem契約の実装に依存している場合です。Laravelでは、このような振る舞いの定義をシンプルで、読み書きしやすくしています。Sometimes you may have two classes that utilize the same interface, but you wish to inject different implementations into each class. For example, two controllers may depend on different implementations of the Illuminate\Contracts\Filesystem\Filesystem contract[/docs/{{version}}/contracts]. Laravel provides a simple, fluent interface for defining this behavior:

use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when([VideoController::class, UploadController::class])
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });

タグ付けTagging

一連の「カテゴリー」の結合を全部解決する必要がある場合も存在するでしょう。たとえば、異なった多くのReportインターフェイスの実装配列を受け取る、レポート収集プログラム(aggregator)を構築しているとしましょう。Reportの実装を登録後、それらをtagメソッドでタグ付けできます。Occasionally, you may need to resolve all of a certain "category" of binding. For example, perhaps you are building a report aggregator that receives an array of many different Report interface implementations. After registering the Report implementations, you can assign them a tag using the tag method:

$this->app->bind('SpeedReport', function () {
    //
});

$this->app->bind('MemoryReport', function () {
    //
});

$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

サービスにタグを付けてしまえば、taggedメソッドで簡単に全部解決できます。Once the services have been tagged, you may easily resolve them all via the tagged method:

$this->app->bind('ReportAggregator', function ($app) {
    return new ReportAggregator($app->tagged('reports'));
});

結合の拡張Extending Bindings

extendメソッドでサービスの解決結果を修正できます。たとえば、あるサービスが解決されたときに、そのサービスをデコレート、もしくは設定するために追加のコードを実行できます。extendメソッドは唯一引数としてクロージャを受け取り、修正したサービスを返します。クロージャは依存解決されたサービスとコンテナインスタンスを受け取ります。The extend method allows the modification of resolved services. For example, when a service is resolved, you may run additional code to decorate or configure the service. The extend method accepts a Closure, which should return the modified service, as its only argument. The Closure receives the service being resolved and the container instance:

$this->app->extend(Service::class, function ($service, $app) {
    return new DecoratedService($service);
});

依存解決Resolving

makeメソッドThe make Method

コンテナによりクラスインスタンスを依存解決する場合は、makeメソッドを使います。makeメソッドには、依存解決したいクラスかインターフェイスの名前を引数として渡します。You may use the make method to resolve a class instance out of the container. The make method accepts the name of the class or interface you wish to resolve:

$api = $this->app->make('HelpSpot\API');

もし、$app変数へアクセスできない場所で依存解決したい場合は、グローバルなresolveヘルパが使えます。If you are in a location of your code that does not have access to the $app variable, you may use the global resolve helper:

$api = resolve('HelpSpot\API');

依存しているクラスが、コンテナにより解決できない場合は、makeWithメソッドへ連想配列を渡すことにより注入できます。If some of your class' dependencies are not resolvable via the container, you may inject them by passing them as an associative array into the makeWith method:

$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);

自動注入Automatic Injection

一番重要な依存解決方法は、コンテナによる依存解決が行われるクラスのコンストラクタで、シンプルに依存を「タイプヒント」で記述するやりかたです。これはコントローライベントリスナミドルウェアなどで利用できます。さらに、キュージョブhandleメソッドでも、依存をタイプヒントで指定できます。実践において、コンテナによりオブジェクトの解決が行われるのは、これが一番多くなります。Alternatively, and importantly, you may "type-hint" the dependency in the constructor of a class that is resolved by the container, including controllers[/docs/{{version}}/controllers], event listeners[/docs/{{version}}/events], middleware[/docs/{{version}}/middleware], and more. Additionally, you may type-hint dependencies in the handle method of queued jobs[/docs/{{version}}/queues]. In practice, this is how most of your objects should be resolved by the container.

たとえば、コントローラのコンストラクタで、アプリケーションにより定義されたリポジトリをタイプヒントしてみましょう。このリポジトリは、自動的に依存解決され、クラスへ注入されます。For example, you may type-hint a repository defined by your application in a controller's constructor. The repository will automatically be resolved and injected into the class:

<?php

namespace App\Http\Controllers;

use App\Users\Repository as UserRepository;

class UserController extends Controller
{
    /**
     * Userリポジトリインスタンス
     */
    protected $users;

    /**
     * 新しいコントローラインスタンスの生成
     *
     * @param  UserRepository  $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }

    /**
     * 指定されたIDのユーザー表示
     *
     * @param  int  $id
     * @return Response
     */
    public function show($id)
    {
        //
    }
}

コンテナイベントContainer Events

コンテナはオブジェクトの依存解決時に毎回イベントを発行します。このイベントは、resolvingを使用して購読できます。The service container fires an event each time it resolves an object. You may listen to this event using the resolving method:

$this->app->resolving(function ($object, $app) {
    // どんなタイプのオブジェクトをコンテナが解決した場合でも呼び出される
});

$this->app->resolving(\HelpSpot\API::class, function ($api, $app) {
    // "HelpSpot\API"クラスのオブジェクトをコンテナが解決した場合に呼び出される
});

ご覧の通り、依存解決対象のオブジェクトがコールバックに渡され、最終的に取得元へ渡される前に追加でオブジェクトのプロパティをセットできます。As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer.

PSR-11PSR-11

Laravelのサービスコンテナは、PSR-11インターフェイスを持っています。これにより、Laravelコンテナのインスタンスを取得するために、PSR-11コンテナインターフェイスをタイプヒントで指定できます。Laravel's service container implements the PSR-11[https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md] interface. Therefore, you may type-hint the PSR-11 container interface to obtain an instance of the Laravel container:

use Psr\Container\ContainerInterface;

Route::get('/', function (ContainerInterface $container) {
    $service = $container->get('Service');

    //
});

指定された識別子を解決できない場合は例外が投げられます。識別子に何も結合されていない場合は、Psr\Container\NotFoundExceptionInterfaceのインスタンスが例外として投げられます。識別子が結合されているが、依存解決できない場合は、Psr\Container\ContainerExceptionInterfaceのインスタンスが投げられます。An exception is thrown if the given identifier can't be resolved. The exception will be an instance of Psr\Container\NotFoundExceptionInterface if the identifier was never bound. If the identifier was bound but was unable to be resolved, an instance of Psr\Container\ContainerExceptionInterface will be thrown.

章選択

設定

明暗テーマ
light_mode
dark_mode
brightness_auto システム設定に合わせる
テーマ選択
photo_size_select_actual デフォルト
photo_size_select_actual モノクローム(白黒)
photo_size_select_actual Solarized風
photo_size_select_actual GitHub風(青ベース)
photo_size_select_actual Viva(黄緑ベース)
photo_size_select_actual Happy(紫ベース)
photo_size_select_actual Mint(緑ベース)
コードハイライトテーマ選択

明暗テーマごとに、コードハイライトのテーマを指定できます。

テーマ配色確認
スクリーン表示幅
640px
80%
90%
100%

768px以上の幅があるときのドキュメント部分表示幅です。

インデント
無し
1rem
2rem
3rem
原文確認
原文を全行表示
原文を一行ずつ表示
使用しない

※ 段落末のEボタンへカーソルオンで原文をPopupします。

Diff表示形式
色分けのみで区別
行頭の±で区別
削除線と追記で区別

※ [tl!…]形式の挿入削除行の表示形式です。

テストコード表示
両コード表示
Pestのみ表示
PHPUnitのみ表示
OS表示
全OS表示
macOSのみ表示
windowsのみ表示
linuxのみ表示
和文変換

対象文字列と置換文字列を半角スペースで区切ってください。(最大5組各10文字まで)

本文フォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

コードフォント

総称名以外はCSSと同様に、"〜"でエスケープしてください。

保存内容リセット

localStrageに保存してある設定項目をすべて削除し、デフォルト状態へ戻します。

ヘッダー項目移動

キーボード操作