کانتینر سرویس
معرفی
کانتینر سرویس لاراول یک ابزار قدرتمند برای مدیریت وابستگی های کلاس است. Dependency Injection یک کلمه فانتزی است که در اصل به این معنی است: وابستگی های کلاس از طریق سازنده یا در برخی موارد متدهای "setter" به کلاس "تزریق" می شوند.
بیایید به یک مثال ساده نگاه کنیم:
<?php namespace App\Handlers\Commands; use App\User;use App\Commands\PurchasePodcastCommand;use Illuminate\Contracts\Mail\Mailer; class PurchasePodcastHandler { /** * The mailer implementation. */ protected $mailer; /** * Create a new instance. * * @param Mailer $mailer * @return void */ public function __construct(Mailer $mailer) { $this->mailer = $mailer; } /** * Purchase a podcast. * * @param PurchasePodcastCommand $command * @return void */ public function handle(PurchasePodcastCommand $command) { // } }
در این مثال،
PurchasePodcast
کنترل کننده فرمان باید هنگام خرید پادکست ایمیل ارسال کند.
بنابراین، ما سرویسی را
تزریق
خواهیم کرد که قادر به ارسال ایمیل باشد.
از آنجایی که سرویس تزریق می شود، می توانیم به راحتی آن را با پیاده سازی دیگری تعویض کنیم.
ما همچنین میتوانیم به راحتی "تقلید" کنیم، یا یک پیادهسازی ساختگی از میلر هنگام آزمایش برنامه خود ایجاد کنیم.
درک عمیق کانتینر سرویس لاراول برای ساختن یک برنامه قدرتمند و بزرگ و همچنین برای کمک به هسته لاراول ضروری است.
استفاده پایه
الزام آور
تقریباً تمام اتصالات کانتینر خدمات شما در
ارائه دهندگان خدمات
ثبت می شود ، بنابراین همه این مثال ها استفاده از کانتینر را در آن زمینه نشان می دهد.
با این حال، اگر به نمونه ای از کانتینر در جای دیگری از برنامه خود مانند کارخانه نیاز دارید، می توانید قرارداد را تایپ کنید
Illuminate\Contracts\Container\Container
و نمونه ای از کانتینر برای شما تزریق می شود.
از طرف دیگر، می توانید از
App
نما برای دسترسی به ظرف استفاده کنید.
ثبت یک Resolver پایه
در یک ارائه دهنده خدمات، شما همیشه از طریق متغیر نمونه به کانتینر دسترسی دارید
$this->app
.
چندین روش وجود دارد که کانتینر سرویس میتواند وابستگیها را ثبت کند، از جمله تماسهای بسته بستن و رابطهای اتصال به پیادهسازیها. ابتدا، ما تماس های بسته شدن را بررسی می کنیم. یک Closure Resoler با یک کلید (معمولاً نام کلاس) و یک Closure که مقداری مقدار را برمی گرداند در ظرف ثبت می شود:
$this->app->bind('FooBar', function($app){ return new FooBar($app['SomethingElse']);});
ثبت نام Singleton
گاهی اوقات، ممکن است بخواهید چیزی را به کانتینر متصل کنید که فقط یک بار باید حل شود، و همان نمونه باید در تماسهای بعدی به کانتینر برگردانده شود:
$this->app->singleton('FooBar', function($app){ return new FooBar($app['SomethingElse']);});
اتصال یک نمونه موجود به کانتینر
همچنین میتوانید یک نمونه شی موجود را با استفاده از روش به کانتینر متصل کنید
instance
.
نمونه داده شده همیشه در تماس های بعدی به کانتینر برگردانده می شود:
$fooBar = new FooBar(new SomethingElse); $this->app->instance('FooBar', $fooBar);
حل و فصل
راههای مختلفی برای حل مشکل خارج از ظرف وجود دارد.
ابتدا می توانید از
make
روش زیر استفاده کنید:
$fooBar = $this->app->make('FooBar');
ثانیاً، میتوانید از «دسترسی آرایه» در کانتینر استفاده کنید، زیرا
ArrayAccess
رابط PHP را پیادهسازی میکند:
$fooBar = $this->app['FooBar'];
در نهایت، اما مهمتر از همه، میتوانید به سادگی وابستگی در سازنده کلاس را که توسط کانتینر حل میشود، از جمله کنترلکنندهها، شنوندگان رویداد، کارهای صف، فیلترها و موارد دیگر "تایپ" کنید. ظرف به طور خودکار وابستگی ها را تزریق می کند:
<?php namespace App\Http\Controllers; use Illuminate\Routing\Controller;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) { // } }
اتصال رابط به پیاده سازی
تزریق وابستگی های بتن
یکی از ویژگی های بسیار قدرتمند کانتینر سرویس، توانایی آن در اتصال یک رابط به یک پیاده سازی معین است. برای مثال، شاید برنامه ما با سرویس وب Pusher برای ارسال و دریافت رویدادهای بلادرنگ ادغام شود. اگر از Pusher's PHP SDK استفاده می کنیم، می توانیم نمونه ای از سرویس گیرنده Pusher را به یک کلاس تزریق کنیم:
<?php namespace App\Handlers\Commands; use App\Commands\CreateOrder;use Pusher\Client as PusherClient; class CreateOrderHandler { /** * The Pusher SDK client instance. */ protected $pusher; /** * Create a new order handler instance. * * @param PusherClient $pusher * @return void */ public function __construct(PusherClient $pusher) { $this->pusher = $pusher; } /** * Execute the given command. * * @param CreateOrder $command * @return void */ public function execute(CreateOrder $command) { // } }
در این مثال، خوب است که ما وابستگی های کلاس را تزریق می کنیم.
با این حال، ما به شدت با Pusher SDK مرتبط هستیم.
اگر روشهای Pusher SDK تغییر کند یا تصمیم بگیریم که به طور کامل به یک سرویس رویداد جدید سوئیچ کنیم، باید
CreateOrderHandler
کد خود را تغییر دهیم.
برنامه به یک رابط
به منظور "عایق بندی"
CreateOrderHandler
در برابر تغییرات در فشار رویداد، می توانیم یک
EventPusher
رابط و یک
PusherEventPusher
پیاده سازی تعریف کنیم:
<?php namespace App\Contracts; interface EventPusher { /** * Push a new event to all clients. * * @param string $event * @param array $data * @return void */ public function push($event, array $data); }
هنگامی که پیاده سازی خود را از این رابط کدگذاری کردیم
PusherEventPusher
، می توانیم آن را در کانتینر سرویس مانند زیر ثبت کنیم:
$this->app->bind('App\Contracts\EventPusher', 'App\Services\PusherEventPusher');
این به کانتینر میگوید که
PusherEventPusher
وقتی یک کلاس نیاز به پیادهسازی دارد، باید آن را تزریق کند
EventPusher
.
EventPusher
اکنون می توانیم اینترفیس را در سازنده خود
تایپ کنیم :
/** * Create a new order handler instance. * * @param EventPusher $pusher * @return void */public function __construct(EventPusher $pusher){ $this->pusher = $pusher;}
پیوند متنی
گاهی اوقات ممکن است دو کلاس داشته باشید که از یک رابط استفاده می کنند، اما می خواهید پیاده سازی های مختلفی را به هر کلاس تزریق کنید. به عنوان مثال، هنگامی که سیستم ما یک سفارش جدید دریافت می کند، ممکن است بخواهیم رویدادی را از طریق PubNub به جای Pusher ارسال کنیم. لاراول یک رابط ساده و روان برای تعریف این رفتار ارائه می دهد:
$this->app->when('App\Handlers\Commands\CreateOrderHandler') ->needs('App\Contracts\EventPusher') ->give('App\Services\PubNubEventPusher');
برچسب زدن
گاهی اوقات، ممکن است لازم باشد همه یک «دسته» خاصی از الزام آور را حل کنید.
به عنوان مثال، شاید شما در حال ساخت یک جمعآوری گزارش هستید که آرایهای از
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'));});
کاربردهای عملی
لاراول چندین فرصت برای استفاده از کانتینر سرویس برای افزایش انعطاف پذیری و آزمایش پذیری برنامه شما فراهم می کند. یکی از مثالهای اصلی، حل کردن کنترلرها است. همه کنترلکنندهها از طریق کانتینر سرویس حل میشوند، به این معنی که میتوانید وابستگیها را در سازنده کنترلکننده تایپ کنید و آنها به طور خودکار تزریق میشوند.
<?php namespace App\Http\Controllers; use Illuminate\Routing\Controller;use App\Repositories\OrderRepository; class OrdersController extends Controller { /** * The order repository instance. */ protected $orders; /** * Create a controller instance. * * @param OrderRepository $orders * @return void */ public function __construct(OrderRepository $orders) { $this->orders = $orders; } /** * Show all of the orders. * * @return Response */ public function index() { $orders = $this->orders->all(); return view('orders', ['orders' => $orders]); } }
در این مثال،
OrderRepository
کلاس به طور خودکار به کنترلر تزریق می شود.
این به این معنی است که ممکن است هنگام
آزمایش واحد
OrderRepository
، یک "تقلید" به کانتینر متصل شود
، که اجازه می دهد تا تعامل لایه پایگاه داده بدون دردسر ایجاد شود.
نمونه های دیگر استفاده از کانتینر
البته همانطور که در بالا ذکر شد، کنترلرها تنها کلاس هایی نیستند که لاراول از طریق کانتینر سرویس حل می کند. همچنین میتوانید وابستگیهای مربوط به بسته شدن مسیر، فیلترها، کارهای صف، شنوندگان رویداد و موارد دیگر را تایپ کنید. برای نمونه هایی از استفاده از کانتینر سرویس در این زمینه ها، لطفاً به مستندات آنها مراجعه کنید.
رویدادهای کانتینری
ثبت نام شنونده حل و فصل
کانتینر هر بار که یک شی را حل می کند یک رویداد را شلیک می کند.
می توانید با استفاده از
resolving
روش زیر به این رویداد گوش دهید:
$this->app->resolving(function($object, $app){ // Called when container resolves object of any type...}); $this->app->resolving(function(FooBar $fooBar, $app){ // Called when container resolves objects of type "FooBar"...});
شیئی که حل می شود به تماس برگشتی ارسال می شود.