Как создавать фасады в Laravel
Фасады Laravel — это фантастическая функция фреймворка Laravel, обеспечивающая удобный доступ к сервисам через простой интерфейс. Когда только начинал изучать Laravel, меня смущала одна вещь — метод доступа к фасаду.
Поняв, что фасады — это удобный способ доступа к базовым сервисам в сервис контейнере Laravel, можно легко отследить сервис, стоящий за любым фасадом. Каждый фасад предоставляет метод getFacadeAccessor(), указывающий на имя зарегистрированного сервиса.
Вот пример аксессора фасада для фасада DB:
// Illuminate\Support\Facades\DB;
protected static function getFacadeAccessor()
{
return 'db';
}Таким образом, строка 'db' указывает на сервис в контейнере, используемый фасадом для разрешения.
Некоторые фасады могут обращаться к классу менеджера, который затем будет динамически передавать методы базовому макросу или классу подключения к базе данных:
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
return $this->connection()->$method(...$parameters);
}При вызове без соединения DatabaseManager будет использовать соединение по умолчанию и вызывать методы на этом соединении.
Рекомендую прочитать о том, как работают фасады, в документации Laravel, где также есть список ссылок на фасады, доступные во фреймворке.
Создание собственных фасадов в Laravel
Зачем вообще создавать фасад в приложении, если вы не создаёте плагин или не работаете с фреймворком Laravel напрямую? Некоторые разработчики предпочитают использовать инъекцию зависимостей только для сервисов, определённых в коде приложения, и это нормально. Однако я считаю удобным определять фасады для часто используемых сервисов либо через хелперы, либо через пространство имён App\Facades. Мне нравится гибкость Laravel, и в то же время я могу установить соглашения, позволяющие продуктивно работать в незнакомой кодовой базе.
Чтобы создать фасад в коде приложения, рекомендую использовать команду make:class для генерации фасада, что можно сделать следующим образом:
php artisan make:class App/Facades/ExampleДопустим, вы определили сервис в сервис провайдере приложения с названием App\ExampleService; затем можно создать фасад для него после генерации класса:
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Example extends Facade
{
public static function getFacadeAccessor()
{
return 'example_service';
}
}С таким же успехом можно было бы сделать аксессор фасада строкой полностью определённого класса, если бы не был определён псевдоним или строка определения в сервис провайдере:
public static function getFacadeAccessor()
{
return \App\ExampleService::class;
}Фасады удобны тем, что в тесте можно напрямую имитировать базовый класс сервиса:
use App\Facades\Example;
Example::shouldReceive('getLatestPosts')
->with($after_date)
->andReturn($test_posts);Если не используется фасад, Laravel предоставляет другие удобные методы имитации, такие как partialMock(), позволяющие удобно поменять сервис на имитацию в тесте. С сервисом это можно сделать прямо в тесте, например, так:
$mock = $this->partialMock(MyApiService::class, function (MockInterface $mock) {
$mock->shouldReceive('getLatestPosts')
->with($after_date)
->andReturn($test_posts);
});Вы сами решаете, какого подхода придерживаться, но если в приложении активно используется сервис, подумайте о создании фасада, чтобы обеспечить удобство использования сервиса без потери возможностей, предоставляемых инъекцией зависимостей.