Фасады
- Введение
- Когда использовать фасады
- Как работают фасады
- Фасады в реальном времени
- Справочник классов фасадов
Введение
В документации Laravel Вы увидите примеры кода, который взаимодействует с функциями Laravel через «фасады». Фасады предоставляют «статический» интерфейс для классов, доступных в сервисном контейнере приложения. Laravel поставляется с множеством фасадов, которые обеспечивают доступ почти ко всем функциям Laravel.
Фасады Laravel служат «статическими прокси» для базовых классов в сервисном контейнере, обеспечивая преимущества краткого выразительного синтаксиса при сохранении большей тестируемости и гибкости, чем традиционные статические методы. Это прекрасно, если Вы не совсем понимаете, как фасады работают под капотом - просто плывите по течению и продолжайте изучать Laravel.
Все фасады Laravel определены в пространстве имён Illuminate\Support\Facades
. Итак, мы можем легко получить доступ к такому фасаду:
use Illuminate\Support\Facades\Cache;use Illuminate\Support\Facades\Route; Route::get('/cache', function () { return Cache::get('key');});
В документации Laravel во многих примерах будут использоваться фасады для демонстрации различных функций фреймворка.
Helper Functions
To complement facades, Laravel offers a variety of global "helper functions" that make it even easier to interact with common Laravel features. Some of the common helper functions you may interact with are view
, response
, url
, config
, and more. Each helper function offered by Laravel is documented with their corresponding feature; however, a complete list is available within the dedicated helper documentation.
For example, instead of using the Illuminate\Support\Facades\Response
facade to generate a JSON response, we may simply use the response
function. Because helper functions are globally available, you do not need to import any classes in order to use them:
use Illuminate\Support\Facades\Response; Route::get('/users', function () { return Response::json([ // ... ]);}); Route::get('/users', function () { return response()->json([ // ... ]);});
Когда использовать фасады
У фасадов много преимуществ. Они предоставляют краткий, запоминающийся синтаксис, который позволяет Вам использовать функции Laravel, не запоминая длинные имена классов, которые необходимо вводить или настраивать вручную. Более того, благодаря уникальному использованию динамических методов PHP их легко протестировать.
Однако при использовании фасадов необходимо соблюдать некоторую осторожность. Основная опасность фасадов - это класс «ползучесть». Поскольку фасады настолько просты в использовании и не требуют инъекций, можно легко позволить Вашим классам продолжать расти и использовать множество фасадов в одном классе. При использовании внедрения зависимостей этот потенциал снижается за счет визуальной обратной связи, которую дает большой конструктор, что Ваш класс становится слишком большим. Поэтому, используя фасады, обратите особое внимание на размер Вашего класса, чтобы объем его ответственности оставался небольшим. Если Ваш класс становится слишком большим, рассмотрите возможность разделения его на несколько более мелких классов.
Фасады против Внедрения зависимостей
Одним из основных преимуществ внедрения зависимостей является возможность поменять местами реализации внедренного класса. Это полезно во время тестирования, так как Вы можете вставить имитацию или заглушку и утверждать, что для заглушки были вызваны различные методы.
Как правило, невозможно издеваться над действительно статическим методом класса или заглушить его. Однако, поскольку фасады используют динамические методы для проксирования вызовов методов к объектам, разрешенным из контейнера службы, мы фактически можем тестировать фасады так же, как мы тестировали бы внедренный экземпляр класса. Например, учитывая следующий маршрут:
use Illuminate\Support\Facades\Cache; Route::get('/cache', function () { return Cache::get('key');});
Используя методы тестирования фасадов Laravel, мы можем написать следующий тест, чтобы проверить, что метод Cache::get
был вызван с ожидаемым аргументом:
use Illuminate\Support\Facades\Cache; /** * Пример базового функционального теста. * * @return void */public function testBasicExample(){ Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $response = $this->get('/cache'); $response->assertSee('value');}
Фасады против Вспомогательных функций
Помимо фасадов, Laravel включает в себя множество «вспомогательных» функций, которые могут выполнять общие задачи, такие как создание представлений, запуск событий, диспетчеризация заданий или отправка HTTP-ответов. Многие из этих вспомогательных функций выполняют ту же функцию, что и соответствующий фасад. Например, этот вызов фасада и вызов помощника эквивалентны:
return Illuminate\Support\Facades\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'); $response = $this->get('/cache'); $response->assertSee('value');}
Как работают фасады
В приложении Laravel фасад - это класс, который предоставляет доступ к объекту из контейнера. Техника, которая выполняет эту работу, относится к классу Facade
. Фасады Laravel и любые пользовательские фасады, которые Вы создаете, будут расширять базовый класс Illuminate\Support\Facades\Facade
.
Базовый класс Facade
использует магический метод __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
) против этого объекта.
Фасады в реальном времени
Используя фасады в реальном времени, вы можете рассматривать любой класс в своем приложении, как если бы он был фасадом. Чтобы проиллюстрировать, как это можно использовать, давайте сначала рассмотрим код, который не использует фасады в реальном времени. Например, предположим, что наша модель Podcast
имеет метод publish
. Однако, чтобы опубликовать подкаст, нам нужно внедрить экземпляр Publisher
:
<?php namespace App\Models; use App\Contracts\Publisher;use Illuminate\Database\Eloquent\Model; class Podcast extends Model{ /** * Публикация подкаста. * * @param Publisher $publisher * @return void */ public function publish(Publisher $publisher) { $this->update(['publishing' => now()]); $publisher->publish($this); }}
Внедрение реализации издателя в метод позволяет нам легко тестировать метод изолированно, поскольку мы можем имитировать внедренного издателя. Однако он требует от нас всегда передавать экземпляр издателя каждый раз, когда мы вызываем метод publish
. Используя фасады в реальном времени, мы можем поддерживать такую же тестируемость, при этом не требуя явной передачи экземпляра Publisher
. Чтобы сгенерировать фасад в реальном времени, добавьте к пространству имен импортируемого класса префикс Facades
:
<?php namespace App\Models; use Facades\App\Contracts\Publisher;use Illuminate\Database\Eloquent\Model; class Podcast extends Model{ /** * Публикация подкаста. * * @return void */ public function publish() { $this->update(['publishing' => now()]); Publisher::publish($this); }}
Когда используется фасад реального времени, реализация издателя будет разрешена из контейнера службы с использованием части интерфейса или имени класса, которая появляется после префикса Facades
. При тестировании мы можем использовать встроенные помощники Laravel для тестирования фасадов, чтобы имитировать вызов этого метода:
<?php namespace Tests\Feature; use App\Models\Podcast;use Facades\App\Contracts\Publisher;use Illuminate\Foundation\Testing\RefreshDatabase;use Tests\TestCase; class PodcastTest extends TestCase{ use RefreshDatabase; /** * Пример теста. * * @return void */ public function test_podcast_can_be_published() { $podcast = Podcast::factory()->create(); Publisher::shouldReceive('publish')->once()->with($podcast); $podcast->publish(); }}
Справочник классов фасадов
Ниже Вы найдете каждый фасад и его базовый класс. Это полезный инструмент для быстрого изучения документации API для данного корня фасада. Ключ привязка сервисного контейнера также включен, где это применимо.