Введение
Фасады предоставляют «статический» интерфейс к классам, доступным в сервис-контейнере. Laravel поставляется со множеством фасадов, предоставляющих доступ почти ко всем возможностям Laravel. Фасады Laravel служат «статическими прокси» для классов в сервис-контейнере, обеспечивая преимущества краткого выразительного синтаксиса, и поддерживая большую тестируемость и гибкость, чем традиционные статические методы.
добавлено в 5.3 ()
Все фасады Laravel определены в пространстве имён Illuminate\Support\Facades. Поэтому мы можем легко обратиться к фасаду таким образом:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Во многих примерах в документации по Laravel используются фасады для демонстрации различных возможностей фреймворка.
Когда стоит использовать фасады
Фасады имеют много преимуществ. Они обеспечивают лаконичный, запоминающийся синтаксис, который позволяет вам использовать возможности Laravel, не запоминая длинные имена классов, которые должны внедряться или настраиваться вручную. Более того, благодаря уникальному использованию динамических методов PHP они легко тестируются.
Тем не менее, при использовании фасадов необходимо соблюдать некоторые предосторожности. Основная опасность фасадов — разрастание границ класса. Поскольку фасады так просты в использовании и не требуют внедрения, становится очень вероятна ситуация, когда ваши классы постоянно продолжают расти, и когда в одном классе используется множество фасадов. При использовании внедрения зависимостей риск возникновения такой ситуации снижается за счёт того, что при виде большого конструктора вы можете визуально оценить увеличение размера вашего класса. Поэтому при использовании фасадов обратите особое внимание на размер вашего класса, чтобы границы его ответственности оставались узкими.
При создании стороннего пакета, взаимодействующего с Laravel, лучше внедрять контракты Laravel вместо использования фасадов. Поскольку пакеты создаются вне самого Laravel, у вас не будет доступа к вспомогательным функциям тестирования фасадов.
Фасады или внедрение зависимостей
Одно из основных преимуществ внедрения зависимостей — возможность подменить реализацию внедрённого класса. Это полезно при тестировании, потому что вы можете внедрить макет или заглушку, в которой якобы будут вызываться различные методы.
Обычно невозможно сделать макет или заглушку метода настоящего статического класса. Но поскольку фасады используют динамические методы для передачи вызовов к объектам, извлекаемым из сервис-контейнера, мы можем тестировать фасад точно так же, как тестировали бы внедрённый экземпляр класса. Например, возьмём следующий маршрут:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Мы можем написать следующий тест, чтобы проверить, что метод PHPCache::get()
был вызван с ожидаемым нами аргументом:
use Illuminate\Support\Facades\Cache;
/**
* Пример базового функционального теста.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Фасады или вспомогательные функции
Кроме фасадов в Laravel есть множество «вспомогательных» функций, которые могут выполнять общие задачи, такие как генерация представлений, запуск событий, постановка задач и отправка HTTP-откликов. Многие из этих вспомогательных функций выполняют те же задачи, что и соответствующий фасад. Например, эти вызовы фасада и вспомогательной функции эквивалентны:
return View::make('profile');
return view('profile');
Нет абсолютно никакой фактической разницы между фасадами и вспомогательными функциями. При использовании вспомогательных функций вы можете тестировать их точно так же, как тестировали бы соответствующий фасад. Например, возьмём следующий маршрут:
Route::get('/cache', function () {
return cache('key');
});
При этом вспомогательная функция PHPcache()
вызовет метод PHPget()
на классе, лежащем в основе фасада PHPCache
. Поэтому несмотря на то, что мы используем вспомогательную функцию, мы можем написать следующий тест, чтобы проверить, что метод был вызван с ожидаемым нами аргументом:
use Illuminate\Support\Facades\Cache;
/**
* Пример базового функционального теста.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Как работают фасады
В Laravel-приложении фасад — это класс, который предоставляет доступ к объекту в контейнере. Весь этот механизм реализован в классе PHPFacade
. И фасады Laravel, и ваши собственные, наследуют базовый класс Illuminate\Support\Facades\Facade.
Базовый класс PHPFacade
использует магический метод PHP PHP__callStatic()
для перенаправления вызовов методов с вашего фасада на полученный объект. В примере ниже делается обращение к механизму кэширования Laravel. На первый взгляд может показаться, что метод PHPget()
принадлежит классу PHPCache
:
<?php
namespace App\Http\Controllers;
use Cache;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* Показать профиль данного пользователя.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
Обратите внимание, в начале мы «импортируем» фасад Cache. Этот фасад служит переходником для доступа к лежащей в основе реализации интерфейса Illuminate\Contracts\Cache\Factory. Все выполняемые через этот фасад вызовы передаются ниже — в экземпляр кэш-сервиса Laravel.
Если вы посмотрите в исходный код класса Illuminate\Support\Facades\Cache, то увидите, что он не содержит метода PHPget()
:
class Cache extends Facade
{
/**
* Получить зарегистрированное имя компонента.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
Вместо этого, фасад Cache наследует базовый класс PHPFacade
и определяет метод PHPgetFacadeAccessor()
. Его задача — вернуть имя привязки в сервис-контейнере. Когда вы обращаетесь к любому статическому методу фасада Cache, Laravel получает по привязке объект cache из сервис-контейнера и вызывает на нём требуемый метод (в этом случае — PHPget()
).
добавлено в 5.0 ()
Таким образом, вызов PHPCache::get()
может быть переписан так:
$value = $app->make('cache')->get('key');
Помните, если вы используете фасад в контроллере, который находится в пространстве имён, то вам надо импортировать класс фасада в пространство имён. Все фасады живут в глобальном пространстве имён:
<?php namespace App\Http\Controllers;
use Cache;
class PhotosController extends Controller {
/**
* Получить все фотографии приложения.
*
* @return Response
*/
public function index()
{
$photos = Cache::get('photos');
//
}
}
Создание фасадов
Создать фасад в вашем приложении или пакете довольно просто. Вам нужны только три вещи:
- Привязка в сервис-контейнере.
- Класс-фасад.
- Настройка для псевдонима фасада.
Посмотрим на следующий пример. Здесь определён класс PHPPaymentGateway\Payment
:
namespace PaymentGateway;
class Payment {
public function process()
{
//
}
}
Нам нужно, чтобы этот класс извлекался из сервис-контейнера, поэтому давайте добавим для него привязку (binding):
App::bind('payment', function()
{
return new \PaymentGateway\Payment;
});
Самое лучшее место для регистрации этой привязки — новый поставщик услуг, который мы назовём PHPPaymentServiceProvider
и в котором мы создадим метод PHPregister()
, содержащий представленный выше код. После этого вы можете настроить Laravel для загрузки этого поставщика в файле config/app.php.
Дальше мы можем создать класс нашего фасада:
use Illuminate\Support\Facades\Facade;
class Payment extends Facade {
protected static function getFacadeAccessor() { return 'payment'; }
}
Наконец, по желанию можно добавить псевдоним (alias) для этого фасада в массив aliases файла настроек config/app.php. Тогда мы сможем вызывать метод PHPprocess()
на экземпляре класса PHPPayment
:
Payment::process();
Об автозагрузке псевдонимов
В некоторых случаях классы в массиве aliases не доступны из-за того, что PHP не загружает неопределённые классы при указании их типов. Если PHP\ServiceWrapper\ApiTimeoutException
имеет псевдоним PHPApiTimeoutException
, то PHPcatch (ApiTimeoutException $e)
, помещённый в любое пространство имён, кроме PHPServiceWrapper
, никогда не «поймает» исключение, даже когда оно возникнет. Аналогичная проблема возникает в классах, которые содержат указания типов на классы с псевдонимами. Единственное решение — не использовать псевдонимы и вместо них в начале каждого файла, где это необходимо, писать PHPuse
для тех классов, типы которых вы хотите указать.
Фасады-заглушки
Юнит-тесты играют важную роль в том, как именно работают фасады. На самом деле возможность тестирования — основная причина, по которой фасады вообще существуют. Эта тема подробнее раскрыта в разделе документации о фасадах-заглушках.
Соответствие фасадов и классов
В таблице ниже перечислены все фасады и соответствующие им классы. Это полезный инструмент для быстрого начала работы с документацией по API данного корня фасадов. Также указаны ключи привязок в сервис-контейнере, где это нужно.
Комментарии (2)
Так, все-таки , пытаюсь разобраться фасады в Laravel — подпадают под определение фасадов банды четырех в ооп? Если нет, в чем разница?
Да, попадают.