نسخه:

کانتینر سرویس

معرفی

کانتینر سرویس لاراول یک ابزار قدرتمند برای مدیریت وابستگی های کلاس و انجام تزریق وابستگی است. تزریق وابستگی یک عبارت فانتزی است که اساساً به این معنی است: وابستگی‌های کلاس از طریق سازنده یا در برخی موارد متدهای «setter» به کلاس «تزریق» می‌شوند.

بیایید به یک مثال ساده نگاه کنیم:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\User;
 
class UserController extends Controller
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users;
 
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
 
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$user = $this->users->find($id);
 
return view('user.profile', ['user' => $user]);
}
}

در این مثال، UserController نیاز به بازیابی کاربران از یک منبع داده است. بنابراین، ما سرویسی را تزریق خواهیم کرد که قادر به بازیابی کاربران باشد. در این زمینه، ما UserRepository به احتمال زیاد از Eloquent برای بازیابی اطلاعات کاربر از پایگاه داده استفاده می کنیم. با این حال، از آنجایی که مخزن تزریق می شود، می توانیم به راحتی آن را با پیاده سازی دیگری تعویض کنیم. ما همچنین می‌توانیم به راحتی «تقلید» کنیم، یا یک پیاده‌سازی ساختگی از آن UserRepository در هنگام آزمایش برنامه‌مان ایجاد کنیم.

درک عمیق کانتینر سرویس لاراول برای ساختن یک برنامه قدرتمند و بزرگ و همچنین برای کمک به هسته لاراول ضروری است.

الزام آور

مبانی صحافی

تقریباً تمام اتصالات کانتینر خدمات شما در ارائه دهندگان خدمات ثبت می شود ، بنابراین بیشتر این نمونه ها استفاده از کانتینر را در آن زمینه نشان می دهند.

اگر کلاس ها به هیچ واسطی وابسته نباشند، نیازی به اتصال کلاس ها به کانتینر نیست. ظرف نیازی به آموزش نحوه ساخت این اشیاء ندارد، زیرا می تواند به طور خودکار این اشیاء را با استفاده از بازتاب حل کند.

اتصالات ساده

در یک ارائه دهنده خدمات، شما همیشه از طریق دارایی به کانتینر دسترسی دارید $this->app . ما می‌توانیم با استفاده از bind متد، یک binding را ثبت کنیم، کلاس یا نام رابطی را که می‌خواهیم ثبت کنیم همراه با یک Closure نمونه از کلاس را ارسال کنیم:

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

توجه داشته باشید که ما خود ظرف را به عنوان آرگومان برای حل کننده دریافت می کنیم. سپس می‌توانیم از کانتینر برای رفع وابستگی‌های فرعی شیئی که می‌سازیم استفاده کنیم.

Binding A Singleton

متد singleton یک کلاس یا اینترفیس را به کانتینر متصل می کند که فقط یک بار باید حل شود. پس از حل شدن یک اتصال تکی، همان نمونه شیء در فراخوانی های بعدی به کانتینر برگردانده می شود:

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

موارد الزام آور

همچنین می‌توانید یک نمونه شی موجود را با استفاده از روش به کانتینر متصل کنید instance . نمونه داده شده همیشه در تماس های بعدی به کانتینر برگردانده می شود:

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

اتصال رابط به پیاده سازی

یکی از ویژگی های بسیار قدرتمند کانتینر سرویس، توانایی آن در اتصال یک رابط به یک پیاده سازی معین است. EventPusher به عنوان مثال، فرض کنید یک رابط و یک پیاده سازی داریم RedisEventPusher . هنگامی که پیاده سازی خود را از این رابط کدگذاری کردیم RedisEventPusher ، می توانیم آن را در کانتینر سرویس مانند زیر ثبت کنیم:

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

این دستور به کانتینر می‌گوید که باید در RedisEventPusher زمانی که یک کلاس به پیاده‌سازی نیاز دارد، آن را تزریق کند EventPusher . EventPusher اکنون می‌توانیم اینترفیس را در یک سازنده یا هر مکان دیگری که وابستگی‌ها توسط کانتینر سرویس تزریق می‌شود، تایپ کنیم :

use App\Contracts\EventPusher;
 
/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}

پیوند متنی

گاهی اوقات ممکن است دو کلاس داشته باشید که از یک رابط استفاده می کنند، اما می خواهید پیاده سازی های مختلفی را به هر کلاس تزریق کنید. برای مثال، دو کنترل کننده ممکن است به پیاده سازی های مختلف قرارداد وابسته Illuminate\Contracts\Filesystem\Filesystem باشند . لاراول یک رابط ساده و روان برای تعریف این رفتار ارائه می دهد:

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

پیوندهای اولیه

گاهی اوقات ممکن است کلاسی داشته باشید که برخی از کلاس های تزریقی را دریافت می کند، اما به یک مقدار اولیه تزریق شده مانند یک عدد صحیح نیز نیاز دارد. می‌توانید به راحتی از پیوند متنی برای تزریق هر مقداری که کلاس شما به آن نیاز دارد استفاده کنید:

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

گاهی اوقات یک کلاس ممکن است به آرایه ای از نمونه های برچسب گذاری شده بستگی داشته باشد. با استفاده از این giveTagged روش، می توانید به راحتی تمام اتصالات ظرف را با آن برچسب تزریق کنید:

$this->app->when(ReportAggregator::class)
->needs('$reports')
->giveTagged('reports');

واریادیک های صحافی تایپ شده

