یکی از مهمترین قسمتهای فریمورک لاراول Service Provider ها هست . یه جورایی میشه گفت مغز لاراول هست . چون همه چیز اول اونجا ثبت میشه .
Service Provider به زبان ساده
هر برنامهای (حتی غیر لاراولی) برای اینکه اجرا بشه باید چندین کلاس و فایل رو لود و اجرا کنه . سرویسهایی مثل کش ، سشن ، کوکی ، دیتابیس و ... از قبل باید لود بشن تا برنامهی ما کار اصلیش رو انجام بده . این لود شدنها توسط قسمت Service Provider ها انجام میشه . این قسمت مسئول پیکربندی و آمادهسازی پروژه هست . توی این پست با این ویژگی بیشتر آشنا میشیم .
لاراول پرووایدر (Provider) های ما رو از کجا فراخوانی میکنه ؟ فایل app.php توی پوشه config رو ببینید ؛ یک آرایهای وجود داره به اسم providers :
خب همونطور که میبینیم پرووایدرهایی مثل Auth , Cache , Cookie و Database اینجا ثبت شدن ! لاراول اول اینها رو لود میکنه و بعد برنامهی اصلی ما که وابسته به این سرویسها هست پردازش میشه . یک پرووایدر مثل Auth رو اگه باز کنین میبینیم که تنظیمات مربوط احراز هویت اونجا ثبت شده . برای کش و کوکی هم همینطور .
آیا ما به پرووایدر شخصی نیاز داریم ؟
اگه ما به کلاس یا سرویسی نیاز داریم که باید توی برنامه همیشه در دسترس باشه باید اون رو یک جایی ثبت کنیم . اگه تعداد کلاسها و سرویسهای ما کم باشن اونها رو میتونیم توی یک پرووایدر به اسم AppServiceProvider ذخیره کنیم . این پرووایدر از قبل وجود داره و ما توی اون میتونیم تنظیمات برنامه رو ثبت یا ویرایش کنیم . و همچنین کلاسها و سرویسها رو رجیستر کنیم . این کلاس توی مسیر app/Providers قرار داره :
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$sms = new SmsNotification;
$sms->setNumber(0911);
$sms->setTemplate('...');
$this->app->instance('SmsNotification', $sms);
}
}
خب اگه تعداد سرویسها ، تنظیمات و کلاسهای ما زیاد بشن چطور ؟
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$sms = new SmsNotification;
$sms->setNumber(0911);
$sms->setTemplate('...');
$this->app->instance('SmsNotification', $sms);
$email = new EmailNotification;
$email->setTemplate('...');
$email->setServer('...');
$this->app->instance('EmailNotification', $email);
$logger = new Logger;
$logger->setDriver('file');
$logger->setRate(30);
$this->app->instance('Logger', $logger);
}
}
همونطور که میبینیم کلاسها و تنظیمات بیربط کنار هم قرار گرفتن خوانایی و انسجام کد پایین اومده . راه درست اینه کلاسها و تنظیماتی که به هم ارتباط ندارن رو از هم جدا کنیم . برای این کار لاراول به ما اجازه میده که پرووایدر شخصی خودمون رو بسازیم .
ساختن یک پرووایدر شخصی
برای ساختن یک پرووایدر شخصی از دستور آرتیزان زیر استفاده میکنیم :
php artisan make:provider LoggerServiceProvider
با اجرای این دستور یک فایل به اسم LoggerServiceProvider.php توی مسیر app/Providers برای ما ساخته میشه با محتویات زیر :
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class LoggerServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
//
}
}
متد register مخصوص bind کردن چیزها توی Service Container هست . حالا چیزایی که توی AppServiceProvider نوشتیم رو انتقال میدیم به این متد :
class LoggerServiceProvider extends ServiceProvider
{
public function register()
{
$logger = new Logger;
$logger->setDriver('file');
$logger->setRate(30);
$this->app->instance('Logger', $logger);
}
// ...
}
تنها کاری که باید انجام بدیم شناسایی کردن این پرووایدر به فریمورک هست . فایل config/app.php رو باز و این پرووایدر رو به آرایه providers اضافه میکنیم :
الان این پرووایدر و همهی تنظیمات مربوط به اون توی برنامه در دسترس هست .
تفاوت متد register و boot
یکی از چیزهایی که شاید برای خیلیا هنوز گنگ باشه کاربرد این دو متد هست .
یک پرووایدر شامل این دو متد register و boot هست . همونطور که بالاتر گفته شد متد register فقط مخصوص bind کردن و ضمیمه کردن چیزها توی Service Container هست . فریمورک وقتی میخواد اجرا بشه ، متد register همهی پرووایدرهایی که ثبت شدن رو اجرا میکنه . توی این متد نباید از یک پرووایدر دیگه استفاده کنیم . چون ممکنه این پرووایدر هنوز توسط فریمورک پردازش و لود نشده باشه و توی نوبت باشه . واسه همین ممکنه هنوز در دسترس نباشه .
اما به محض اینکه متد register همهی پرووایدرها پردازش شد ، متد boot همه پرووایدرها توسط فریمورک شروع به پردازش شدن میکنه . این رو میتونین با یک echo ساده بررسی کنین . پس توی متد boot ما مطمئن هستیم که همهی پرووایدرها رجیستر شدن . پس کارهایی غیر از bind کردن مثل استفاده از یک پرووایدر دیگه ، اضافه کردن فایلهای Route ، ثبت کردن Event/Listerner ها و ... رو توی متد boot مینویسیم .
اگه توی یک پرووایدر فقط binding انجام میدیم ، یک کار بهینه اینه که این پرووایدر رو deferred کنیم . یعنی این پرووایدر زمانی لود میشه که ما به یکی از binding ها نیاز داریم . این کار باعث سریعتر شدن برنامه میشه . برای این کار پرووایدر ما باید اینترفیس DeferrableProvider رو پیادهسازی کنه . همچنین یک متد به اسم provides که آرایهای از binding ها رو return میکنه رو باید بسازیم :
namespace App\Providers;
use App\Services\Logger;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Support\DeferrableProvider;
class LoggerServiceProvider extends ServiceProvider implements DeferrableProvider
{
public function register()
{
$this->app->bind(Logger::class, function() {
return new Logger;
});
}
public function provides()
{
return [Logger::class];
}
}
دقت کنین که بعد از این کار دستور آرتیزان زیر رو وارد کنین :
php artisan clear-compiled
خب این بود همهی چیزایی که باید از سرویس پرووایدرها میدونستیم . همونطور که همیشه گفتم این موارد پیادهسازی و استفادهی خیلی راحتی دارن اما نکتهی مهمتر درک کردن این موارد هست . اینکه برای چی به وجود اومدن و چه مشکلی حل میکنن . مزایا و معایب استفاده از اونها چیه و ... . دونستن این موارد هست که به یک توسعهدهندهی خوب تبدیل میشیم .