1. Архитектурные концепции
  2. Фасады

Присоединяйся к нашему Telegram сообществу @webblend!

Здесь ты найдешь сниппеты по Laravel и полезные советы по веб-разработке.

Введение

На протяжении документации 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 во многих примерах будут использоваться фасады для демонстрации различных функций фреймворка.

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

Дополняя фасады, Laravel предлагает различные глобальные "вспомогательные функции", которые делают еще более простым взаимодействие с общими функциями Laravel. Некоторые из общих вспомогательных функций, с которыми вы можете взаимодействовать, - это view, response, url, config и многие другие. Каждая вспомогательная функция Laravel документирована с соответствующей функциональностью; однако полный список доступен в документации по вспомогательным функциям.

Например, вместо использования фасада Illuminate\Support\Facades\Response для создания JSON-ответа, мы можем просто использовать функцию response. Поскольку вспомогательные функции глобально доступны, вам не нужно импортировать какие-либо классы для их использования:

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;
 
/**
* Пример базового функционального теста.
*/
public function test_basic_example(): void
{
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;
 
/**
* Пример базового функционального теста.
*/
public function test_basic_example(): void
{
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;
use Illuminate\View\View;
 
class UserController extends Controller
{
/**
* Показать профиль для указанного пользователя.
*/
public function showProfile(string $id): View
{
$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
{
/**
* Получить зарегистрированное имя компонента.
*/
protected static function getFacadeAccessor(): string
{
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
{
/**
* Опубликовать подкаст.
*/
public function publish(Publisher $publisher): void
{
$this->update(['publishing' => now()]);
 
$publisher->publish($this);
}
}

Внедрение реализации публикатора в метод позволяет нам легко тестировать метод в изоляции, поскольку мы можем подделать внедренного публикатора. Однако это требует от нас всегда передавать экземпляр публикатора при вызове метода publish. Используя фасады в реальном времени, мы можем сохранить ту же тестируемость, не требуя явного передачи экземпляра Publisher каждый раз при вызове метода publish. Чтобы создать фасад в реальном времени, добавьте префикс пространства имен импортированного класса Facades:

<?php
 
namespace App\Models;
 
use App\Contracts\Publisher;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
 
class Podcast extends Model
{
/**
* Опубликовать подкаст.
*/
public function publish(Publisher $publisher): void
public function publish(): void
{
$this->update(['publishing' => now()]);
 
$publisher->publish($this);
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;
 
/**
* Пример теста.
*/
public function test_podcast_can_be_published(): void
{
$podcast = Podcast::factory()->create();
 
Publisher::shouldReceive('publish')->once()->with($podcast);
 
$podcast->publish();
}
}

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

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

Фасад Класс Привязка контейнера служб
App Illuminate\Foundation\Application app
Artisan Illuminate\Contracts\Console\Kernel artisan
Auth Illuminate\Auth\AuthManager auth
Auth (Instance) Illuminate\Contracts\Auth\Guard auth.driver
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Broadcast Illuminate\Contracts\Broadcasting\Factory  
Broadcast (Instance) Illuminate\Contracts\Broadcasting\Broadcaster  
Bus Illuminate\Contracts\Bus\Dispatcher  
Cache Illuminate\Cache\CacheManager cache
Cache (Instance) Illuminate\Cache\Repository cache.store
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
Date Illuminate\Support\DateFactory date
DB Illuminate\Database\DatabaseManager db
DB (Instance) Illuminate\Database\Connection db.connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Gate Illuminate\Contracts\Auth\Access\Gate  
Hash Illuminate\Contracts\Hashing\Hasher hash
Http Illuminate\Http\Client\Factory  
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\LogManager log
Mail Illuminate\Mail\Mailer mailer
Notification Illuminate\Notifications\ChannelManager  
Password Illuminate\Auth\Passwords\PasswordBrokerManager auth.password
Password (Instance) Illuminate\Auth\Passwords\PasswordBroker auth.password.broker
Pipeline (Instance) Illuminate\Pipeline\Pipeline  
Process Illuminate\Process\Factory  
Queue Illuminate\Queue\QueueManager queue
Queue (Instance) Illuminate\Contracts\Queue\Queue queue.connection
Queue (Base Class) Illuminate\Queue\Queue  
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\RedisManager redis
Redis (Instance) Illuminate\Redis\Connections\Connection redis.connection
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory  
Response (Instance) Illuminate\Http\Response  
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Builder  
Session Illuminate\Session\SessionManager session
Session (Instance) Illuminate\Session\Store session.store
Storage Illuminate\Filesystem\FilesystemManager filesystem
Storage (Instance) Illuminate\Contracts\Filesystem\Filesystem filesystem.disk
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  
Vite Illuminate\Foundation\Vite