تو خونه نشستی پای فوتبال ، خانمت غر میزنه میگه برو نون بگیر ، حالا از اون جایی که تو هم پیسر خوبی هستی و به حرف خانمت گوش میدی ، تصمیم میگیری بری نون تهیه کنی ، خب طبیعتا یه فرآیندی طی میکنی تا آماده بشی ، لباس میپوشی ، موهات شونه میکنی ، کیف پولتو برمیداری و . . .
همونطور که اگه بخوایی نون بگیری یا هر کار دیگه کنی یه سری کار های آماده سازی باید انجام بدی ، هر برنامه ای هم برای اینکه اجرا بشه و به درستی بتونه کار کنه باید یه سری از کلاس ها و سرویس ها ، اینترفیس ها و موارد مهم و ضروری قبلش فرآخوانی کنه .
توی لاراول قبل از اینکه برنامه بخواد به درستی اجرا بشه ، باید سرویس های مهم دیتابیس ، کش ، کوکی و یه سری سرویس های دیگه فرآخونی بشن . حالا کسی که این عملیات آماده سازی انجام میده آقای Service Provider هستش ینی کسی که قبل از شروع برنامه ، سرویس های مورد نیاز برنامه آماده میکنه . این آقا به نوعی راه انداز سیستم لاراولی ما هستش و میاد میبینه برنامه ما به چه سرویس هایی نیاز داره میره اونا برامون میاره و کاری میکنه که بتونیم توی هر جایی از برنامه ، اجراشون کنیم .
حالا فکرش بکن ، زندگی چقدر راحت تر میشد اگه توی زندگی واقعی هم آقای Service Provider بود ، در این صورت ما فقط فرآیند های اصلی انجام میدادیم و فرآیند هایی که قبلش باید انجام بدیم ، مثل لباس پوشیدن ، رانندگی کردن ، کارت کشیدن ، آقای Service Provider فراهم میکرد .
توی یه برنامه لاراولی ، هر سرویسی ، مثل Database و یا Session و یا هر سرویس دیگه برای اینکه مجوز فعالیت توی برنامه بگیره باید به آقای Service Provider معرفی بشه ، ینی بگه آقای Service Provider من سرویس Session هستم و میخوام توی این برنامه مورد استفاده قرار بگیرم . حالا این معرفی شدنه باید کجا انجام بشه ؟؟ ما چطوری باید سرویس خودمون به لاراول معرفی کنیم ؟؟
برنامه لاراولی باز کن و وارد مسیر Config/App.php شو یکم به پایین اسکرول کن یه آرایه میبینی به اسم Providers :
'providers' => [ /* * Laravel Framework Service Providers... */ Illuminate\Auth\AuthServiceProvider::class, Illuminate\Broadcasting\BroadcastServiceProvider::class, Illuminate\Bus\BusServiceProvider::class, Illuminate\Cache\CacheServiceProvider::class, Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, Illuminate\Cookie\CookieServiceProvider::class,
]
هر سرویسی که توی سیستم قراره استفاده بشه ، باید توی این آرایه اضافه بکنیم . مثلا اگه ما یه پکیج به سیستم اضافه کنیم باید از این طریق پکیج به سیستم معرفی کنیم .
در حالت پیشفرض یه سری از سرویس های اصلی و مهم که مربوط به هسته لاراول هستند اینجا ثبت شدند ، وقتی که برنامه ما اجرا میشه لاراول اول Provider های ما لود میکنه و بعد برنامه اصلیمون میاد لود میکنه به این ترتیب وقتی برنامه اصلی لود شد هر چیزی که نیاز داره برای استفاده آمادست .
خب ما تا الان گفتیم سرویس پروایدر قبل از اجرای برنامه اصلیمون همه سرویس های مورد نیاز فراهم میکنه . حالا بعضی وقتا ما نیاز داریم یه سری سرویس هایی توی کل سیستم در اختیار داشته باشیم ، ینی درواقع یه چیز عمومی توی کل سیستم ، مثل سیستم اطلاع رسانی :
خوب همونطور که در تصویر میبینید ، فرقی نمیکنه ما توی داشبورد باشیم یا مثلا هر جای دیگه از سایتمون ، همیشه و همه جا باید بتونید اعلان های جدید ببینیم .
طبیعتا برای فراخوانی این اعلان ها باید کدی نوشته بشه منتها فرق این کد با بقیه کد ها اینه که لازمه توی کل سیستم باشه و منحصر به یه کنترلر نیست ، خب یه راهش اینه که بیاییم توی توابع سازنده هر بخش (Controller )این تیکه کد بزاریم که مثلا اگه سایت ما هزار بخش داشته باشه باید هزار بار این تیکه کد کپی کنیم که طبیعتا کار منطقی نیست .
خب واسه این که با یه بار نوشتن این تیکه کد توی هر جایی که بخواییم بهش دسترسی داشته باشیم ، باید از Service Provider استفاده کنیم (اصلا کارش همینه دیگه ) اینجوری کدامون خیلی خیلی بهینه میشن . حالا چطوری ؟
اگه سرویس هایی که میخواییم به برناممون اضافه کنیم کم باشند مثلا دو سه مورد باشند ، میتونیم بزاریمشون توی App Service Provider که یه Provider هستش که قبلا ثبت شده بنابراین دیگه نیاز نیست برید یه پروایدر جدید بسازید .
class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { //اینجا میتونیم کدمون اضافه کنیم } }
ولی اگه برناممون برنامه کوچیکی نباشه ما و سرویس های زیادی داشته باشیم که نیاز باشه به سیستم اضافه بشه باید دیگه بهینه و منظقی نیست که بیاییم توی App Service Provider همه این موارد بنویسیم .
خب گفتیم اگه سرویس های زیادی داشته باشیم که نیاز باشه به سیستم اضافه بشه و در قسمت های مختلف برنامه استفاده بشه دیگه کار درستی نیست که بیاییم همشون توی App Service Provider اضافه کنیم و بهترین کار اینه که برای هر کدوم یه Provider جدا درست کنیم حالا سوال اینجاست که چطوری این کار بکنیم ؟
برای ساخت Provider شخصی ما میاییم از دستور آرتیسان زیر استفاده میکنیم :
php artisan make:provider --name
حالا این چیزی که ساختیم کجا ذخیره میشه و ما باید کجا کدمون بنویسیم ؟
اگه پوشه App باز کنی یه بخشی داره به اسم Providers توی اون بخش میتونی Provider که ساختی ببینی . خب حالا اینجا این Provider عزیز ما دو تا قسمت داره ، یکی Boot و یکی دیگه Register که الان بت میگم هر کدومشون برا چیه و چه موقعی باید ازش استفاده کرد .
وقتی بخواییم چیزی به Service Container اضافه (Bind)کنیم میاییم کدمون توی این متد مینویسیم .
توی این متد ، نباید از سرویس های دیگه استفاده کنیم و حالت درستش اینه که اطلاعاتمونو فقط به یه سرویس Bind کنیم . حالا دلیلش چیه ؟ دلیلش اینه که ، وقتی فریمورک اجرا میشه ، اول میاد متد Register همه پروایدر هایی که تو سیستم ثبت شدن اجرا میکنه .
پس اگه توی یه دونه از این Provider ها بیاییم از دوتا یا سه تا سرویس استفاده کنیم ، ممکنه اونا هنوز پردازش نشده باشند ، ینی فریمورک متد Register اونا هنوز اجرا نکرده باشه .وقتی کار Service Provider با همه متد های Register تموم شد ، هر سرویسی که بخواییم در اختیار داریم و با خیال راحت میتونیم از هر امکانی استفاده کنیم .
خیلی وقتا ما میخاییم هر وقت که یه View ی رندر شد ، یه سری اطلاعات هم به اون ویو Bind بشه مثل مثال زیر :
public function boot() { View::composer('admin.require.header', function($view) { $view->with([ 'notifications' => Auth::user()->notifications(), 'unreadNotifications' => Auth::user()->unreadNotifications(), ]); }); }
توی این مثال ما میخواییم هر وقت که ویو Header رندر شد ، مثلا Notification ها هم بهش Bind بشه و ما توی Header به اعلان های کاربر دسترسی داریم . خب همونطور که توی مثال هم مشخصه برای اینکار باید از View Composer (راجب نحوه عملکرد View Composer جلوتر توضیح دادم )استفاده کنیم و اگه بخواییم از View Composer استفاده کنیم باید توی متد Boot کدمون بنویسیم .
هر چیزی غیر از Bind کردن باید توی متد Boot انجام بدید ، ، مثلا وقتی میخوایی از Event Listener ها استفاده کنی یا وقتی میخوایی از Middleware ها تو کدت استفاده کنی باید این کار توی متد Boot انجام بدی .
وقتی داری تو متد Boot کد میزنی ، میتونی مطمعن باشی همه سرویس ها ثبت شدند و از همه این سرویس ها میتونی توی کدت استفاده کنی ، مثلا من اگه بخوام از سرویس اعتبار سنجی (auth) و ,کش (cache ) توی متد Boot استفاده کنم هیچ مشکلی نداره .
# تزریق وابستگی به متد Boot :
گاها پیش میاد متد Boot برای این که کار خودش به درستی انجام بده به وابستگی هایی نیاز داشته باشه ، از اونجایی که ما داریم از یه فریمورک خارق العاده و بینظیر استفاده میکنیم این وابستگی ها به شکل اتوماتیک توسط Service Container به متد Boot تزریق میشن . مثال زیر نگاه کن تا کاملا متوجه بشی :
public function boot(ResponseFactory $response) { $response->macro('serialized', function ($value) { // }); }
خب تا اینجای کار ما Provider خودمون ساختیم و کدمون هم توی متد Boot و یا Register نوشتیم حالا به نظرتون ما میتونیم از Providerی که ثبت کردیم توی سیستم استفاده کنیم ؟؟
مسلما جواب منفی هستش . درسته که ما Provider بدون هیچ عیب و ایرادی ساختیم ولی هنوز به لیست Provider های لاراول اضافش نکردیم ، پس وقتی که لاراول شروع به کار میکنه ، این Provider توی لیست خودش نمیبینه و به همین خاطر هم کدهایی که نوشتیم اجرا نمیکنه .
پس ما باید این Provider توی سیستم ثبت کنیم برای اینکار ما اول از همه وارد پوشه Config میشیم و سپس فایل App.php باز میکنیم بعد دنبال قسمت provider ها میگردیم و Provider خودمون معرفی میکنیم دقیقا به شکل زیر :
'providers' => [ // Other Service Providers App\Providers\ComposerServiceProvider::class, ],
نکته :
توی آرایه ای که میبینی همه Provider ها یه اهمیت یکسان ندارند بعضیاشون واسه راه اندازی سیستم حیاتی هستند مثل Mailer و Cache و یه سری دیگه واسه راه افتادن سیستم ضروری نیستند ، و سیستم ما میتونه بدون اونا هم به درستی راه اندازی بشه ، مثل اون پکیج هایی که اضافه میکنیم .
حالا این Provider هایی که برای راه انداخته شدن سیستم ضروری نیستند اکثرا به شکل Deferred ثبت شدند ینی وقتی بهشون نیاز باشه صدا زده میشند و اجرا میشند اینطوری سیستم ما خیلی بهینه تر میشه .
یه مثال کاربردی :
خب ماشین سرویس های زیادی داره ، کولر ، ظبط ، گرمکن های صندلی ، بالابر های شیشه و . . . ولی آیا وقتی ماشین میخواییم روشن کنیم نیازی هست کولر هم روشن بشه ؟ نیازی هست ظبط هم روشن بشه ؟ چرا الکی به موتور و باتری خودمون فشار وارد کنیم ؟ سرویس هایی مثل کولر و ظبط وقتی بهشون نیاز داشته باشیم فراخوانی میشند و تاثیری توی راه اندازی اولیه ندارند دقیقا مثل لاراول .
ما میخواییم توی سایت لاراولیمون اطلاعیه های مربوط به هر کاربر توی پنل خودش بهش نشون بدیم ینی مثلا اگه یه پیامی براش اومد بنویسیم "یک پیام جدید دارید ! " .
از طرفی بخش Notification هم برای اینکار تنظیم کردیم و فقط میخواییم کاربر هر جا باشه بتونه پیامهاش ببینه . خب مشخصا باید از سرویس پروایدر ها استفاده کنیم و چون اطلاعاتی که میگیریمو میخواییم به یه ویو ارسال کنیم باید کدمون توی متد Boot بنویسیم .
public function boot() { View::composer('admin.require.header', function($view) { $view->with([ 'notifications' => Auth::user()->notifications(), 'unreadNotifications' => Auth::user()->unreadNotifications(), ]); }); }
ببین وقتی که میخوایی سرویس پروایدر شخصیت ثبت کنی و اطلاعات میخوایی به یه View ارسال کنی ، باید از متد Composer استفاده کنی این متد ازت سوال میپرسه آقای برنامه نویس چه اطلاعاتی میخوایی همیشه برات بیارم ؟ مثلا اینجا اون اطلاعات میشه notification و unreadNotification ینی لیست اطلاعیه ها (اینو داره توی آرگومان دوم تابع میپرسه که یه کال بک فانکشنه ) .
بعدش میپرسه خب اوکی ولی این اطلاعات توی چه View هایی در دسترس باشند ؟ خب از اونجایی که Header توی همه صفحه ها هست و من هم میخوام اطلاعیه هام توی Header نشون بدم میگم آقا هر وقت ویو Header صدا زده شد بیا و اطلاعیه ها هم برام بیار .
الان اگه بریم توی فایل Header و بخواییم از Notification ها استفاده کنیم بازم نمیتونیم چون ما Provider که تعریف کردیم به Service Provider معرفی نکردیم . برای این که اینکار بتونم انجام بدیم باید بریم توی پوشه Config و فایل App.php باز کنیم توی قسمت Providers میاییم و Provider که ساختیم معرفی میکنیم .
'providers' => [ /* * Laravel Framework Service Providers... */ App\Providers\NotificationServiceProvider::class,
]
خب حالا به راحتی میتونیم توی فایل Header از اطلاعات دلخواهمون استفاده کنیم و اعلان ها را نمایش بدیم .
@foreach($notifications as $notif) <li> <div href="#" class="p-3 list-group-item d-flex align-items-center link-1 hide-show-toggler"> <div> <figure class="avatar avatar-sm m-r-15"> <span class="avatar-title bg-info-bright text-info rounded-circle"> <i class="{{$notif->data['icon']}} font-size-20"></i> </span> </figure> </div> </li> @endforeach
از اونجایی که فریمورک در هر درخواستی میاد این پروایدر ها اجرا میکنه ، پس اگه کاری کنیم که پروایدری که نوشتیم فقط در مواقعی که بهش نیاز داریم فراخوانی بشه و نه با هر Request ی که به برنامه میاد ، برناممون خیلی بهینه تر و حرفه ای تر میشه به این کار میگن Differed کردن .
وقتی که ما توی Providerی که نوشتیم فقط از متد Request استفاده کرده باشیم ، ینی درواقع فقط عملیات Bind کردن انجام داده باشیم میتونیم با Differed کردن ، به لاراول بگیم اقای لاراول لطفا این Provider وقتی اجرا کن که بهش نیازه .
#حرف آخر
خب دوستان این پست هم تموم شد و من همه چیزایی که لازم بود راجب Service Provider بدونید بهتون گفتم ولی خودتون هم تمرین کنید و هر سوالی براتون پیش اومد میتونید ازم بپرسید البته من توی اینستاگرام و لینکدین هم حضور فعال دارم (@danial_sanaee) خیلی خوشحال میشم اونجا ببینمتون .
به امید دیدار