Фасады

Содержание:


Введение

Фасады продоставляют статические интерфейсы для классов, которые доступны для сервис контейнера. Laravel включает в себя большое количество фасадов, которые фактически обеспечивают функциональность Ларавел. Фасады Laravel служат как "статичные прокси" для основных классов сервис контейнера, обеспечивая преимущество краткого, выразительного синтаксиса, предоставляя большую гибкость и тестируемость нежели традиционные статические методы.

Фасады располагаются по адресу: Illuminate\Support\Facades. Получить доступ к фасаду можно так:

use Illuminate\Support\Facades\Cache;

Route::get('/cache', function () {
    return Cache::get('key');
});

Примеры из документации используют фасады, чтобы продемонстрировать большое количество возможностей фреймворка.


Когда использовать фасады

У фасадов есть много преимуществ. Они предоставляют кратки, запоминающий синтаксис, который позволяет вам использовать возможности фреймворка без необходимости запоминать большое количество классов, которые должны быть настроены вручную. Еще их легко тестировать в силу уникального использования динимических методов PHP.

Тем не менее, необходимо соблюдать меры предосторожности при использовании фасадов. Основная опасность при использовании фасадав — это чрезмерное разрастание класса. Фасады легко использовать и они не требуют внедрения зависимостей. Вы можете даже не заметить, как начнёте использовать нескольо фасадов внутри класса, а сам класс станет слишком большим. Используя внедрение зависимоти, внешне видно, когда класс становится большим. Поэтому, при использивании фасадов, обращайте внимание на размер класса, не нужно пытаться вместить в класс слишком много функционала.

При проектировании компонентов взаимодействующих с Laravel, лучше использовать контракты вместо фасадов. В силу того, что пакает собран вне фреймворка, у вас не будет доступа к помощникам Ларавел для тестирования.

Фасады с Внедрение зависимости

Одно из самых важных преимуществ внедрения зависимости — это возможность поменять реализацию внедрённого класса. Это полезно при тестировании т.к. вы можете внедрить и макет или заглушку для нужных методов.

Обычно невозможно использовать заглушку для статического метода. Однако фасады используют динамечиеские методы для передачи вызовов объектов из сервис контейнера. Поэтому мы можем тестировать их как и обычные внедрённые классы. Наприме, для данного маршрута:

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

Фасад Класс Связь сервис контейнера
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
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
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
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