Фасади
Вступ
Протягом документації 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; test('basic example', function () { Cache::shouldReceive('get') ->with('key') ->andReturn('value'); $response = $this->get('/cache'); $response->assertSee('value'); });
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 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. Щоб згенерувати фасад в реальному часі, додайте префікс 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 use App\Models\Podcast; use Facades\App\Contracts\Publisher; use Illuminate\Foundation\Testing\RefreshDatabase; uses(RefreshDatabase::class); test('podcast can be published', function () { $podcast = Podcast::factory()->create(); Publisher::shouldReceive('publish')->once()->with($podcast); $podcast->publish(); });
<?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(); } }
Довідник класу Facade
Нижче ви знайдете кожен фасад та відповідний йому клас. Це корисний інструмент для швидкого ознайомлення з документацією API для певного фасаду. Також включено ключ прив’язки сервісного контейнера, якщо він застосовний.
