مفهوم Facade در لاراول
Facade در لاراول یک رابط استاتیک برای کلاس هایی که در داخل Service Container در دسترس هستند ارائه می کند. لاراول به صورت پیشفرض facade های بسیاری دارد که تقریبا دسترسی به همه ویژگی های لاراول را فراهم می کنند. Facade های لاراول به عنوان “پروکسی های استاتیک” برای کلاس های اصلی موجود در Service Container عمل می کنند. آن ها در مقایسه با متدهای استاتیک قدیمی قابلیت تست پذیری و انعطاف بیشتری دارند.
تمام facade های لاراول تحت فضای نام IlluminateSupportFacades تعریف شده اند. بنابراین برای دسترسی به آن ها می توانیم ماننده نمونه زیر عمل کنیم:
use IlluminateSupportFacadesCache; use IlluminateSupportFacadesRoute; Route::get('/cache', function () { return Cache::get('key'); });
123456 | use IlluminateSupportFacadesCache;use IlluminateSupportFacadesRoute; Route::get('/cache', function () { return Cache::get('key');}); |
توابع کمکی
برای تکمیل کار facade ها، لاراول انواع مختلفی از توابع کمکی سراسری (Global) را ارائه می دهد که با کمک آن ها تعامل با ویژگی های رایج فریم ورک آسان تر می شود. برخی از این توابع کمکی رایج عبارت اند از: view، response، url، config و غیره. برای مثال برای تولید یک پاسخ از نوع JSON به جای استفاده از IlluminateSupportFacadesResponse می توانیم از تابع کمکی response استفاده کنیم. همانطور که گفتیم توابع کمکی به صورت سراسری در دسترس هستند، نیازی به مشخص کردن هیچ گونه فضای نامی نیست. نمونه کد:
use IlluminateSupportFacadesResponse; Route::get('/users', function () { return Response::json([ // … ]); }); Route::get('/users', function () { return response()->json([ // … ]); });
123456789101112 | use IlluminateSupportFacadesResponse; Route::get('/users', function () { return Response::json([ // … ]);});Route::get('/users', function () { return response()->json([ // … ]);}); |
چه زمانی باید از Facade ها استفاده کنیم
Facade ها مزایای زیادی دارند. آن ها یک سینتکس مختصر و به یاد ماندنی ارائه می کنند که به توسعه دهنده اجازه می دهد تا بدون به خاطر سپردن نام طولانی کلاس ها که باید به صورت دستی تزریق یا پیکربندی شوند، از ویژگی های لاراول استفاده کنید. علاوه بر این، به دلیل استفاده از قابلیت متدهای پویا در زبان PHP، تست کردن آن ها نیز آسان است.
با این حال هنگام استفاده از facade ها باید به بعضی از موارد توجه داشته باشید. اصلی ترین خطر استفاده از facade ها، خزش دامنه (Scope Creep) است. از آنجا که استفاده از facade ها بسیار آسان است و نیازی به هیچ گونه تزریق ندارد، کلاس های استفاده کننده از آن ها این قابلیت را پیدا می کنند تا بدون جلب توجه با استفاده از facade های زیاد رشد کرده و بزرگتر شوند. اما زمانی وابستگی های کلاس تزریق شوند این پتانسیل کاهش می یابد و دلیل آن هم این است که سازنده کلاس هر چقدر که تزریق بیشتری داشته باشد، بزرگتر به نظر می رسد و شما با مشاهده آن سازنده متوجه می شوید که کلاس شما بیش از حد بزرگ شده است. پس مهم ترین نکته در هنگام استفاده از facade ها توجه به اندازه کلاس است تا دامنه مسئولیت و کارهایی که انجام می دهد محدود بماند. اگر کلاس شما خیلی بزرگ شد، می توانید آن را به کلاس های کوچکتر تقسیم کنید.
Facade ها در مقابل تزریق وابستگی
یکی از مزایای اصلی تزریق وابستگی امکان جایگزین کردن یک وابستگی با پیادهسازی های مختلف است. این موضوع در هنگام تست کردن به درد می خورد زیرا می توانیم یک پیادهسازی ساختگی و الکی را به جای پیادهسازی اصلی تزریق کنیم.
به طور معمول امکان ساخت یک نمونه ساختگی از متد مربوط به یک کلاس استاتیک وجود ندارد. با این حال از آنجا که facade ها از قابلیت متدهای پویا در PHP استفاده می کنند امکان تست کردن آن ها نیز مانند کلاس های تزریق شده فراهم می شود. برای مثال به تکه کد زیر توجه کنید:
use IlluminateSupportFacadesCache; Route::get('/cache', function () { return Cache::get('key'); });
12345 | use IlluminateSupportFacadesCache; Route::get('/cache', function () { return Cache::get('key');}); |
با استفاده از متدهای مربوط به تست کردن facade در لاراول، می توانیم برای مثال فوق به این شکل تست بنویسم تا مطمئن شویم که متد Cache::get با آرگومانی که انتظار داریم فراخوانی می شود:
use IlluminateSupportFacadesCache; /** * A basic functional test example. * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $response = $this->get('/cache'); $response->assertSee('value'); }
1234567891011121314151617 | use IlluminateSupportFacadesCache; /** * 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 ها، لاراول شامل توابع کمکی مختلفی است که می توانند به منظور انجام عملیات رایج مانند تولید ویوها، انتشار رویدادها توزیع کارها و یا ارسال پاسخ HTTP استفاده شوند. بسیاری از این توابع کمکی همان کار facade مربوطه انجام می دهند. در نمونه کد زیر هر دو خط یک کار مشابه را انجام می دهند:
return IlluminateSupportFacadesView::make('profile'); return view('profile');
123 | return IlluminateSupportFacadesView::make('profile'); return view('profile'); |
مطلقاً هیچ تفاوت عملی بین facade و تابع کمکی وجود ندارد. به این معنی که هنگام استفاده از توابع کمکی می توانید آنها را دقیقا مانند facade مربوطه تست کنید. برای مثال به کد زیر توجه کنید:
Route::get('/cache', function () { return cache('key'); });
123 | Route::get('/cache', function () { return cache('key');}); |
در اصل تابع کمکی cache متد get که در کلاس Cache تعریف شده است را فراخوانی می کند. بنابراین اگرچه ما از یک تابع کمکی استفاده کرده ایم، می توانیم مانند یک facade برای آن تست بنویسم:
use IlluminateSupportFacadesCache; /** * A basic functional test example. * * @return void */ public function testBasicExample() { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $response = $this->get('/cache'); $response->assertSee('value'); }
1234567891011121314151617 | use IlluminateSupportFacadesCache; /** * 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 یک کلاسی است که دسترسی به یک شیء از طریق Container را فراهم می کند. مکانیزمی که این سیستم با آن کار می کند در داخل کلاس IlluminateSupportFacadesFacade که کلاس پایه برای همه facade ها است تعریف شده است. در داخل کلاس پایه برای فراخوانی توابع مورد نظر از متد __callStatic() استفاده می شود. در مثال زیر از متد get برای گرفتن مقدار کش شده استفاده شده است. در نگاه اول شاید فکر کنید که متد استاتیک get به کلاس Cache تعلق دارد:
<?php namespace AppHttpControllers; use AppHttpControllersController; use IlluminateSupportFacadesCache; 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]); } }
12345678910111213141516171819202122 | <?php namespace AppHttpControllers; use AppHttpControllersController;use IlluminateSupportFacadesCache; 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]); }} |
اما اگر به پیادهسازی کلاس IlluminateSupportFacadesCache نگاه کنید مشاهده خواهید کرد که هیچ متد استاتیکی در آن وجود ندارد:
class Cache extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'cache'; } }
123456789 | class Cache extends Facade{ /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'cache'; }} |
در کد فوق کلاس Cache کلاس پایه Facade را توسعه می دهد و یک متد با نام getFacadeAccessor() نیز تعریف کرده است. کار این متد بازگرداندن نام سرویس مربوطه در Service Container است. زمانی که متدهای استاتیک بر روی facade مربوط به Cache فراخوانی می شود، لاراول با استفاده از متد گفته شده سرویس مورد نیاز را از Container میگیرد و متد درخواست شده (در این مورد get) را بر روی آن سرویس اجرا می کند.
Facade های موجود در لاراول
در جدول زیر می توانید facade های موجود در فریم ورک لاراول را مشاهده کنید.
Facade |
Class |
Service Container Binding |
App |
IlluminateFoundationApplication |
app |
Artisan |
IlluminateContractsConsoleKernel |
artisan |
Auth |
IlluminateAuthAuthManager |
auth |
Auth (Instance) |
IlluminateContractsAuthGuard |
auth.driver |
Blade |
IlluminateViewCompilersBladeCompiler |
blade.compiler |
Broadcast |
IlluminateContractsBroadcastingFactory |
|
Broadcast (Instance) |
IlluminateContractsBroadcastingBroadcaster |
|
Bus |
IlluminateContractsBusDispatcher |
|
Cache |
IlluminateCacheCacheManager |
cache |
Cache (Instance) |
IlluminateCacheRepository |
cache.store |
Config |
IlluminateConfigRepository |
config |
Cookie |
IlluminateCookieCookieJar |
cookie |
Crypt |
IlluminateEncryptionEncrypter |
encrypter |
Date |
IlluminateSupportDateFactory |
date |
DB |
IlluminateDatabaseDatabaseManager |
db |
DB (Instance) |
IlluminateDatabaseConnection |
db.connection |
Event |
IlluminateEventsDispatcher |
events |
File |
IlluminateFilesystemFilesystem |
files |
Gate |
IlluminateContractsAuthAccessGate |
|
Hash |
IlluminateContractsHashingHasher |
hash |
Http |
IlluminateHttpClientFactory |
|
Lang |
IlluminateTranslationTranslator |
translator |
Log |
IlluminateLogLogManager |
log |
|
IlluminateMailMailer |
mailer |
Notification |
IlluminateNotificationsChannelManager |
|
Password |
IlluminateAuthPasswordsPasswordBrokerManager |
auth.password |
Password (Instance) |
IlluminateAuthPasswordsPasswordBroker |
auth.password.broker |
Queue |
IlluminateQueueQueueManager |
queue |
Queue (Instance) |
IlluminateContractsQueueQueue |
queue.connection |
Queue (Base Class) |
IlluminateQueueQueue |
|
Redirect |
IlluminateRoutingRedirector |
redirect |
Redis |
IlluminateRedisRedisManager |
redis |
Redis (Instance) |
IlluminateRedisConnectionsConnection |
redis.connection |
Request |
IlluminateHttpRequest |
request |
Response |
IlluminateContractsRoutingResponseFactory |
|
Response (Instance) |
IlluminateHttpResponse |
|
Route |
IlluminateRoutingRouter |
router |
Schema |
IlluminateDatabaseSchemaBuilder |
|
Session |
IlluminateSessionSessionManager |
session |
Session (Instance) |
IlluminateSessionStore |
session.store |
Storage |
IlluminateFilesystemFilesystemManager |
filesystem |
Storage (Instance) |
IlluminateContractsFilesystemFilesystem |
filesystem.disk |
URL |
IlluminateRoutingUrlGenerator |
url |
Validator |
IlluminateValidationFactory |
validator |
Validator (Instance) |
IlluminateValidationValidator |
|
View |
IlluminateViewFactory |
view |
View (Instance) |
IlluminateViewView |