При проектировании компонентов взаимодействующих с Laravel, лучше использовать
контракты вместо фасадов.
В силу того, что пакает собран вне фреймворка, у вас не будет доступа к помощникам
Ларавел для тестирования.
Фасады
Содержание:
- Введение
- Когда использовать фасады
- Как работают фасады
- Фасады в реальном времени
- Ссылка на класс фасада
Введение
Фасады продоставляют статические интерфейсы для классов, которые доступны для сервис контейнера. Laravel включает в себя большое количество фасадов, которые фактически обеспечивают функциональность Ларавел. Фасады Laravel служат как "статичные прокси" для основных классов сервис контейнера, обеспечивая преимущество краткого, выразительного синтаксиса, предоставляя большую гибкость и тестируемость нежели традиционные статические методы.
Фасады располагаются по адресу: Illuminate\Support\Facades
.
Получить доступ к фасаду можно так:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Примеры из документации используют фасады, чтобы продемонстрировать большое количество возможностей фреймворка.
Когда использовать фасады
У фасадов есть много преимуществ. Они предоставляют кратки, запоминающий синтаксис, который позволяет вам использовать возможности фреймворка без необходимости запоминать большое количество классов, которые должны быть настроены вручную. Еще их легко тестировать в силу уникального использования динимических методов PHP.
Тем не менее, необходимо соблюдать меры предосторожности при использовании фасадов. Основная опасность при использовании фасадав — это чрезмерное разрастание класса. Фасады легко использовать и они не требуют внедрения зависимостей. Вы можете даже не заметить, как начнёте использовать нескольо фасадов внутри класса, а сам класс станет слишком большим. Используя внедрение зависимоти, внешне видно, когда класс становится большим. Поэтому, при использивании фасадов, обращайте внимание на размер класса, не нужно пытаться вместить в класс слишком много функционала.
Фасады с Внедрение зависимости
Одно из самых важных преимуществ внедрения зависимости — это возможность поменять реализацию внедрённого класса. Это полезно при тестировании т.к. вы можете внедрить и макет или заглушку для нужных методов.
Обычно невозможно использовать заглушку для статического метода. Однако фасады используют динамечиеские методы для передачи вызовов объектов из сервис контейнера. Поэтому мы можем тестировать их как и обычные внедрённые классы. Наприме, для данного маршрута:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
Мы можем написать тест, чтобы верефицировать метод Cache::get
был
вызван с ожидаемыми аргументами:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Фасады и функции помощники
В дополнение к фасадам, Laravel включает набор функций "пощников", которые могут быть полезны при выполнении различных задач: генерация представлений, запуск событий, постановка задач или HTTP отклики. Многие функции-помощники выпоняют одну и ту же задачу как и фасад. Например, вызов фасада эквивалентен функции помощнику:
return View::make('profile');
return view('profile');
Нет абсолютно никакой практической разницы между фасадами и функциями помощниками. При использовании функций помодников, вы можете и тестировать их также, как и соответствующие фасады. Например, для следующего маршрута:
Route::get('/cache', function () {
return cache('key');
});
Под капотом, помощник cache
собирается вызвать метод get
, который
основывается на фасаде Cache
. Поэтому, даже если используем функцию помощник,
мы можем написать следующий тест, чтобы слудующий тест для проверки наших ожиданий:
use Illuminate\Support\Facades\Cache;
/**
* A basic functional test example.
*
* @return void
*/
public function testBasicExample()
{
Cache::shouldReceive('get')
->with('key')
->andReturn('value');
$this->visit('/cache')
->see('value');
}
Как работают фасады
В приложении Laravel фасад — это класс, который предоставляет доступ к объекту из контейнера.
Механизм реализован в классе Facade
. Фасады Ларавел и дюбые другие пользовательские
расширяют класс Illuminate\Support\Facades\Facade
.
Базовый класс Facade
использует магический метод __callStatic()
, чтобы
перенаправить запрос от фасада к объекту из контейнера. В примере ниже, запрос поступает в
систему кэширования Ларавел. Если взглянуть на код, можно сказать, что статический метод
get
принадлежит классу Cache
:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
Заметьте, что ввержу файла мы импортируем фасад Cache
, который служит как
прокси, чтобы получить доступ к реализации интерефейса
Illuminate\Contracts\Cache\Factory
. Любые запросы, которые мы делаем с
использованием фасада будут переданы к основному экземпляру кэш-сервиса Ларавел.
Если мы посмотрим на класс Illuminate\Support\Facades\Cache
, вы увидите,
что нет статического метода get
:
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor() { return 'cache'; }
}
Фасад Cache
расширяет базовый класс Facade
и определяет метод
getFacadeAccessor()
. Задача этого метода заключается в том, чтобы вернуть
имя, связанное с сервис контейнером. Когда пользователь ссылается на любой статичный
метод фасада Cache
, Ларавел берёт связь cache
из сервис
контейнера и выполняет запрошенный метод для объекта. (в нашем случае get
).
Фасады "на лету"
Ипользуя фасады "на лету", вы можете использовать любой класс так, как
если бы это был фасад. Давайте попробуем альтернативу. Например, давайте представим,
что модель Podcast
имеет метод publish
. Чтобы опубликовать
подкаст, нам нужно внедрить экземпляр Publisher
:
<?php
namespace App;
use App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @param Publisher $publisher
* @return void
*/
public function publish(Publisher $publisher)
{
$this->update(['publishing' => now()]);
$publisher->publish($this);
}
}
Внедрение реализации Publisher
в метод позволяет нам легко тестировать метод
в изоляции из-за того, что мы сделали макет внедрённого публиковальщика. Однако, нам
требуется передать экземпляр публиковальщика каждый раз, когда мы вызываем метод
publish
. Используя быстрые фасады, мы можем поддержать
такой же уровень тестируемости, в то время как, нам нет необходимости передавать
экземпляр Publisher
. Чтобы сгенерировать такой фасад, укажите префикс
для Facades
пространства имён импортируемого класса:
<?php
namespace App;
use Facades\App\Contracts\Publisher;
use Illuminate\Database\Eloquent\Model;
class Podcast extends Model
{
/**
* Publish the podcast.
*
* @return void
*/
public function publish()
{
$this->update(['publishing' => now()]);
Publisher::publish($this);
}
}
Когда мы используем быстрые фасады, реализация публиковальщика будет
взята из сервис контейнера с помощью части имени класса или интерфейса, которая находится
после префикса Facades
. При тестировании, мы можем использовать встроенные
функции помощники, чтобы имитировать такой вызов метода:
<?php
namespace Tests\Feature;
use App\Podcast;
use Tests\TestCase;
use Facades\App\Contracts\Publisher;
use Illuminate\Foundation\Testing\RefreshDatabase;
class PodcastTest extends TestCase
{
use RefreshDatabase;
/**
* A test example.
*
* @return void
*/
public function test_podcast_can_be_published()
{
$podcast = factory(Podcast::class)->create();
Publisher::shouldReceive('publish')->once()->with($podcast);
$podcast->publish();
}
}
Ссылка на класс фасада
Ниже вы найдете каждый фасад и соответствующий класс. Это полезный инструмент, что быстро просмотреть API документацию в директории фасада. Также включена связь с сервис контейнером, там где она есть.