Servicecontainer
Service-Container
Einführung
Laravel Der Service-Container ist ein leistungsstarkes Tool zum Verwalten von Klassenabhängigkeiten und zum Durchführen der Abhängigkeitsinjektion. Der ausgefallene Begriff „Abhängigkeitsinjektion“ bedeutet im Wesentlichen, dass die Abhängigkeiten einer Klasse über den Konstruktor oder in einigen Fällen über die „Setter“-Methode in die Klasse „injiziert“ werden.
Sehen wir uns ein einfaches Beispiel an:
<?php namespace App\Http\Controllers; use App\User; use App\Repositories\UserRepository; use App\Http\Controllers\Controller; class UserController extends Controller{ /** * 用户存储库的实现. * * @var UserRepository */ protected $users; /** * 创建新的控制器实例. * * @param UserRepository $users * @return void */ public function __construct(UserRepository $users) { $this->users = $users; } /** * 显示指定用户的 profile. * * @param int $id * @return Response */ public function show($id) { $user = $this->users->find($id); return view('user.profile', ['user' => $user]); } }
In diesem Beispiel muss der Controller UserController
Benutzer aus der Datenquelle abrufen. Daher müssen wir einen Dienst einführen, der Benutzer erreichen kann. Im aktuellen Kontext verwendet unser UserRepository
höchstwahrscheinlich Eloquent, um die Benutzerinformationen aus der Datenbank abzurufen. Da das Repository jedoch injiziert wird, können wir es problemlos auf eine andere Implementierung umstellen. Der Vorteil dieser Injektionsmethode besteht darin, dass wir beim Schreiben von Tests für unsere Anwendung auch problemlos eine virtuelle Implementierung von UserRepository
„verspotten“ oder erstellen können.
Wenn Sie leistungsstarke Großanwendungen erstellen möchten, ist eines entscheidend: ein tiefes Verständnis des Laravel-Service-Containers. Das Gleiche gilt natürlich auch für die Mitarbeit am Kerncode von Laravel.
Servicebindung
Basisbindung
Aufgrund der Größe Die meisten Benutzerdienstcontainer sind in registriert Dienstanbieter, daher veranschaulichen die meisten der folgenden Beispiele die Verwendung von Containern bei Dienstanbietern.
{tip} Wenn ein Container von keiner Schnittstelle abhängig ist, besteht keine Notwendigkeit, Klassen in diesem Container zu binden. Der Container muss nicht angeben, wie diese Objekte erstellt werden, da er Reflektion verwenden kann, um diese Objekte automatisch aufzulösen.
Einfache Bindung
Bei einem Dienstanbieter können Sie immer über das Attribut $this->app
auf den Container zugreifen. Wir können die Bindung über die bind
-Methode des Containers registrieren. Der erste Parameter der bind
-Methode ist der zu bindende Klassen-/Schnittstellenname, und der zweite Parameter ist ein Closure
, der eine Klasseninstanz zurückgibt:
$this->app->bind('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
Beachten Sie, dass wir den Container selbst als Argument für den Parser akzeptieren. Anschließend können wir den Container verwenden, um die Unterabhängigkeiten des zu erstellenden Objekts aufzulösen. Die Methode
Einen Singleton binden
singleton
bindet eine Klasse oder Schnittstelle in einen Container, der nur einmal aufgelöst wird. Sobald die Singleton-Bindung aufgelöst ist, wird bei nachfolgenden Aufrufen dieselbe Objektinstanz an den Container zurückgegeben:
$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });
Instanzen binden
Sie können auch vorhandene Objektinstanzen mit der Methode instance
an einen Container binden. Die angegebene Instanz wird bei nachfolgenden Aufrufen immer an den Container zurückgegeben:
$api = new HelpSpot\API(new HttpClient); $this->app->instance('HelpSpot\API', $api);
Binding Basic Values
Wenn Sie eine Klasse haben, die nicht nur eine injizierte Klasse akzeptieren, sondern auch injizieren muss ein Grundwert (z. B. eine ganze Zahl). Mithilfe der Kontextbindung können Sie ganz einfach jeden Wert einfügen, den Ihre Klasse benötigt:
$this->app->when('App\Http\Controllers\UserController') ->needs('$variableName') ->give($value);
Binden Sie die Schnittstelle an die Implementierung
Der Service-Container hat A Eine sehr leistungsstarke Funktion ist die Unterstützung für die Bindung einer Schnittstelle an eine bestimmte Implementierung. Zum Beispiel, wenn wir eine EventPusher
-Schnittstelle und eine RedisEventPusher
-Implementierung haben. Sobald wir die EventPusher
-Implementierung der RedisEventPusher
-Schnittstelle geschrieben haben, können wir sie wie folgt im Service-Container registrieren:
$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\RedisEventPusher' );
Dies entspricht der Mitteilung an den Container: wann eine Klasse < implementieren muss 🎜> , sollte mit EventPusher
injiziert werden. Jetzt können wir Typhinweise verwenden, um die RedisEventPusher
-Schnittstelle im Konstruktor oder an einer anderen Stelle einzufügen, an der Abhängigkeiten über den Service-Container eingefügt werden: EventPusher
use App\Contracts\EventPusher; /** * Create a new class instance. * * @param EventPusher $pusher * @return void */ public function __construct(EventPusher $pusher){ $this->pusher = $pusher; }KontextbindungManchmal haben Sie möglicherweise zwei Klassen, die dieselbe Schnittstelle verwenden, Sie möchten jedoch in jede unterschiedliche Implementierungen einfügen. Beispielsweise können sich zwei Controller auf den
-Vertrag verlassen, der eine einfache, elegante Schnittstelle zum Definieren dieses Verhaltens bereitstellt: IlluminateContractsFilesystemFilesystem
use Illuminate\Support\Facades\Storage; use App\Http\Controllers\PhotoController; use App\Http\Controllers\VideoController; use Illuminate\Contracts\Filesystem\Filesystem; $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'); });-Tag Manchmal müssen Sie möglicherweise alle Bindungen unter einer bestimmten „Kategorie“ analysieren. Beispielsweise könnten Sie einen Berichtsaggregator erstellen, der eine Reihe verschiedener
-Schnittstellenimplementierungen empfängt. Nachdem Sie Report
Implementierungen registriert haben, können Sie ihnen mit der Report
-Methode ein Tag zuweisen: tag
$this->app->bind('SpeedReport', function () { // }); $this->app->bind('MemoryReport', function () { // }); $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');Sobald der Dienst markiert ist, können Sie ihn über
zuweisen Methoden, um sie einfach zu analysieren: tagged
$this->app->bind('ReportAggregator', function ($app) { return new ReportAggregator($app->tagged('reports')); });Erweiterungsbindungen
Methoden können aufgelöste Dienste ändern. Nachdem ein Dienst beispielsweise aufgelöst wurde, können Sie zusätzlichen Code hinzufügen, um ihn zu dekorieren oder zu konfigurieren. Die extend
-Methode akzeptiert einen Abschluss, dessen einziger Parameter der Dienst ist, und gibt den geänderten Dienst zurück: extend
$this->app->extend(Service::class, function ($service) { return new DecoratedService($service); });Parse-Instanz
Methoden make
Mit der Methode können Sie eine Klasseninstanz aus dem Container auflösen. Die Methode make
erhält den Namen der Klasse oder Schnittstelle, die Sie auflösen möchten: make
$api = $this->app->make('HelpSpot\API');Wenn sich Ihr Code an einer Stelle befindet, an der auf die Variable
nicht zugegriffen werden kann, können Sie die globale Hilfsfunktion < verwenden 🎜> zum Auflösen: $app
$api = resolve('HelpSpot\API');
resolve
Wenn Klassenabhängigkeiten nicht vom Container aufgelöst werden können, können Sie sie als assoziatives Array als übergeben Methodenparameter-Injektion: $api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);
makeWith
Darüber hinaus und was noch wichtiger ist, können Sie einfach „Typhinweise“ verwenden, um Abhängigkeiten einzufügen, die vom Container im Konstruktor der Klasse aufgelöst werden müssen, einschließlich Controllern und Ereignissen , Warteschlangenaufgaben, Middleware usw. Tatsächlich sollten die meisten Objekte auf diese Weise vom Container aufgelöst werden. Zum Beispiel können Sie im Konstruktor des Controllers einen Repository-Typhinweis hinzufügen, und dann wird das Repository automatisch analysiert und in die Klasse eingefügt: Der Dienstcontainer löst jedes Mal ein Ereignis aus, wenn er ein Objekt analysiert, das Sie verwenden können Die Wie Sie sehen können, wird das analysierte Objekt an die Rückruffunktion übergeben, die es Ihnen ermöglicht, zusätzliche Parameter für das Objekt festzulegen, bevor es an den Aufrufer übergeben wird . Eigenschaften. Der Servicecontainer von Laravel implementiert die PSR-11-Schnittstelle. Daher können Sie den „Schnittstellentyp-Hinweis“ des PSR-11-Containers verwenden, um eine Instanz des Laravel-Containers zu erhalten: Wenn der angegebene Bezeichner nicht aufgelöst werden kann, wird eine Ausnahme ausgelöst. Wenn der Bezeichner nicht gebunden ist, wird eine Automatische Injektion
<?php
namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the user with the given ID.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}
Containerereignisse
resolving
-Methode wartet auf dieses Ereignis: $this->app->resolving(function ($object, $app) {
// Called when container resolves object of any type...
});
$this->app->resolving(HelpSpot\API::class, function ($api, $app) {
// Called when container resolves objects of type "HelpSpot\API"...
});
PSR-11
use Psr\Container\ContainerInterface;
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
//
});
PsrContainerNotFoundExceptionInterface
-Ausnahme ausgelöst. Wenn der Bezeichner gebunden ist, aber nicht aufgelöst werden kann, wird eine PsrContainerContainerExceptionInterface
-Ausnahme ausgelöst.