Может войдёшь?
Черновики Написать статью Профиль

Фасады

перевод документация 5.х

  1. 1. Введение
  2. 2. Когда стоит использовать фасады
    1. 2.1. Фасады или внедрение зависимостей
    2. 2.2. Фасады или вспомогательные функции
  3. 3. Как работают фасады
  4. 4. Создание фасадов
    1. 4.1. Об автозагрузке псевдонимов
  5. 5. Фасады-заглушки
  6. 6. Соответствие фасадов и классов
Этот перевод актуален для англоязычной документации на (ветка 5.3) , (ветка 5.2) , (ветка 5.1) и (ветка 5.0). Опечатка? Выдели и нажми Ctrl+Enter.

Введение

Фасады предоставляют «статический» интерфейс к классам, доступным в сервис-контейнере. Laravel поставляется со множеством фасадов, предоставляющих доступ почти ко всем возможностям Laravel. Фасады Laravel служат «статическими прокси» для классов в сервис-контейнере, обеспечивая преимущества краткого выразительного синтаксиса, и поддерживая большую тестируемость и гибкость, чем традиционные статические методы.

+ 5.3

добавлено в 5.3 ()

Все фасады Laravel определены в пространстве имён Illuminate\Support\Facades. Поэтому мы можем легко обратиться к фасаду таким образом:

PHP
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
  return 
Cache::get('key');
});

Во многих примерах в документации по Laravel используются фасады для демонстрации различных возможностей фреймворка.

Когда стоит использовать фасады

Фасады имеют много преимуществ. Они обеспечивают лаконичный, запоминающийся синтаксис, который позволяет вам использовать возможности Laravel, не запоминая длинные имена классов, которые должны внедряться или настраиваться вручную. Более того, благодаря уникальному использованию динамических методов PHP они легко тестируются.

Тем не менее, при использовании фасадов необходимо соблюдать некоторые предосторожности. Основная опасность фасадов — разрастание границ класса. Поскольку фасады так просты в использовании и не требуют внедрения, становится очень вероятна ситуация, когда ваши классы постоянно продолжают расти, и когда в одном классе используется множество фасадов. При использовании внедрения зависимостей риск возникновения такой ситуации снижается за счёт того, что при виде большого конструктора вы можете визуально оценить увеличение размера вашего класса. Поэтому при использовании фасадов обратите особое внимание на размер вашего класса, чтобы границы его ответственности оставались узкими.

При создании стороннего пакета, взаимодействующего с Laravel, лучше внедрять контракты Laravel вместо использования фасадов. Поскольку пакеты создаются вне самого Laravel, у вас не будет доступа к вспомогательным функциям тестирования фасадов.

Фасады или внедрение зависимостей

Одно из основных преимуществ внедрения зависимостей — возможность подменить реализацию внедрённого класса. Это полезно при тестировании, потому что вы можете внедрить макет или заглушку, в которой якобы будут вызываться различные методы.

Обычно невозможно сделать макет или заглушку метода настоящего статического класса. Но поскольку фасады используют динамические методы для передачи вызовов к объектам, извлекаемым из сервис-контейнера, мы можем тестировать фасад точно так же, как тестировали бы внедрённый экземпляр класса. Например, возьмём следующий маршрут:

PHP
use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
  return 
Cache::get('key');
});

Мы можем написать следующий тест, чтобы проверить, что метод PHPCache::get() был вызван с ожидаемым нами аргументом:

PHP
use Illuminate\Support\Facades\Cache;

/**
 * Пример базового функционального теста.
 *
 * @return void
 */
public function testBasicExample()
{
  
Cache::shouldReceive('get')
       ->
with('key')
       ->
andReturn('value');

  
$this->visit('/cache')
       ->
see('value');
}

Фасады или вспомогательные функции

