Фасады

Введение

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

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

use Illuminate\Support\Facades\Cache;

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

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

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

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

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

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

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

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

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

use Illuminate\Support\Facades\Cache;

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

Мы можем написать следующий тест, чтобы проверить, что метод Cache::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');
});

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

use Illuminate\Support\Facades\Cache;

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

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

Как работают фасады

В контексте приложения на Laravel, фасад - это класс, который предоставляет доступ к объекту в контейнере. Весь этот механизм реализован в классе Facade. Фасады как Laravel, так и ваши собственные, наследуют этот базовый класс Illuminate\Support\Facades\Facade.

Класс Facade использует магический метод PHP __callStatic() для перенаправления вызовов методов с вашего фасада на полученный объект из контейнера. В примере ниже делается обращение к механизму кэширования Laravel. На первый взгляд может показаться, что статический метод get принадлежит классу Cache:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;

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, то увидите, что он не содержит метода get:

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

Вместо этого фасад Cache расширяется на базе класса Facade и определяет метод getFacadeAccessor(). Задача этого метода - вернуть строковое имя (ключ) привязки объекта в сервис-контейнере. Когда вы обращаетесь к любому статическому методу фасада Cache, Laravel получает объект cache из сервис-контейнера и вызывает у него требуемый метод (в этом случае - get).

Справочное описание классов фасадов

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

Фасад Класс Привязка сервис-контейнера
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\Repository cache
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection  
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue
Queue (Base Class) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint  
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store  
Storage Illuminate\Contracts\Filesystem\Factory filesystem
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (Instance) Illuminate\Validation\Validator  
View Illuminate\View\Factory view
View (Instance) Illuminate\View\View