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