Кроме фасадов в Laravel есть множество «вспомогательных» функций, которые могут выполнять общие задачи, такие как генерация представлений, запуск событий, постановка задач и отправка HTTP-откликов. Многие из этих вспомогательных функций выполняют те же задачи, что и соответствующий фасад. Например, эти вызовы фасада и вспомогательной функции эквивалентны:

PHP
return View::make('profile');

return 
view('profile');

Нет абсолютно никакой фактической разницы между фасадами и вспомогательными функциями. При использовании вспомогательных функций вы можете тестировать их точно так же, как тестировали бы соответствующий фасад. Например, возьмём следующий маршрут:

PHP
Route::get('/cache', function () {
  return 
cache('key');
});

При этом вспомогательная функция PHPcache() вызовет метод PHPget() на классе, лежащем в основе фасада PHPCache. Поэтому несмотря на то, что мы используем вспомогательную функцию, мы можем написать следующий тест, чтобы проверить, что метод был вызван с ожидаемым нами аргументом:

PHP
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
<?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():

PHP
class Cache extends Facade
{
  
/**
   * Получить зарегистрированное имя компонента.
   *
   * @return string
   */
  
protected static function getFacadeAccessor() { return 'cache'; }
}

Вместо этого, фасад Cache наследует базовый класс PHPFacade и определяет метод PHPgetFacadeAccessor(). Его задача — вернуть имя привязки в сервис-контейнере. Когда вы обращаетесь к любому статическому методу фасада Cache, Laravel получает по привязке объект cache из сервис-контейнера и вызывает на нём требуемый метод (в этом случае — PHPget()).

+ 5.0

добавлено в 5.0 ()

Таким образом, вызов PHPCache::get() может быть переписан так:

PHP
$value $app->make('cache')->get('key');

Импорт фасадов

Помните, если вы используете фасад в контроллере, который находится в пространстве имён, то вам надо импортировать класс фасада в пространство имён. Все фасады живут в глобальном пространстве имён:

PHP
<?php namespace App\Http\Controllers;

use 
Cache;

class 
PhotosController extends Controller {

  
/**
   * Получить все фотографии приложения.
   *
   * @return Response
   */
  
public function index()
  {
    
$photos Cache::get('photos');

    
//
  
}

}

Создание фасадов

Создать фасад в вашем приложении или пакете довольно просто. Вам нужны только три вещи:

  1. Привязка в сервис-контейнере.
  2. Класс-фасад.
  3. Настройка для псевдонима фасада.

Посмотрим на следующий пример. Здесь определён класс PHPPaymentGateway\Payment:

PHP
namespace PaymentGateway;

class 
Payment {

  public function 
process()
  {
    
//
  
}

}

Нам нужно, чтобы этот класс извлекался из сервис-контейнера, поэтому давайте добавим для него привязку (binding):

PHP
App::bind('payment', function()
{
  return new \
PaymentGateway\Payment;
});

Самое лучшее место для регистрации этой привязки — новый поставщик услуг, который мы назовём PHPPaymentServiceProvider и в котором мы создадим метод PHPregister(), содержащий представленный выше код. После этого вы можете настроить Laravel для загрузки этого поставщика в файле config/app.php.

Дальше мы можем создать класс нашего фасада:

PHP
use Illuminate\Support\Facades\Facade;

class 
Payment extends Facade {

  protected static function 
getFacadeAccessor() { return 'payment'; }

}

Наконец, по желанию можно добавить псевдоним (alias) для этого фасада в массив aliases файла настроек config/app.php. Тогда мы сможем вызывать метод PHPprocess() на экземпляре класса PHPPayment:

PHP
Payment::process();

Об автозагрузке псевдонимов

В некоторых случаях классы в массиве aliases не доступны из-за того, что PHP не загружает неопределённые классы при указании их типов. Если PHP\ServiceWrapper\ApiTimeoutException имеет псевдоним PHPApiTimeoutException, то PHPcatch (ApiTimeoutException $e), помещённый в любое пространство имён, кроме PHPServiceWrapper, никогда не «поймает» исключение, даже когда оно возникнет. Аналогичная проблема возникает в классах, которые содержат указания типов на классы с псевдонимами. Единственное решение — не использовать псевдонимы и вместо них в начале каждого файла, где это необходимо, писать PHPuse для тех классов, типы которых вы хотите указать.

