البحث في الموقع
المحتوى عن 'service provider'.
-
رأينا في درس كيف تنشئ مزود خدمة Service provider في Laravel طريقة عمل مزوّدات الخدمة والآليّة التي تتيح بها مرونة أكبر في التعامل مع الأصناف في Laravel. سنرى في هذا الدّرس كيفيّة الاستفادة من تعليمة inject@ لإعادة استخدام مزوّد خدمة في قالب Blade. استخدمنا في الدرس السابق مزوّد خدمة داخل متحكّم على النحو التالي: public function index(RocketShipContract $rocketship) { $boom = $rocketship->blastOff(); return view('demo.index', compact('boom')); } سنفترض أن لدينا ثلاثة عروض view1 وview2 إضافة للعرض index (كلّها في نفس المجلّد): العرض view1: <!-- تعليمات خاصّة بالعرض view1 --> @include('demo.index') <!-- تعليمات خاصّة بالعرض view1 --> العرض view2: <!-- تعليمات خاصّة بالعرض view2 --> @include('demo.index') <!-- تعليمات خاصّة بالعرض view2 --> العرض index: @extends('layouts.master') @section('content') {{ $boom }} @endsection نلاحظ أن العرض index يُضمَّن في العرضيْن view1 وview2. سنفترض أيضا أن لدينا الدالتين التاليتين في المتحكّم: public function function1(RocketShipContract $rocketship) { $boom = $rocketship->blastOff(); return view('demo.view1', compact('boom')); } public function function2(RocketShipContract $rocketship) { $boom = $rocketship->blastOff(); return view('demo.view2', compact('boom')); } تعمل الطريقة أعلاه دون مشكل؛ ولكن ماذا لو أضفنا مسارا ثالثا يستدعي عرضا آخر يُضمِّن العرض index؟ إذا اتبعنا نفس الطريقة فسنضيف دالة جديدة إلى المتحكّم كالتالي: public function function3(RocketShipContract $rocketship) { $boom = $rocketship->blastOff(); return view('demo.view3', compact('boom')); } يمكن أن نلاحظ بوضوح وجود الكثير من التكرار لاستخدام الصّنف RocketShipContract. لتلافي هذا الوضع يعرّف Laravel حقن الخدمة Service injection. يقوم المبدأ على استخدام التعليمة inject@ التي تتيح استدعاء صنف مّا مباشرة في العرض. بتطبيق هذا المبدأ في الحالة أعلاه نعدّل العرض index ليصبح كالتالي: @inject('boom', 'App\Helpers\Contracts\RocketShipContract') @extends('layouts.master') @section('content') {{ $boom->blastOff() }} @endsection تأخذ التعليمة inject@ معطييْن؛ الأول (boom في المثال) اسم المتغيّر الذي سنحتفظ فيه بكائن من الصّنف الممرّر في المعطى الثاني (RocketShipContract). يمكننا بعد ذلك استخدام المتغيّر واستدعاء الدالة. بالعودة إلى دوال المتحكّم فيمكننا تغييرها كالتالي: public function function1() { return view('view1'); } public function function2() { return view('view2'); } نرى بوضوح أن الطريقة الأخيرة تتيح قابليّةً أكبر لإعادة الاستخدام. حقن الخدمة - كما رأينا - وسيلة مفيدة لإعادة استخدام وظائف من التطبيق وتقديمها في عروض، إلا أنه لا يُنصَح باستخدامها إلا عند الحاجة؛ ومن الضروريّ عدم المبالغة في استخدامها. ترجمة -وبتصرّف- للمقال How to Use @inject in Blade in Laravel 5.1 لصاحبه Bill Keck.
-
تعتمد بنية Laravel كثيرا على مزوّدي الخدمة Service providers لتحميل الأصناف Classes إلى الذاكرة مع بدء عمل التطبيق أو عند الحاجة إليها. يستخدم Laravel حاويةَ خدمة Service container لإدارة الاعتمادات بين الأصناف: ماهي الأصناف التي يعتمد عليها الصّنف الجديد للعمل؟ كيف يمكنه الحصول على كائن من هذه الأصناف؟ أين يوجد الصّنف وهل سبق لإطار العمل تحميلُه إلى الذاكرة؟ تمكننا بإعداد مزوّد خدمة وتعريفه إضافةُ صنف إلى حاويّة الخدمة ممّا يسهّل بالتالي استخدامه؛ إذ سيتعرّف إطار العمل على ما يحتاجه هذا الصّنف (أي الاعتمادات) وينشئه إن لم يكن موجودا سلفا. سنشرح في هذا الدرس أساسيّات إنشاء مزود خدمة في Laravel. نبدأ بإنشاء مسار سنستخدمه للتوضيح: Route::get('/demo', 'DemoController@index'); ثم ننشئ المتحكّم: php artisan make:controller DemoController ثم نعرّف الدالة index في المتحكّم: public function index() { return view('demo.index'); } تطلب الدالة index تقديم العرض demo.index؛ لذا سننشئ مجلّدا باسم demo في مجلّد العروض وننشئ فيه عرضا باسم index.blade.php. نضيف المحتوى التالي للعرض index: @extends('layouts.master') @section('content') <h1>Demo Page</h1> @endsection يمدّد العرض index عرضا رئيسا باسم master ضمن مجلّد العروض layouts. أنشئ العرض الرئيس إن احتجت لذلك. لم ندخل في صميم الموضوع لحد السّاعة، فقط ضبطنا بعض الإعدادات البدائية. تظهر عند الدخول إلى الرابط demo/ صفحة بها عنوان Demo Page. إعداد مزود خدمة سنشرح في الفقرات المواليّة كيفية إنشاء مزوّد خدمة. نبدأ بإنشاء مجلّد باسم Helpers في مجلّد app ثم ننشئ مجلّدا متفرعا عن Helpers باسم Contracts؛ بداخل الأخير ننشئ ملفّا باسم RocketShipContract.php نضيف إليه المحتوى التالي: <?php namespace App\Helpers\Contracts; Interface RocketShipContract { public function blastOff(); } لاحظ تعريف الصّنف أعلاه. عرّفنا RocketShipContract على أنه واجهة Interface. تعرّف الواجهات في PHP (ولغات برمجيّة أخرى) "عقدا" يجب على الأصناف التي تطبّق الواجهة اتّباعه. يجب على الأصناف التي تنفّذ Implement الواجهة المعرَّفة أعلاه أن تحوي دالة عمومية public باسم blastOff. نأتي الآن بعد تعريف الواجهة إلى الصّنف الذي ينفّذها. تحوي الواجهة خطوطا عريضة يأتي الصّنف المنفّذ لتخصيصها. ننشئ صنفا باسم RocketShip.php في مجلّد Helpers ونضيف إليه المحتوى التالي: <?php namespace app\Helpers; use App\Helpers\Contracts\RocketShipContract; class RocketShip implements RocketShipContract { public function blastOff() { return 'Houston, we have ignition'; } } يمكن أن تعالج في هذا الصّنف الكثير من الأمور، إلا أننا لا نودّ التعقيد. كل ما تفعله هذه الدالة هو إرجاع العبارة Houston, we have ignition. عرفنا الواجهة وكيفيةً لتنفيذها. يأتي الآن دور مزوّد الخدمة الذي سننشئه بالأمر التالي: php artisan make:provider RocketShipServiceProvider ينشئ الأمر ملفّا باسم RocketShipServiceProvider.php في المجلّد. تجد عند فتح الملف ما يلي (بعد نزع التعليقات): <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class RocketShipServiceProvider extends ServiceProvider { public function boot() { } public function register() { } } عدّل على الملف ليصبح على النحو التالي: <?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use App\Helpers\RocketShip; class RocketShipServiceProvider extends ServiceProvider { protected $defer = true; public function boot() { } public function register() { $this->app->bind('App\Helpers\Contracts\RocketShipContract', function(){ return new RocketShip(); }); } public function provides() { return ['App\Helpers\Contracts\RocketShipContract']; } } أولى التعديلات هي استدعاء الصّنف RocketShip: use App\Helpers\RocketShip; ثم إضافة الخاصّية التاليّة: protected $defer = true; يعني تمكين الخاصيّة defer$ أننا نطلُب ألا يُحمَّل الصّنف إلا عند الضرورة؛ مما يسهِم في الرفع من أداء التطبيق. ثم يأتي دور الدالة register التي تربط بين الواجهة والصّنف الذي ينفّذها. public function register() { $this->app->bind('App\Helpers\Contracts\RocketShipContract', function(){ return new RocketShip(); }); } يتيح هذا الربط إمكانية استخدام الواجهة في أي مكان تريده وسيستخدم مزوّد الخدمة الصنف الذي تربطه بها في دالة register تلقائيا. يزيد هذا الإعداد من مرونة التطبيق؛ فكل ما عليك فعله لتغيير سلوك الواجهة هو تعديل الصّنف المربوط بها. التعديل الأخير هو إضافة الدالة provides التي تُرجع الواجهة. يجب تعريف هذه الدالة عند تمكين الخاصيّة defer. الخطوة الأخيرة من إعداد مزوّد الخدمة هي إضافته إلى ملف إعداد التطبيق config\app.php ضمن مصفوفة providers (أدرجنا بعضا من محتويات الملف في الشفرة أدناه، مزوّد الخدمة الخاص بنا هو الأخير): /* * Application Service Providers... */ App\Providers\AppServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\RocketShipServiceProvider::class, استخدام مزود الخدمة نعود الآن للمتحكّم DemoController ونعدّل الدالة index كالتالي: public function index(RocketShipContract $rocketship) { $boom = $rocketship->blastOff(); return view('demo.index', compact('boom')); } لا تنس استيراد الواجهة: use App\Helpers\Contracts\RocketShipContract; نمرّر للدّالة index كائنا من الصنف RocketShipContract (أي الواجهة). سيعرف Laravel تلقائيا أننا نريد الصّنف RocketShip الذي ينفّذ الواجهة. يعود السبب في ذلك إلى الربط الموجود في مزوّد الخدمة. نمرّر النتيجة المتحصّل عليها من استدعاء blastOff إلى العرض لتقديمه. نعدّل العرض لاستخدام المتغيّر الذي ممرناه إليه: @extends('layouts.master') @section('content') {{ $boom }} @endsection تظهر العبارة التاليّة في المتصفّح عند زيارة الرابط demo/: Houston, we have ignition سننشئ لتوضيح المرونة التي تتيحها مزودات الخدمة صنفا آخر ينفّذ الواجهة RocketShipContract. سنسمّي هذا الصنف الجديد RocketLauncher ونضعه في نفس المجلد الذي يوجد به صنف التنفيذ السابق (Helpers): <?php namespace app\Helpers; use App\Helpers\Contracts\RocketShipContract; class RocketLauncher implements RocketShipContract { public function blastOff() { return 'Houston, we have launched!'; } } لم نغيّر الكثير بالمقارنة مع الصنف RocketShip السّابق؛ فقط العبارة. نعود إلى مزوّد الخدمة ونغيّر ربط الواجهة كالتالي: public function register() { $this->app->bind('App\Helpers\Contracts\RocketShipContract', function(){ return new RocketLauncher(); }); } دون أن ننسى استدعاء الصّنف: use App\Helpers\RocketLauncher; ستلاحظ الآن تغير العبارة الظاهرة في المتصفّح عند زيارة الرابط demo/. لم نفعل أمورا معقّدة في الخطوات السّابقة إلا أن بالإمكان رؤية أن التعامل مع الواجهات مباشرة وترك التنفيذ الفعلي للأصناف المربوطة عبر مزوّد الخدمة يضيف الكثير من المرونة إلى بنية التطبيق. يوجد أمران ينبغي الانتباه إليهما عند التعامل مع مزوّدات الخدمة في Laravel: اجعل إضافة مزوّد الخدمة إلى ملف الإعداد config/app.php هي آخر الخطوات؛ بعد تعريف الواجهة ومزوّد الخدمة. إضافة صنف غير موجود إلى ملف الإعداد تؤدي إلى التشويش على عمل artisan. من الأفضل حذف مزوّد الخدمة ثم إنشاء واحد جديد بأداة artisan؛ بدلا من إعادة تسميّته. قد تؤدي إعادة التسميّة إلى عدم تحميل مزوّد الخدمة فلا يتعرّف عليه التطبيق. ترجمة -وبتصرّف- للمقال How to Create a Service Provider in Laravel 5.1 لصاحبه Bill Keck.
-
- service provider
- laravel
-
(و 2 أكثر)
موسوم في: