کانتینر سرویس
معرفی
کانتینر سرویس لاراول یک ابزار قدرتمند برای مدیریت وابستگی های کلاس و انجام تزریق وابستگی است. تزریق وابستگی یک عبارت فانتزی است که اساساً به این معنی است: وابستگیهای کلاس از طریق سازنده یا در برخی موارد متدهای «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
پرتاب می شود.