Фасады-заглушки

Юнит-тесты играют важную роль в том, как именно работают фасады. На самом деле возможность тестирования — основная причина, по которой фасады вообще существуют. Эта тема подробнее раскрыта в разделе документации о фасадах-заглушках.

Соответствие фасадов и классов

В таблице ниже перечислены все фасады и соответствующие им классы. Это полезный инструмент для быстрого начала работы с документацией по API данного корня фасадов. Также указаны ключи привязок в сервис-контейнере, где это нужно.

Фасад Класс Привязка в сервис-контейнере
AppIlluminate\Foundation\Applicationapp
Artisan5.3, 5.2 и 5.1 Illuminate\Contracts\Console\Kernel
5.0 Illuminate\Console\Application
artisan
AuthIlluminate\Auth\AuthManagerauth
Auth (экземпляр)Illuminate\Auth\Guard только для 5.1 и ранее
BladeIlluminate\View\Compilers\BladeCompilerblade.compiler
BusIlluminate\Contracts\Bus\Dispatcher
Cache5.3, 5.2 и 5.1 Illuminate\Cache\Repository
5.0 Illuminate\Cache\CacheManager
cache
ConfigIlluminate\Config\Repositoryconfig
CookieIlluminate\Cookie\CookieJarcookie
CryptIlluminate\Encryption\Encrypterencrypter
DBIlluminate\Database\DatabaseManagerdb
DB (экземпляр)Illuminate\Database\Connection
EventIlluminate\Events\Dispatcherevents
FileIlluminate\Filesystem\Filesystemfiles
Form5.0 Illuminate\Html\FormBuilderform
GateIlluminate\Contracts\Auth\Access\Gate
HashIlluminate\Contracts\Hashing\Hasherhash
InputIlluminate\Http\Request только для 5.1 и ранееrequest
LangIlluminate\Translation\Translatortranslator
LogIlluminate\Log\Writerlog
MailIlluminate\Mail\Mailermailer
NotificationIlluminate\Notifications\ChannelManager для 5.3 и выше
Password5.3, 5.2 и 5.1 Illuminate\Auth\Passwords\PasswordBroker
5.0 Illuminate\Auth\Reminders\PasswordBroker
auth.password
auth.reminder
QueueIlluminate\Queue\QueueManagerqueue
Queue (экземпляр)Illuminate\Queue\QueueInterface для 5.1 и ранее
Illuminate\Contracts\Queue\Queue для 5.2 и выше

queue
Queue (базовый класс)Illuminate\Queue\Queue
RedirectIlluminate\Routing\Redirectorredirect
RedisIlluminate\Redis\Databaseredis
RequestIlluminate\Http\Requestrequest
ResponseIlluminate\Contracts\Routing\ResponseFactory
RouteIlluminate\Routing\Routerrouter
SchemaIlluminate\Database\Schema\Blueprint
SessionIlluminate\Session\SessionManagersession
Session (экземпляр)Illuminate\Session\Store
StorageIlluminate\Contracts\Filesystem\Factoryfilesystem
URLIlluminate\Routing\UrlGeneratorurl
ValidatorIlluminate\Validation\Factoryvalidator
Validator (экземпляр)Illuminate\Validation\Validator
ViewIlluminate\View\Factoryview
View (экземпляр)Illuminate\View\View

Комментарии (2)

Oks_1984

Так, все-таки , пытаюсь разобраться фасады в Laravel — подпадают под определение фасадов банды четырех в ооп? Если нет, в чем разница?

santaatnas

Да, попадают.

Написать комментарий

Разметка: ? ?

Авторизуйся, чтобы прокомментировать.