Шаблон Facade — это шаблон проектирования программного обеспечения, который часто используется в объектно-ориентированном программировании. Фактически фасад — это класс, заключающий в себе сложную библиотеку, чтобы обеспечить более простой и читаемый интерфейс. Шаблон Facade также можно использовать для предоставления унифицированного и хорошо разработанного API для группы сложных и плохо разработанных API.
Каркас Laravel имеет особенность, похожую на этот рисунок, также называемую Фасады. В этом уроке мы узнаем, как перенести «Фасады» Ларавеля в другие рамки. Прежде чем мы продолжим, вы должны иметь общее представление о контейнерах Ioc .
Давайте сначала пройдемся по внутренним рабочим частям фасадов Laravel, а затем обсудим, как мы можем адаптировать эту функцию к другим средам.
Фасады в Ларавеле
Фасад Laravel — это класс, который предоставляет статический интерфейс для служб внутри контейнера. Эти фасады, согласно документации, служат прокси для доступа к базовой реализации сервисов контейнера.
Однако в сообществе PHP было много споров об этом именовании . Некоторые утверждают, что этот термин следует изменить, чтобы избежать путаницы среди разработчиков, поскольку он не полностью реализует шаблон Facade. Если это название вас смущает, не стесняйтесь называть его так, как считаете нужным, но учтите, что базовый класс, который мы собираемся использовать, называется Facade в среде Laravel.
Как Фасады реализованы в Laravel
Как вы, наверное, знаете, каждый сервис внутри контейнера имеет уникальное имя. В приложении Laravel для доступа к сервису напрямую из контейнера мы можем использовать метод App::make()
или вспомогательную функцию app()
.
<? php App :: make ( 'some_service' )-> methodName ();
Как упоминалось ранее, Laravel использует классы фасадов, чтобы сделать сервисы доступными для разработчика более читабельным способом. Используя класс фасадов, нам нужно было бы написать следующий код, чтобы сделать то же самое:
// ... someService :: methodName ();
// ...
В Laravel все сервисы имеют класс фасадов. Эти классы фасадов расширяют базовый класс Facade, который является частью пакета Illuminate/Support
. Единственное, что им нужно реализовать, — это метод getFacadeAccessor
, который возвращает имя службы внутри контейнера.
В приведенном выше синтаксисе someService
ссылается на класс фасада. methodName
фактически является методом исходного сервиса в контейнере. Если мы посмотрим на этот синтаксис вне контекста Laravel, это будет означать, что есть класс с именем someService
представляющий статический метод с именем methodName()
, но это не то, как Laravel реализует этот интерфейс. В следующем разделе мы увидим, как работает базовый класс Facade Laravel за кулисами.
Базовый Фасад
Класс Facade имеет частное свойство с именем $app
котором хранится ссылка на контейнер службы. Если нам нужно использовать фасады вне Laravel, мы должны явно установить контейнер с помощью setFacadeApplication()
. Мы вернемся к этому в ближайшее время.
Внутри базового класса фасада был реализован магический метод __callStatic
для обработки вызова статических методов, которые на самом деле не существуют. Когда мы вызываем статический метод для класса фасада Laravel, вызывается метод __callStatic, потому что класс фасада не реализовал этот метод. Следовательно, __callStatic
извлекает соответствующую услугу из контейнера и вызывает метод против него.
Вот реализация метода __callStatic
в базовом классе фасада:
<? php // ...
/** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed */
public static function __callStatic ( $method , $args )
{ $instance = static :: getFacadeRoot ();
switch ( count ( $args )) {
case 0 :
return $instance -> $method ();
case 1 :
return $instance -> $method ( $args [ 0 ]);
case 2 :
return $instance -> $method ( $args [ 0 ], $args [ 1 ]);
case 3 :
return $instance -> $method ( $args [ 0 ], $args [ 1 ], $args [ 2 ]);
case 4 :
return $instance -> $method ( $args [ 0 ], $args [ 1 ], $args [ 2 ], $args [ 3 ]);
default :
return call_user_func_array ([ $instance , $method ], $args );
}
}
В приведенном выше методе getFacadeRoot()
получает сервис из контейнера.
Анатомия Фасадного Класса
Каждый класс фасадов расширяет базовый класс. Единственное, что нам нужно реализовать, — это getFacadeAccessor()
. Этот метод не делает ничего, кроме как возвращает имя службы в контейнере.
<? php namespace App \Facades ;
use Illuminate \Support\Facades\Facade as BaseFacade ;
class SomeServiceFacade extends BaseFacade {
/** * Get the registered name of the component. * * @return string */
protected static function getFacadeAccessor () { return 'some.service' ; }
}
Псевдонимы
Поскольку фасады Laravel являются PHP-классами, нам нужно импортировать их, прежде чем мы сможем их использовать. Благодаря пространствам имен и поддержке автозагрузки в PHP все классы автоматически загружаются, когда мы обращаемся к ним по полному имени. PHP также поддерживает псевдонимы классов с помощью директивы use
:
use App \Facades\SomeServiceFacade SomeServiceFacade : SomeMethod ();
Тем не менее, мы должны делать это в каждом сценарии, где нам нужен этот конкретный класс фасадов. Laravel обрабатывает псевдонимы фасадов по-своему, используя загрузчик псевдонимов.
Как Laravel совмещает фасады
Все псевдонимы хранятся в массиве aliases
в app.php
конфигурации app.php
, который находится в каталоге /config
.
Если мы посмотрим на массив, то увидим, что каждое имя псевдонима сопоставляется с полным именем класса. Это означает, что мы можем использовать любое имя для класса фасадов:
// ..
'aliases' => [
// ...
'FancyName' => 'App\Facades\SomeServiceFacade' ,
],
Хорошо, теперь давайте посмотрим, как Laravel использует этот массив для наложения классов фасадов. На этапе начальной загрузки Laravel использует службу с именем AliasLoader
которая является частью пакета Illuminate\Foundation
. AliasLoader
принимает массив aliases
, выполняет AliasLoader
по всем элементам и создает очередь функций __autoload
используя PHP spl_autoload_register . Каждая функция __autoload
отвечает за создание псевдонима для соответствующего класса фасада с помощью PHP-функции class_alias .
В результате нам не придется импортировать и псевдонимы классов перед их использованием, как мы обычно делаем с use
директивы use
. Поэтому всякий раз, когда мы пытаемся получить доступ к несуществующему классу, PHP будет проверять очередь __autoload
для получения правильного автозагрузчика. К тому времени AliasLoader
уже зарегистрировал все функции __autoload
. Каждый автозагрузчик принимает причудливое имя класса и разрешает его в исходное имя класса в соответствии с массивом aliases
. Наконец, он создает псевдоним для этого класса. Рассмотрим следующий вызов метода:
<? php // FancyName is resolved to App\Facades\SomeServiceFacade according to the aliases array
FancyName :: someMethod ()
За кулисами FancyName
в App\Facades\SomeServiceFacade
.
Использование фасадов в других каркасах
Хорошо, теперь, когда мы хорошо понимаем, как Laravel обрабатывает свои фасады и псевдонимы, мы можем адаптировать фасадный подход Laravel к другим средам. В этой статье мы собираемся использовать фасады в рамках Silex. Однако вы можете адаптировать эту функцию и к другим платформам, следуя той же концепции.
Silex имеет свой собственный контейнер, так как он расширяет Pimple
. Чтобы получить доступ к сервису внутри контейнера, мы можем использовать объект $app
следующим образом:
<? php $app [ 'some.service' ]-> someMethod ()
С помощью классов фасадов мы можем предоставить статический интерфейс для наших сервисов Silex. Кроме того, мы можем использовать сервис AliasLoader
для создания значимых псевдонимов для этих фасадов. В результате мы сможем реорганизовать приведенный выше код следующим образом:
<? php SomeService :: someMethod ();
Требования
Чтобы использовать базовый класс фасадов, нам нужно установить пакет Illuminate\Support
с помощью composer:
composer require illuminate\support
Этот пакет также содержит и другие сервисы, но пока нам просто нужен базовый класс Facade.
Создание Фасадов
Чтобы создать фасад для сервиса, нам просто нужно расширить базовый класс Facade и реализовать метод getFacadeAccessor
.
В этом уроке давайте оставим все фасады под src/Facades
path. Например, для службы с именем some.service
, фасадный класс будет выглядеть следующим образом:
<? php namespace App \Facades use Illuminate \Support\Facades\Facade ;
class SomeServiceFacade extends Facade {
/** * Get the registered name of the component. * * @return string */
protected static function getFacadeAccessor () { return 'some.service' ; }
}
Обратите внимание, что мы разместили пространство имен в классе app\facades
.
Остается только установить контейнер приложения на класс фасада. Как указывалось ранее, когда мы вызываем метод в статическом контексте для класса фасада, запускается __callStatic
. _callStatic
использует данные, возвращаемые getFacadeAccessor()
чтобы идентифицировать службу внутри контейнера и пытается извлечь ее. Когда мы используем базовый класс Facade вне Laravel, объект контейнера не устанавливается автоматически, поэтому нам нужно будет сделать это вручную.
Для этого базовый класс фасада предоставляет метод с именем setFacadeApplication
который устанавливает контейнер приложения для этого класса фасада.
В нашем файле app.php
нам нужно добавить следующий код:
<? php Illumiante \Support\Facade :: setFacadeApplication ( $app );
Это установит контейнер для всех фасадов, которые расширяют базовый класс фасадов.
Теперь вместо доступа к сервису из нашего контейнера мы можем использовать только что созданный класс фасадов, который также позволяет нам вызывать все методы в статическом контексте.
Реализация псевдонимов
Чтобы AliasLoader
псевдоним классов фасадов, мы будем использовать AliasLoader
который мы представили ранее. Aliasloader
является частью Aliasloader
illuminate\foundation
. Мы можем либо загрузить весь пакет, либо просто позаимствовать код и сохранить его в виде файла.
Если вы просто хотите скопировать исходный файл, я предлагаю вам сохранить его в src/Facades
. Вы можете использовать пространство имен класса AliasLoader
на основе архитектуры вашего проекта.
Для этого примера давайте скопируем код и поместим его в пространство имен в app/facades
.
Создание массива псевдонимов
Давайте создадим файл в нашей директории config
именем aliases.php
и поместим в него привязки псевдонима-фасада следующим образом:
<? php return [
'FancyName' => 'App\Facades\SomeService' ,
];
FancyName
— это имя, которое мы хотим использовать вместо App\Facades\SomeService
.
Регистрация псевдонимов
AliasLoader
— это одиночный сервис. Чтобы создать или получить экземпляр загрузчика псевдонимов, нам нужно вызвать метод getInstance
с массивом псевдонимов в качестве аргумента. Наконец, чтобы зарегистрировать все псевдонимы, нам нужно вызвать его метод register
.
Снова в файле app.php
добавьте следующий код:
<? php // ... $aliases = require __DIR__ . '/../../config/aliases.php' ;
App \Facades\AliasLoader :: getInstance ( $aliases )-> register ();
И это все, что нужно сделать! Теперь мы можем использовать сервис так:
<? php FancyName :: methodName ();
Завершение
Класс Facade должен только реализовать метод getFacadeAccessor
который возвращает имя службы внутри контейнера. Поскольку мы используем эту функцию вне среды Laravel, мы должны явно установить контейнер службы, используя метод setFacadeApplication()
.
Чтобы ссылаться на классы фасада, мы должны либо использовать полностью определенные имена классов, либо импортировать их с use
директивы use
PHP. В качестве альтернативы, мы можем следовать способу псевдонимов фасадов Laravel, используя загрузчик псевдонимов.
Вопросов? Комментарии? Оставь их ниже! Спасибо за прочтение!