Статьи

Используйте контракты Laravel для создания пакета веток Laravel 5

Laravel 5 наконец-то вышел, и со всеми его потрясающими возможностями. Одним из новых архитектурных изменений является новый пакет контрактов . В этой статье мы собираемся понять причину этого изменения и попытаться создать практический вариант использования, используя новые контракты.

Laravel

Что такое контракт

Контракт — это интерфейс, который определяет поведение. Давайте возьмем это определение из Википедии .

В объектно-ориентированных языках термин интерфейс часто используется для определения абстрактного типа, который не содержит данных или кода, но определяет поведение как сигнатуры методов.

При этом мы используем интерфейсы для извлечения поведения объекта из класса в интерфейс и зависят только от определения. В IoC-контейнере Laravel вы можете привязать интерфейс к реализации .

$this->app->bind('App\Contracts\EventPusher', 'App\Services\PusherEventPusher'); 

Теперь каждый раз, когда вы разрешаете интерфейс EventPusher из контейнера, вы будете получать реализацию Pusher . Если вы решите переключиться на другой сервис, такой как Fanout , вам нужно всего лишь изменить привязку.

 $this->app->bind('App\Contracts\EventPusher', 'App\Services\FanoutEventPusher'); 

Большинство основных служб Laravel теперь извлекаются в контракт, и если вы хотите, например, переопределить службу Illuminate/Mail , вы можете реализовать Illuminate\Contracts\Mail и определить необходимые методы и добавить его в качестве поставщика.

Laravel View Contract

Laravel использует собственный шаблонизатор Blade . Тем не менее, я хочу использовать Symfony Twig в качестве моего движка шаблонов. Blade уже предлагает возможность зарегистрировать свои собственные расширения; проверьте Мост Ветки для получения дополнительной информации. В Laravel 5 есть лучший способ достичь той же цели с помощью контрактов, поэтому давайте создадим собственную реализацию.

Определение пакета

Чтобы начать сборку нашего пакета, нам нужно определить его внутри файла composer.json . Нам требуется Twig и автозагрузка нашей папки src качестве корневого пространства имен.

 // composer.json { "name": "whyounes/laravel5-twig", "description": "Twig for Laravel 5", "authors": [ { "name": "RAFIE Younes", "email": "[email protected]" } ], "require": { "twig/twig": "1.18.*" }, "autoload": { "psr-0": { "RAFIE\\": "src/" } } } 

Посмотреть поставщика услуг

Предпочтительный способ зарегистрировать привязки вашего пакета — через поставщика услуг .

 // src/RAFIE/Twig/TwigViewServiceProvider.php public function registerLoader() { $this->app->singleton('twig.loader', function ($app) { $view_paths = $app['config']['view.paths']; $loader = new \Twig_Loader_Filesystem($view_paths); return $loader; }); } 

Метод registerLoader свяжет наш Twig Loader с контейнером. $app['config']['view.paths'] содержит наши пути просмотра. По умолчанию в нем есть только папка resources/views .

 // src/RAFIE/Twig/TwigViewServiceProvider.php public function registerTwig() { $this->app->singleton('twig', function ($app) { $options = [ 'debug' => $app['config']['app.debug'], 'cache' => $app['config']['view.compiled'], ]; $twig = new \Twig_Environment($app['twig.loader'], $options); // register Twig Extensions $twig->addExtension(new \Twig_Extension_Debug()); // register Twig globals $twig->addGlobal('app', $app); return $twig; }); } 

Класс Twig_Environment является Twig_Environment классом Twig. Он принимает Twig_LoaderInterface и список параметров:

  • $app['config']['app.debug'] извлекает наш флаг отладки из файла конфигурации.
  • $app['config']['view.compiled'] — это путь к нашим скомпилированным представлениям, зарегистрированный в файле config/view.php .

Метод $twig->addGlobal регистрирует переменную, которая будет доступна для всех представлений.

 // src/RAFIE/Twig/TwigViewServiceProvider.php namespace RAFIE\Twig; class TwigViewServiceProvider extends ServiceProvider { public function register() { $this->registerLoader(); $this->registerTwig(); $this->app->bind('view', function ($app) { return new TwigFactory($app); }); } } 

Метод register связывает twig и twig.loader с контейнером. Ключ представления ранее содержал фабрику представлений Blade, теперь он разрешит наш новый класс TwigFactory который будет отвечать за отображение нашего представления.
Laravel не будет загружать вашего поставщика услуг по умолчанию, поэтому вам нужно зарегистрировать его в вашем массиве config/app.php provider. Мы также прокомментируем поставщика услуг Laravel View.

 // config/app.php ... 'providers' => [ 'RAFIE\Twig\TwigViewServiceProvider', //'Illuminate\View\ViewServiceProvider', ... 

Посмотреть фабрику

Класс TwigFactory должен реализовывать интерфейс Illuminate\Contracts\View\Factory чтобы получить форму и поведение системы представления. Этот класс выполнит работу по передаче представления парсеру Twig. Чтобы добиться более свободной связи, у нас есть класс TwigView который реализует Illuminate\Contracts\View\View . Этот класс будет вести себя как мешок для объекта представления и будет иметь ссылку на класс TwigFactory .

 // src/RAFIE/Twig/TwigFactory.php class TwigFactory implements FactoryContract { /* * Twig environment * * @var Twig_Environment * */ private $twig; public function __construct(Application $app) { $this->twig = $app['twig']; } public function exists($view) { return $this->twig->getLoader()->exists($view); } public function file($path, $data = [], $mergeData = []) { // or maybe use the String loader if (!file_exists($path)) { return false; } $filePath = dirname($path); $fileName = basename($path); $this->twig->getLoader()->addPath($filePath); return new TwigView($this, $fileName, $data); } public function make($view, $data = [], $mergeData = []) { $data = array_merge($mergeData, $data); return new TwigView($this, $view, $data); } public function share($key, $value = null) { $this->twig->addGlobal($key, $value); } public function render($view, $data) { return $this->twig->render($view, $data); } public function composer($views, $callback, $priority = null){} public function creator($views, $callback){} public function addNamespace($namespace, $hints){} } 

Мы разрешаем объект twig из контейнера с параметрами, описанными ранее, и начинаем реализовывать логику метода. Я опустил последние три функции, потому что их определение потребует создания событий и их отправки и сделает наш простой пример более сложным. Метод make возвращает новый экземпляр TwigView с текущей фабрикой, представлением и данными.

 // src/RAFIE/Twig/TwigView.php use Illuminate\Contracts\View\View as ViewContract; class TwigView implements ViewContract { /* * View name to render * @var string * */ private $view; /* * Data to pass to the view * @var array * */ private $data; /* * Twig factory * @var RAFIE\Twig\TwigFactory * */ private $factory; public function __construct(TwigFactory $factory, $view, $data = []) { $this->factory = $factory; $this->view = $view; $this->data = $data; } public function render() { return $this->factory->render($this->view, $this->data); } public function name() { return $this->view; } public function with($key, $value = null) { $this->data[$key] = $value; return $this; } public function __toString() { return $this->render(); } } 

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

 // app/Http/routes.php Route::get('/', function(){ return View::make('home.twig', ['name' => 'younes']); }); // we can also pass the name by chaining the call to the `with` method. Route::get('/', function(){ return View::make('home.twig')->with('name', 'younes'); }); 
 // resources/views/home.twig <h2>Hello {{ name|upper }}</h2> 

Home

Когда вы нажимаете на страницу индекса вашего приложения, поток выполнения будет:

  • Фасад View будет разрешен из контейнера и вернет экземпляр TwigFactory .
  • TwigView метод make который возвращает новый экземпляр TwigView с фабрикой, представлением и данными.
  • Laravel Router принимает объект Response или строку в качестве ответа на запрос, поэтому __toString вызывается в нашем экземпляре TwigView . Метод render внутри TwigFactory вызывает метод Twig_Environment объекта Twig_Environment .

При использовании контрактов API приложения всегда согласован, поэтому тестирование других реализованных методов будет выполняться так же, как и раньше.

 // test if a view exists View::exists('contact.twig'); // render a view from a different folder View::file('app/contact.twig'); 

Вывод

Ознакомьтесь с окончательной версией проекта здесь .

Новый пакет Contracts предоставляет лучший способ расширения основных компонентов Laravel и предоставляет стабильный API для разработчиков, чтобы получить доступ к поведению платформы. Дайте мне знать, что вы думаете о новом пакете контрактов в комментариях, и если у вас есть какие-либо вопросы, не стесняйтесь размещать их ниже.