Facades یکی از مباحث بسیار داغ در اجتماعات لاراول است. برخی آن را میپسندند و برخی دیگر از آن تنفر دارند. ولی به نظر من Facades یکی از ویژگی‌های ارزشمند فریم‌ورک لاراول است. که  باعث ایجاد کدهایی تمیز می‌شود و بدون Facades لاراول به این حد از محبوبیت نمی‌رسید.

اما عدم اقبال و تنفر نسبت به آن به چند دلیل است: اول اینکه Facades اسم یک Design Pattern است درحالی که کلاسهای Facades عملکردی بسیار شبیه proxy design pattern دارند. به هر حال نباید با این موضوع مشکلی داشت چون Facades بعنوان یکی از component های فریم‌ورک لاراول است و یقینا design pattern نیست. Facades در واقع نقابی بر روی سرویس‌های لاراول است.

مشکل دیگر آنکه Facades ها تاحدی شبیه global function ها هستند. اگر شما برنامه نویس شی‌گرای مقیدی باشید ممکن است این ایراد را بگیرید که چرا آنها اشیایی(Object) از سرویس‌ها(Services) نیستند. که این سرویس‌ها خود درواقع کلاس(Class) هستند. هر چند این مشکل مطرح شده صحیح است ولی Facades ها قابلیت‌هایی را برای فریم‌ورک ایجاد می‌کنند که واقعا ارزشش را دارد. Facades ها کاربرد بسیار ساده‌ای دارند و باعث می‌شوند کدهای شما بسیار تمیز به نظر برسند. درضمن Facades ها همیشه در قسمت‌های مختلف در دسترس هستند. همچنین باعث افزایش سرعت توسعه کد و سهولت کار برنامه‌نویسان تازه کار می‌شود.

با اینحال اگر نمی‌خواهید از Facades ها استفاده کنید اجباری در کار نیست چون بجای آن می‌توانید بدون هیچ مشکلی از Dependency Injection استفاده نمایید.

Facades چیست؟

اگر شما از فریم‌ورک لاراول استفاده کرده باشید و حتی یک کد خیلی ساده نوشته باشید حتما از Facade استفاده کرده‌اید. زمانی که شما در فایل routes/web.php از کلاس Route و یا در هر جایی از عبارت Input استفاده کرده باشید درواقع از Facade استفاده کرده اید.

مولفه‌های(Component) لاراول همچون database , events, routing و … در جایی نگهداری میشوند که اصطلاحا به آن container میگویند. container را در واقع می‌توانید بعنوان ظرف بزرگی تصور کنید که همه‌ی این سرویسها را در خود ذخیره کرده است. استفاده از این ظرف باعث میشود که این سرویسها فقط یکبار و آن هم در هنگام نیاز ایجاد شوند. همچنین لازم نیست وقتی که شما به این سرویسها نیاز دارید خودتان آنها را راه اندازی و ایجاد کنید بلکه بصورت خودکار برای شما ایجاد میشوند.

نحوه کارکرد:

بیایید نگاهی به یک Facade method بیاندازیم:

در مثال بالا Route در واقع Facade است و get() متدی در آن Facade. با توجه به استفاده از علامت(دو تا دونقطه) :: ممکن است تصور کنید که get یک متد static از کلاس Route باشد! اما اینطور نیست.

همانطور که پیشتر گفتیم Facade بسیار شبیه الگوی proxy است. Facade یک متد __callstatic() دارد که در مقابل تمامی فراخوانی‌های static این کلاس قرار دارد. وقتی ما متد get() را فراخوانی می‌کنیم لاراول ابتدا سرویس متناظر را (که یک شی از کلاس است) از service container واکشی میکند و آن متد static را بر روی متدهای public آن شی اجرا میکند. یعنی در واقع متد get() یکی از متدهای public از کلاس مربوط به آن سرویس است.

هر دو سطر دستور زیر یکسان است:

نگران نحوه و الگوی استفاده از container نباشید در اینجا فقط router خودمان را از آن درخواست کردیم تا متد get() آنرا اجرا کنیم.

اگر بخواهیم خیلی دقیقتر نحوه کار یک Facade را بدانیم بهتر است نگاهی به داخل کدهای آن بیندازیم. Facades های فریم‌ورک لاراول در namespace:

 Illuminate\Support\Facades

قرار دارند. و از نظر پوشه بندی در:

 vendor/laravel/framework/src/Illuminate/Support/Facades

قرار دارند.

در تصویر زیر لیست تعدادی از کلاسهای Facades را می‌بینید:

بعنوان مثال کدهای Facade  Route اینگونه است:

همانطور که مشاهده می‌کنید اثری از متدهای متداول get() ،post() و… که انتظار داریم وجود ندارد. و تنها چیزی که مشاهده خواهید کرد متد getFacadeAccessor() است. این متد در واقع مشخص می‌کند که Facade در لایه زیرین خود کدام سرویس یا کلاس را بکار می‌گیرد.

در اینجا این متد کلمه ‘router’ را برگردانده است پس این Facade  سرویس router از service container را واکشی می‌کند و در لایه زیرین خود استفاده می‌کند. سرویس router یک شی از یک کلاس است که دارای متدهای get() ،post() و… بصورت public است. که متد public مورد نظر همچون get() یا post() آن فراخوانی شود.

خوب گفتیم که متدها در داخل کلاس Facade قرار ندارد پس گرفتن یک نمونه از service container  و فراخوانی متد مربوطه چگونه انجام خواهد شد؟ پاسخ این سوالات در داخل کلاس Facade که پدر کلاسهای Facade است قرار دارد. در تصویر زیر بخش کوچکی از کلاس Facade را مشاهده میکنید:

