- مقدمة
- متى تُستخدم واجهات Facades
- كيفية عمل واجهات Facades
- واجهات Facades أثناء التنفيذ
- مرجع لكائنات Facades
خلال قراءتك لتوثيق لارافيل ستشاهد أمثلة لأكواد تقوم بالتفاعل مع ميزات لارافيل بواسطة "facades". توفر واجهات لارافيل Facades واجهة ستاتيكية (static interface) للكائنات (classes) المتوافرة في حاوي خدمات التطبيق. تحتوي لارافيل العديد من واجهات Facades التي توفر الوصول لمعظم ميزات لارافيل.
تعمل واجهات لارافيل Facades بصفتها وكيل ستاتيكي ("static proxies") للكائنات الأساسية في حاوي الخدمة، حيث توفر ميزة الصياغة المعبرة والموجزة مع الحفاظ على قابلية الاختبار ومرونة أكبر من الطرق الستاتيكية (static methods) التقليدية. من الطبيعي جداً عدم فهمك بشكل كامل لكيفية عمل Facades ضمنياً، عليك فقط بالمسايرة واكمال تعلمك عن لارافيل.
كل واجهات لارافيل Facades معرفة ضمن نطاق الأسماء Illuminate\Support\Facades
. أي أنه يمكننا وبسهولة الوصول لـ Facades بالأسلوب التالي:
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Route;
Route::get('/cache', function () {
return Cache::get('key');
});
خلال توثيق لارافيل يوجد العديد من الأمثلة التي تستعمل واجهات Facades لتوضيح ميزات متنوعة لإطار عمل لارافيل.
واستكمالاً لواجهات Facades تقدم لارافيل عدة توابع مساعدة متنوعة لتجعل التعامل مع الميزات العامة في لارافيل أكثر سهولة. قد تتعامل مع بعض التوابع المساعدة الشائعة مثل view
، response
، url
، config
وغيرها المزيد. كل تابع مساعد مقدم من لارافيل يتم توثيقه مع الميزة التي يقدمها، وبكل الأحوال تتوفر لائحة كاملة في التوثيق المخصص للتوابع المساعدة .
على سبيل المثال، بدلاً من استخدام الـ Facade التالي Illuminate\Support\Facades\Response
لانشاء رد (response) من النوع JSON يمكنك ببساطة استخدام التابع المساعد response
. ولأن التوابع المساعدة متوفرة على نطاق التطبيق (globally available)، لست بحاجة لاستيراد (import) أي صفوف لاستخدمها:
use Illuminate\Support\Facades\Response;
Route::get('/users', function () {
return Response::json([
// ...
]);
});
Route::get('/users', function () {
return response()->json([
// ...
]);
});
إن واجهات Facades عديدة الفوائد. فهي توفر صياغة موجزة وقابلة للتذكر مما يتيح استخدام ميزات لارافيل بدون تذكر أسماء الكائنات الطويلة التي يجب حقنها أو اعدادها يدوياً. وبسبب استخدامها الفريد لطرائق PHP الديناميكية يجعلها ذلك سهلة الاختبار أيضاً.
بكل الأحوال، يجب الاحتياط قليلاً عند استعمال واجهات Facades. لأن خطرها الرئيسي يكمن بتمدد نطاق الكائن ("scope creep"). بما أن واجهات Facades سهلة الاستعمال ولا تحتاج لحقن، فمن السهولة أن تتضخم كائناتك (classes) عند استخدام أكثر من واجهة Facade في كائن واحد. إن استخدام حقن الاعتماديات من المحتمل أن يحد من المشكلة بسبب التحذيرات المرئية التي يصدرها الباني (constructor) الضخم بأن الكائن يتضخم بشكل كبير. فعند استخدامك لواجهات Facades يجب إعارة انتباه خاص لحجم الكائن لكي يبقى نطاق مسؤليته (scope of responsibility) ضيقاً. وفي حال تضخم الكائن كثيراً، قد تحتاج لتقسيمه لعدة كائنات أصغر.
واحد من الفوائد الأساسية لحقن الاعتمادية هو امكانية تبديل استخدامات (implementations) الكائن الذي تم حقنه. مما يفيد أثناء الاختبار حيث يمكنك حقن غرض مُقلِّد (mock) أو نموذج القابل للاستبدال (stub) وتتحقق من استدعاء مختلف الطرق فيها.
عملياً، ليس من الممكن استخدام الأغراض المُقلِّدة (mock) أو النماذج القابلة للاستبدال (stubs) على طريقة ستاتيكية للكائن. ولكن ولأن واجهات Facades تستخدم الطرق الديناميكة لتوكيل طرق لاستدعاء الكائنات تم الحصول عليها بواسطة حاوي الخدمة، يمكننا في الواقع اختبار Facades كما يمكننا اختبار نسخة (instance) لكائن تم حقنه. على سبيل المثال ليكن لدينا المسار (route) التالي:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
باستخدام طرق الاختبار الخاصة بلارافيل Facade، يمكننا كتابة النص التالي للتحقق من أنه تم استدعاء الطريقة Cache::get
بالوسيط الذي نتوقعه:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
بالإضافة لواجهات Facades تحتوي لارافيل العديد من التوابع المساعدة التي تقوم بالمهام الشائعة كإنشاء واجهات (views)، إطلاق الأحداث (events)، توزيع الأعمال، أو ارسال ردود (responses) من نوع HTTP. والعديد من هذه التوابع المساعدة يقوم بتنفيذ الوظيفة نفسها لواجهة Facade المقابلة لها، فعلى سبيل المثال استدعاء واجهة Facade التالي مطابق لاستدعاء التابع المساعد:
return Illuminate\Support\Facades\View::make('profile');
return view('profile');
عملياً لا يوجد أي فرق بين واجهات Facades والتوابع المساعدة. عند استخدام التوابع المساعدة، يمكنك اختبارها بنفس الأسلوب الذي قد تختبر فيه واجهة Facade الموافقة. على سبيل المثال ليكن لدينا المسار (route) التالي:
Route::get('/cache', function () {
return cache('key');
});
ضمنياً يقوم لتابع المساعد cache
باستدعاء الطريقة get
على الكائن المضمن في واجهة Facade التالية Cache
. مما يعني أنه حتى لو قمنا باستخدام التابع المساعد، يمكننا كتابة الاختبار التالي للتأكد من أنه تم استدعاء الطريقة بالوسيط الذي نتوقعه:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$response = $this->get('/cache');
$response->assertSee('value');
}
في تطبيق لارافيل، واجهة Facade هي كائن يوفر الوصول لكائن من حاوي الخدمات. حيث أن الآلية التي تسمح بذلك موجودة في الكائن Facade
. إن واجهات لارافيل Facades وأي واجهات Facades تقوم بإنشائها، سوف ترث من الكائن الأساسي (base class) Illuminate\Support\Facades\Facade
.
الكائن الأساسي Facade
يستخدم الطريقة الجاهزة (magic-method) ()callStatic__
لتحويل الاستدعاءات من واجهة Facade لكائن يتم الحصول عليه من حاوي الخدمات. في المثال التالي، سيتم استدعاء نظام الذاكرة المؤقت (cache system) الخاص بلارافيل. عند القاء نظرة على هذا الكود قد تظن بأن الطريقة الستاتيكية get
يتم استدعائها على الصف Cache
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
لاحظ في أعلى الملف بأننا نقوم باستيراد (importing) واجهة Facade التالية Cache
. حيث تقوم واجهة Facade بالعمل كوكيل للوصول للاستخدام (implementation) الخاص بالواجهة Illuminate\Contracts\Cache\Factory
. حيث أن أي استدعاءات نقوم بها باستخدام واجهة Facade سيتم تمريرها للنسخة (instance) الضمنية في خدمة الذاكرة المؤقتة في لارافيل.
اذا قمنا بإلقاء نظرة على الكائن Illuminate\Support\Facades\Cache
ستجد بأنه لا وجود للطريقة الستاتيكية get
:
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
بدلاً من ذلك ترث واجهة Facade التالية Cache
من الصف الأساسي Facade
وتُعرِّف الطريقة ()getFacadeAccessor
. إن مهمة هذه الطريقة هو إعادة اسم ارتباط خاص بحاوي الخدمات. عندما يشير المستخدم لأي طريقة ستاتيكية في واجهة Facade التالية Cache
ستقوم لارافيل بالحصول على الارتباط بين cache
و حاوي الخدمات وتنفيذ الطريقة المطلوبة (في هذه الحالة الطريقة get
) على الكائن.
يمكنك معاملة أي كائن من كائناتك بالتطبيق كما لو كان واجهة Facades باستخدام واجهات Facades أثناء التنفيذ. لتوضيح كيفية استخدام ذلك، دعنا بدايةً نتصفح بعض الأكواد التي لا تستخدم واجهات Facades أثناء التنفيذ. على سبيل المثال لنفترض بأن النموذج (model) التالي Podcast
يحتوي على طريقة publish
. حسناً لكي نقوم بنشر بودكاست نحتاج لحقن نسخة (instance) من الناشر Publisher
:
<?php
namespace App\Models;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @param Publisher $publisher
* @return void
*/
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
يسمح لنا حقن استخدام (implementation) للناشر publisher في الطريقة باختبار الطريقة على انفراد طالما أننا قادرون على تقليد (mock) الناشر الذي تم حقنه. بكل الأحوال مطلوب بشكل دائم تمرير نسخة (instance) من الناشر publisher في كل مرة نقوم فيها باستدعاء الطريقة publish
. لذلك وباستخدام واجهات Facades أثناء التنفيذ يمكننا المحافظة على قابلية الاختبار بدون أن نكون مطالبين بشكل صريح بتمرير نسخة من Publisher
. لإنشاء واجهة Facade أثناء التنفيذ أضف كلمة Facades
كبادئة لنطاق الأسماء (namespace) للكائن المستورد (imported class):
<?php
namespace App\Models;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @return void
*/
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
عند استخدام واجهات Facades أثناء التنفيذ سيتم الحصول على استخدام (implementation) للواجهة publisher من حاوي الخدمات باستخدام جزء من الواجهة أو اسم الكائن الذي يظهر بعد البادئة Facades
. عند الاختبار يمكننا استخدام التوابع المساعدة للاختبار المُدمجة بلارافيل لتقليد (mock) استدعاء هذه الطريقة:
<?php
namespace Tests\Feature;
use App\Models\Podcast;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* A test example.
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = Podcast::factory()->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
ستجد في الأسفل كل واجهات Facades وكائناتها الضمنية(underlying classes). يوفر هذا أداة سهلة للبحث عن أصل واجهة Facade في توثيق API الخاص بلارافيل. كما يتوفر مفتاح الارتباط الخاص بحاوي الخدمات في الأماكن المناسبة: