Разработка пакетов

Введение

Пакеты — основной способ добавления нового функционала в Laravel. Пакеты могут быть всем, чем угодно — от классов для удобной работы с датами наподобие Carbon, до целых библиотек BDD-тестирования наподобие Behat.

Конечно, существуют разные типы пакетов. Некоторые пакеты автономны, что позволяет им работать в составе любого фреймворка, не только Laravel. Примерами таких отдельных пакетов являются и Carbon, и Behat. Любой из них может быть использован в Laravel с помощью простого указания их в файле composer.json.

С другой стороны, некоторые пакеты разработаны специально для использования в Laravel. Они могут содержать маршруты, контроллеры, представления и настройки, специально рассчитанные для улучшения приложения на Laravel. Этот раздел документации в основном посвящён разработке именно пакетов для Laravel.

Заметка о фасадах

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

Сервис-провайдеры

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

Сервис-провайдер наследует класс Illuminate\Support\ServiceProvider и содержит два метода: register и boot. Базовый класс ServiceProvider находится в пакете Composer illuminate/support, который вы должны добавить в зависимости своего пакета. Подробнее о структуре и задачах сервис-провайдеров читайте в документации.

Ресурсы

Настройка

Скорее всего вы захотите опубликовать файл настроек вашего пакета в директорию config самого приложения. Это позволит пользователям вашего пакета легко изменять настройки по умолчанию. Для публикации файла настроек просто используйте метод publishes из метода boot вашего сервис-провайдера:

/**
 * Выполнение послерегистрационной загрузки сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->publishes([
        __DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
    ]);
}

Теперь, когда пользователи вашего пакета вызовут команду Laravel vendor:publish, ваш файл будет скопирован в указанное место. Само собой, когда ваш файл настроек опубликован, к нему можно обращаться как к любому другому файлу настроек:

$value = config('courier.option');

{note} Вы не должны задавать функции Closures в своем конфиге. Их нельзя правильно сериализировать, когда пользователи выполняют Artisan-команду config:cache.

Настройки пакета по умолчанию

Вы также можете выбрать вариант соединения файла настроек вашего пакета с его копией в приложении. Это позволит вашим пользователям включать только те параметры, которые они хотят изменить в опубликованной копии настройки. Для объединения конфигураций используйте метод mergeConfigFrom в методе register вашего сервис-провайдера:

/**
 * Регистрация привязок в контейнере.
 *
 * @return void
 */
public function register()
{
    $this->mergeConfigFrom(
        __DIR__.'/path/to/config/courier.php', 'courier'
    );
}

{note} Этот метод объединяет только первый уровень массива настройки. Если ваши пользователи частично определяют многомерный массив настройки, отсутствующие параметры не будут объединены.

Роуты

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

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadRoutesFrom(__DIR__.'/routes.php');
}

Миграции

Если в вашем пакете содержатся миграции БД, то можно использовать метод loadMigrationsFrom для информирования Laravel о том, как загрузить их. Метод loadMigrationsFrom принимает путь к миграциям вашего пакета в качестве своего единственного аргумента:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
}

После того как были зарегистрированы миграции вашего пакета, они будут автоматически запущены при выполнении команды php artisan migrate. Вам не нужно экспортировать их в главную директорию приложения - database/migrations.

Переводы

Если ваш пакет содержит языковые файлы, вы можете использовать метод loadTranslationsFrom, чтобы указать Laravel, как их загружать. Например, если ваш пакет называется courier, вы должны добавить в метод boot своего сервис-провайдера следующее:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}

На файлы переводов пакета ссылаются, используя соглашение о синтаксисе package::file.line. Поэтому вы можете загрузить строку welcome пакета courier из файла messages следующим образом:

echo trans('courier::messages.welcome');

Публикация переводов

Для публикации переводов вашего пакета в директорию resources/lang/vendor приложения, используйте метод сервис-провайдера publishes. Метод publishes принимает массив путей к переводам пакета и соответствующие им места для публикации. Например, для публикации языковых файлов нашего пакета courier можно сделать следующее:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');

    $this->publishes([
        __DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),
    ]);
}

Теперь, когда пользователи вашего пакета вызовут Artisan-команду Laravel vendor:publish, переводы вашего пакета будут скопированы в указанное место.

Шаблоны

Для регистрации шаблонов вашего пакета в Laravel, вам надо указать Laravel, где они расположены. Вы можете сделать это методом loadViewsFrom. Метод loadViewsFrom принимает два аргумента: путь к шаблонам и название пакета. Например, если ваш пакет называется courier, добавьте в метод boot своего сервис-провайдера следующее:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}

На шаблоны пакета ссылаются, используя соглашение о синтаксисе package::view. Поэтому как только путь вашего шаблона зарегистрирован в сервис-провайдере, вы можете загрузить шаблон admin из пакета courier:

Route::get('admin', function () {
    return view('courier::admin');
});

Переопределение шаблонов пакета

Когда вы используете метод loadViewsFrom , на самом деле Laravel регистрирует два расположения для ваших шаблонов: одно в директории приложения resources/views/vendor и второе - в указанной вами директории. Поэтому, используя пример с courier, при запросе шаблона пакета Laravel сначала проверит, предоставил ли разработчик свою версию шаблона в resources/views/vendor/courier. Затем, если шаблон не был изменен, Laravel будет искать директорию шаблона пакета, которую вы указали при вызове loadViewsFrom. Это упрощает настройку / переопределение шаблонов вашего пакета теми, кто будет ими пользоваться.

Публикация шаблонов

Для публикации шаблонов вашего пакета в директорию resources/views/vendor используйте метод publishes вашего сервис-провайдера. Метод publishes принимает массив путей к шаблонам пакета и соответствующие им места для публикации:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');

    $this->publishes([
        __DIR__.'/path/to/views' => resource_path('views/vendor/courier'),
    ]);
}

Теперь, когда пользователи вашего пакета вызовут Artisan-команду Laravel vendor:publish, папка ваших шаблонов будет скопирована в указанное место.

Команды

Можно использовать метод commands, чтобы зарегистрировать Artisan-команды вашего пакета в Laravel. Данный метод ожидает массив команд имен класса. Как только команды были зарегистрированы, вы можете выполнить их, используя Artisan CLI:

/**
 * Первоначальная загрузка сервисов приложения.
 *
 * @return void
 */
public function boot()
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            FooCommand::class,
            BarCommand::class,
        ]);
    }
}

Общие ресурсы

Ваши пакеты могут иметь такие ресурсы, как JavaScript, CSS и изображения. Для публикации этих ресурсов в папку приложения public используйте метод publishes сервис-провайдера. В этом примере мы также добавим для ресурсов групповой тег public, который можно использовать для публикации групп связанных ресурсов:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->publishes([
        __DIR__.'/path/to/assets' => public_path('vendor/courier'),
    ], 'public');
}

Теперь, когда пользователи вашего пакета вызовут команду Laravel vendor:publish, ваши ресурсы будут скопированы в указанное место. Так как обычно каждый раз при обновлении пакета вам необходимо перезаписывать ресурсы, можно использовать флаг --force:

php artisan vendor:publish --tag=public --force

Публикация групп файлов

Вам может пригодиться возможность публиковать отдельные группы файлов. Например, если вы захотите дать вашим пользователям возможность публиковать файлы настроек вашего пакета и файлы ресурсов по отдельности. Вы можете сделать это, присвоив им теги при вызове метода publishes из сервис-провайдера пакета. Например, давайте определим две группы для публикации в методе boot сервис-провайдера пакета:

/**
 * Выполнить послерегистрационную загрузку сервисов.
 *
 * @return void
 */
public function boot()
{
    $this->publishes([
        __DIR__.'/../config/package.php' => config_path('package.php')
    ], 'config');

    $this->publishes([
        __DIR__.'/../database/migrations/' => database_path('migrations')
    ], 'migrations');
}

Теперь ваши пользователи могут публиковать эти группы отдельно, указывая их тегпри выполнении команды vendor:publish:

php artisan vendor:publish --tag=config