مهمترین قسمت این کلاس متد __callstatic() میباشد. که کدهای آنرا در زیر می‌بینید:

قسمت ابتدایی این متد سرویس مورد نظر(یک شی از کلاس) را برمی‌گرداند و در بخش دوم یک کنترل خطا صورت می‌گیرد که آیا توانسته است $instance را با سرویس مورد نظر پر کند یا نه و در قسمت پایانی فقط متدی با نام $method از شی $instance را با پارامترهای $args اجرا می‌کند. و خروجی را برمی‌گرداند.

Facades های فریم‌ورک لاراول ارتباط بسیار تنگاتنگی با Dependency Injection لاراول دارد. و برای یادگیری بهتر است ابتدا با Dependency Injection و Service Container آشنا شوید.

البته انشالله دریک مقاله جداگانه به مبحث Dependency Injection و Service Container را خواهم پرداخت.

نحوه ساخت Facade:

ساختن یک Facade کاری بسیار ساده است. ابتدا کار را با ساختن یک کلاس بعنوان سرویس شروع می‌کنیم. کلاس Greeter را مطابق تصویر زیر در پوشه app ایجاد می‌کنیم(طبق استاندارد اسم فایل‌ها با اسم کلاس‌ها یکسان است). این کلاس کار خاصی انجام نمی‌دهد و فقط دارای یک متد public با نام greet است که پیام ‘Hello’ را نمایش میدهد.

حالا به سراغ ایجاد خود Facade می‌رویم فقط کافی است در پروژه خود داخل پوشه app یک کلاس مثلا با نام GreeterFacade همانند تصویر زیر ایجاد کنیم. همانطور که مشاهده می‌کنید در این کلاس باید از کلاس Facade ارث برد.

حالا فقط کافیست در کلاس فرزند، متد protected static getFacadeAccessor() را پیاده سازی نمود. پیاده سازی این متد بسیار ساده است. یا باید نام یک کلاس را return کنیم و یا نام سرویس موردنظرمان را return کنیم. در این مثال ما نام کامل کلاس را بصورت Greeter::class برگردانده‌ایم.

سوالی که ممکن است پیش بیاید این است که آیا واقعا داخل متد getFacadeAccessor() می‌توانیم نام کلاس برگردانیم؟ خیر، نکته‌ای که در این مساله وجود دارد این است که Dependency Injection داخل service container دنبال سرویسی با نام کلاس می‌گردد ولی پس از اینکه آنرا پیدا نکرد این نام را بعنوان اسم کلاس درنظر گرفته و یک نمونه از آن کلاس را میسازد و در service container قرار می‌دهد.

تست کردن Facade ساخته شده:

برای اینکه Facade ایجاد شده را تست کنیم یک تکه کد در فایل routes/web.php قرار می‌دهیم:

حالا اینجا یه سوال پیش میاد که چرا Facade ایجاد شده توسط ما خیلی کوتاه نیست تا یک کد تمیز‌تر داشته باشیم یعنی درواقع از namespace استفاده شده است؟!

جواب این سوال را باید در ساختن alias برای کلاس پیدا کرد.

نحوه ساخت alias برای Facade:

همانطور که می‌دانید برای استفاده کردن از یک کلاس باید شما آنرا توسط دستورات require یا include زبان PHP در پروژه خود وارد کنید درحالی که در برنامه‌نویسی با فریم‌ورک‌ها از جمله لاراول این کار را انجام نمی‌دهید در واقع فریم‌ورک‌ها خودشان این کار را انجام می‌دهند. وارد کردن کلاس‌ها به داخل پروژه از طریق autoload انجام می‌شود. این autoload ها در واقع تنظیماتی دارند که مشخص می‌کنند فایل مرتبط با یک namespace و کلاس را چگونه یافته و در پروژه وارد کنند.

Alias ها در واقع تنظیماتی هستند که مشخص می‌کنند فایل این اسامی کلاس‌ها که بدون namespace بکار رفته‌اند را از کجا باید پیدا کنند. در اصل نام کامل کلاس آنها با namespace چیست.

این تنظیمات در فایل config/app.php قراردارند. در این فایل کلاً یک آرایه بزرگ وجود دارد که همانطور که در تصویر زیر می‌بینید یکی از کلیدهای این آرایه کلید aliases است:

در کلید aliases آرایه‌ای با کلیدهایی با نام مستعار مورد نظر و مقدار حاوی نام اصلی کلاس با namespace آن قرار دارد. یعنی مثلاً اگر شما می‌خواهید کلاس Route را بدون هیچ namespace ای فراخوانی کنید فقط کافیست مقدار

'Route' => Illuminate\Support\Facades\Route::class,

جزو عناصر این آرایه باشد.

بعنوان مثال قسمتی از کلید aliases را در زیر میبینید:

ما مقدار

'Greet' => App\GreeterFacade::class,

را بعنوان یکی از عناصر آرایه aliases وارد می‌کنیم و سپس در فایل routes/web.php می‌توانیم کد خود را بصورت زیر در بیاوریم:

 


توسط: حمیدرضا صدوقی

منبع: سایت ویــدیــلــی


 

منابع:

Book:

  • Leanpub – CODE SMART
    The Laravel framework version 5 for beginners
    By: Dayle Rees

Video Tutorial:

  • Laracasts – Laravel 6 from scratch
  • Laracasts – Laravel Explained