گاهی اوقات ممکن است کلاسی داشته باشید که آرایه ای از اشیاء تایپ شده را با استفاده از آرگومان سازنده variadic دریافت می کند:

class Firewall
{
protected $logger;
protected $filters;
 
public function __construct(Logger $logger, Filter ...$filters)
{
$this->logger = $logger;
$this->filters = $filters;
}
}

با استفاده از اتصال متنی، می‌توانید این وابستگی را با ارائه give روش بسته شدن که آرایه‌ای از Filter نمونه‌های حل‌شده را برمی‌گرداند، برطرف کنید:

$this->app->when(Firewall::class)
->needs(Filter::class)
->give(function ($app) {
return [
$app->make(NullFilter::class),
$app->make(ProfanityFilter::class),
$app->make(TooLongFilter::class),
];
});

برای راحتی، می‌توانید آرایه‌ای از نام‌های کلاس را نیز ارائه کنید تا هر زمان که به نمونه‌هایی Firewall نیاز داشت ، توسط کانتینر حل شود Filter :

$this->app->when(Firewall::class)
->needs(Filter::class)
->give([
NullFilter::class,
ProfanityFilter::class,
TooLongFilter::class,
]);

وابستگی های تگ متغیر

گاهی اوقات یک کلاس ممکن است وابستگی متغیری داشته باشد که به صورت تایپ به عنوان یک کلاس مشخص ( Report ...$reports ) مشخص می شود. با استفاده از needs و giveTagged متدها، می‌توانید به راحتی تمام اتصالات ظرف را با آن برچسب برای وابستگی داده شده تزریق کنید:

$this->app->when(ReportAggregator::class)
->needs(Report::class)
->giveTagged('reports');

برچسب زدن

گاهی اوقات، ممکن است لازم باشد همه یک «دسته» خاصی از الزام آور را حل کنید. به عنوان مثال، شاید شما در حال ساخت یک جمع‌آوری گزارش هستید که آرایه‌ای از Report پیاده‌سازی‌های مختلف رابط را دریافت می‌کند. پس از ثبت Report پیاده سازی ها، می توانید با استفاده از روش زیر به آنها تگ اختصاص دهید tag :

$this->app->bind('SpeedReport', function () {
//
});
 
$this->app->bind('MemoryReport', function () {
//
});
 
$this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');

هنگامی که سرویس ها برچسب گذاری شدند، می توانید به راحتی همه آنها را از طریق tagged روش حل کنید:

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

گسترش اتصالات

این extend روش امکان اصلاح سرویس های حل شده را می دهد. به عنوان مثال، هنگامی که یک سرویس حل می شود، می توانید کد اضافی را برای تزئین یا پیکربندی سرویس اجرا کنید. این extend روش یک Closure را می پذیرد که باید سرویس تغییر یافته را به عنوان تنها آرگومان خود برگرداند. بسته شدن سرویس در حال حل شدن و نمونه کانتینر را دریافت می کند:

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

حل و فصل

روش make _

می‌توانید از این make متد برای حل یک نمونه کلاس از کانتینر استفاده کنید. متد make نام کلاس یا رابطی را که می خواهید حل کنید می پذیرد:

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

اگر در مکانی از کد خود هستید که به متغیر دسترسی ندارد $app ، می توانید از resolve کمک کننده جهانی استفاده کنید:

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

اگر برخی از وابستگی های کلاس شما از طریق کانتینر قابل حل نیستند، می توانید آنها را با ارسال آنها به عنوان یک آرایه انجمنی به متد تزریق کنید makeWith :

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

تزریق خودکار

روش دیگر، و مهمتر از آن، می‌توانید وابستگی را در سازنده کلاسی که توسط کانتینر حل می‌شود، از جمله کنترل‌کننده‌ها ، شنونده‌های رویداد ، میان‌افزار و غیره «تایپ کنید». handle علاوه بر این، می‌توانید وابستگی‌هایی را در روش مشاغل در صف تایپ کنید . در عمل، این گونه است که بیشتر اشیاء شما باید توسط ظرف حل شوند.

برای مثال، می‌توانید یک مخزن تعریف شده توسط برنامه شما در سازنده کنترلر را تایپ کنید. مخزن به طور خودکار حل می شود و به کلاس تزریق می شود:

<?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)
{
//
}
}

رویدادهای کانتینری

کانتینر سرویس هر بار که یک شی را حل می کند یک رویداد را شلیک می کند. می توانید با استفاده از resolving روش زیر به این رویداد گوش دهید:

$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"...
});

همانطور که می بینید، شی ای که حل می شود به callback ارسال می شود و به شما این امکان را می دهد که هر ویژگی اضافی را روی شی قبل از اینکه به مصرف کننده اش داده شود، تنظیم کنید.

PSR-11

کانتینر سرویس لاراول رابط PSR-11 را پیاده سازی می کند . بنابراین، برای به دست آوردن نمونه ای از کانتینر لاراول، می توانید به رابط کانتینر PSR-11 اشاره کنید:

use Psr\Container\ContainerInterface;
 
Route::get('/', function (ContainerInterface $container) {
$service = $container->get('Service');
 
//
});

اگر شناسه داده شده قابل حل نباشد، یک استثنا ایجاد می شود. Psr\Container\NotFoundExceptionInterface اگر شناسه هرگز محدود نشده باشد، استثنا خواهد بود . اگر شناسه محدود بود اما قابل حل نبود، یک نمونه از Psr\Container\ContainerExceptionInterface پرتاب می شود.