بخش اول - سرویس کانتینر
Service Container (IoC Container) یکی از قدرتمندترین ابزارها در لاراول هست، وظیفه این سرویس قدرتمند Dependency Injection و Inversion of Control میباشد.
یعنی اگر کلاسی نیاز به یک سرویس یا کلاس دیگهای داشته باشه، لاراول به کمک سرویس کانتینر خودش اون نیاز رو براش فراهم میکنه.
همونطور که گفته شد لاراول به صورت خودکار کلاسهایی که وابستگی ندارن، یا فقط به کلاسهای کامل (concrete) وابستهان رو بدون نیاز به تنظیم دستی و به صورت خودکار resolve بکنه. مثلا:
Route::get('/', function (Service $service) {
dd($service::class);
});وقتی یک کلاس ساده مثل Service به صورت type-hint تعریف شده باشه، کانتینر خودش اون رو instantiate و inject میکنه بدون این که ما جای دیگهای مشخص کنیم.
اما همه این دپندنسی ها به صورت خودکار فراهم نمیشوند، به همین دلیل Service Providerها به کمک ما میآیند.
لاراول به صورت پیشفرض یک پروایدر در مسیر App\Providers\AppServiceProvider.php دارد، شما نیز میتوانید پروایدرهای خود را با دستور زیر در همین مسیر ایجاد کنید:
php artisan make:provider MyServiceProvider
گفته شد که لاراول همیشه نمیتواند یک سرویس را بدون تنظیم قبلی inject کند. اما در چه زمان هایی؟
1- وقتی برای یک interface چند پیاده سازی داریم و میخواهیم مشخص کنیم که لاراول از کدام پیادهسازی استفاده کند.
2- وقتی که یک پکیج برای لاراول توسعه دادهایم و میخواهیم سرویسهایی تعریف کنیم.
در این موارد کافی است در یکی از متدهای boot یا register (بسته به نیاز که بعدا توضیح داده میشود) در سرویس پروایدر خود آن پیادهسازی را به کانتینر bind کنیم:
$this->app->bind(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});در این کد گفته شده زمانی که Transistor نیاز شود، کالبک اجرا شود و یک نمونه از کلاس Transistor ایجاد و به کانتینر اضافه کند.
انواع binding
1- bind
این نوع از binding در مثال قبل گفته شد. و در هر بار درخواست یک سرویس، یک نمونه جدید از آن را ایجاد میکند.
2- singleton
همانطور که از نامش پیداست، تنها یک نمونه از کلاس را میسازد و در هر درخواست سرویس، از نمونه ایجاد شده قبلی استفاده میکند:
$this->app->singleton('RedisClient', function () {
return new RedisClient(config('redis'));
});3- instance
زمانی استفاده میشود که خودمان یک instance آماده از یک آبجکت داشته باشیم و بخواهیم آن را به کانتینر bind کنیم:
$logger = new \App\Services\CustomLogger();
$this->app->instance('logger', $logger);
4- scoped
در اپهایی که هر request (یا queue job) باید نسخه جداگانهای از کلاس داشته باشند ولی در همون request بین کلاسها مشترک باشند.
این نوع برای stateful کردن کلاسهایی مثل کانکشن دیتابیس بسیار مهم است.
$this->app->scoped(MyService::class, function ($app) {
return new MyService(now());
});در پروژههای Laravel Octane یا اپهای multi-tenant یا زمانی که Queue Jobها به ایزولاسیون نیاز دارن کاربرد دارد.
5- bindIf
فقط زمانی bind رو انجام میده که کلاس یا کلیدی با اون نام bind نشده باشه:
$this->app->bindIf(SomeInterface::class, DefaultImplementation::class);
6- singletonIf
مشابه bindIf عمل خواهد کرد با این تفاوت که نمونهای که ایجاد میکند از نوع singleton خواهد بود.
7- scopedIf
مشابه bindIf و singletonIf برای scoped bindigها.
Interface Binding
ما میتوانیم مشخص کنیم که وقتی یک کلاس خاص (یا Interface) خواسته شد، چه پیادهسازی (concrete class) برایش استفاده شود.
فرض کنید:
interface PaymentGateway {
public function charge($amount);
}
class StripeGateway implements PaymentGateway {
public function charge($amount) {
//
}
}در این صورت کافی است در سراسر برنامه از PaymentGateway استفاده کنیم و لاراول خودش تصمیم میگیرد چه چیز را inject کند.
برای این کار کافی است در سرویس پروایدر خود به صورت زیر عمل کنیم:
$this->app->bind(PaymentGateway::class, StripeGateway::class);
در این صورت اگر PaymentGateway نیاز شود، یک نمونه از StripeGateway ساخته و inject میشود.
در قسمتهای بعدی با سایر Injectionها آشنا خواهیم شد.
#laravel #لاراول #فصل_۷
@AmirhDeveloper
.