Любите загадки? Событие еще доступно на сайте.
Примите наш вызов и улучшите свои навыки!
Примите наш вызов и улучшите свои навыки!

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

Введение

Пакеты – это основной способ добавления функциональности в Laravel. Пакеты могут быть чем угодно, начиная от отличной библиотеки по работе с датами, такой как Carbon или даже пакетом, который позволяет вам прикреплять файлы к моделям Eloquent, например, Laravel Media Library от Spatie.

Есть разные типы пакетов. Некоторые пакеты являются автономными, что означает, что они работают с любым фреймворком PHP. Carbon и Pest – это примеры автономных пакетов. Любой из этих пакетов можно использовать с Laravel, указав их в вашем файле composer.json.

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

Примечание о фасадах

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

Обнаружение пакетов

Файл bootstrap/providers.php содержит список сервис-провайдеров, которые должны быть загружены Laravel. Однако вместо того, чтобы требовать от пользователей вручную добавлять ваш сервис-провайдер в этот список, вы можете определить провайдер в разделе extra файла composer.json вашего пакета, чтобы он автоматически загружался Laravel. Помимо сервис-провайдеров, вы также можете перечислить любые фасады, которые вы хотели бы зарегистрировать:

"extra": {
    "laravel": {
        "providers": [
            "Barryvdh\\Debugbar\\ServiceProvider"
        ],
        "aliases": {
            "Debugbar": "Barryvdh\\Debugbar\\Facade"
        }
    }
},

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

Отказ от обнаружения пакетов

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

"extra": {
    "laravel": {
        "dont-discover": [
            "barryvdh/laravel-debugbar"
        ]
    }
},

Вы можете отключить обнаружение для всех пакетов, используя метасимвол * внутри директивы dont-discover вашего приложения:

"extra": {
    "laravel": {
        "dont-discover": [
            "*"
        ]
    }
},

Поставщики служб

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

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

Ресурсы

Конфигурация

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../config/courier.php' => config_path('courier.php'),
    ]);
}

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

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

Вы не должны определять замыкания в своих конфигурационных файлах. Они не могут быть корректно сериализованы, когда пользователи выполняют команду config:cache Artisan.

Конфигурация пакета по умолчанию

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

Метод mergeConfigFrom принимает путь к конфигурационному файлу вашего пакета в качестве первого аргумента и имя копии конфигурационного файла приложения в качестве второго аргумента:

/**
 * Регистрация любых служб пакета.
 */
public function register(): void
{
    $this->mergeConfigFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}

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

Маршруты

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}

Миграции

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->publishesMigrations([
        __DIR__.'/../database/migrations' => database_path('migrations'),
    ]);
}

Языковые файлы (Переводы)

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');
}

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

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

Вы можете зарегистрировать файлы перевода вашего пакета в формате JSON с помощью метода loadJsonTranslationsFrom. Метод принимает путь к директории, содержащей файлы перевода вашего пакета в формате JSON:

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}

Публикация языковых файлов

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');

    $this->publishes([
        __DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
    ]);
}

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

Шаблоны

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}

Для ссылок на шаблоны пакетов используется синтаксическое соглашение package::view. Итак, как только путь вашего шаблона зарегистрирован в поставщике службы, вы можете загрузить шаблон dashboard пакета courier следующим образом:

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

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

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

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

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');

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

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

Компоненты шаблонов

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

use Illuminate\Support\Facades\Blade;
use VendorPackage\View\Components\AlertComponent;

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    Blade::component('package-alert', AlertComponent::class);
}

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

<x-package-alert/>

Автозагрузка компонентов

В качестве альтернативы, вы можете использовать метод componentNamespace для автоматической загрузки классов компонентов по соглашению. Например, пакет Nightshade может иметь компоненты Calendar и ColorPicker, которые находятся в пространстве имен Nightshade\Views\Components:

use Illuminate\Support\Facades\Blade;

/**
 * Инициализируйте сервисы вашего пакета.
 */
public function boot(): void
{
    Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
}

Это позволит использовать компоненты пакета с помощью синтаксиса package-name:: их вендорного пространства имен:

<x-nightshade::calendar />
<x-nightshade::color-picker />

Blade автоматически определит класс, связанный с этим компонентом, используя паскаль-кейс название компонента. Поддерживается также использование подкаталогов с помощью “точечной” нотации.

Анонимные компоненты

Если ваш пакет содержит анонимные компоненты, то они должны быть помещены в каталог components каталога «views» вашего пакета (как указано в loadViewsFrom). Затем вы можете отобразить их, добавив к имени компонента префикс пространства имен шаблонов пакета:

<x-courier::alert />

Информация о пакете в Artisan

Встроенная в Laravel команда Artisan “about” предоставляет краткое описание окружения и конфигурации приложения. Пакеты могут добавлять дополнительную информацию в вывод этой команды с помощью класса AboutCommand. Обычно такая информация может быть добавлена из метода boot сервис-провайдера вашего пакета:

use Illuminate\Foundation\Console\AboutCommand;

/**
 * Инициализируйте сервисы вашего пакета.
 */
public function boot(): void
{
    AboutCommand::add('Мой Пакет', fn () => ['Версия' => '1.0.0']);
}

Это позволит вашему пакету добавить информацию о версии и другие данные к выводу команды “about”. В данном примере, “Мой Пакет” будет отображаться в списке пакетов, и его версия будет указана как “1.0.0”.

Команды

Чтобы зарегистрировать команды Artisan вашего пакета в Laravel, вы можете использовать метод commands. Этот метод ожидает массив имен классов команд. После регистрации команд вы можете выполнять их с помощью Artisan CLI:

use Courier\Console\Commands\InstallCommand;
use Courier\Console\Commands\NetworkCommand;

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->commands([
            InstallCommand::class,
            NetworkCommand::class,
        ]);
    }
}

Команды оптимизации

Команда Laravel optimize кэширует конфигурацию приложения, события, маршруты и представления. Используя метод optimizes, вы можете зарегистрировать собственные команды Artisan вашего пакета, которые должны вызываться при выполнении команд optimize и optimize:clear:

/**
 * Bootstrap any package services.
 */
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->optimizes(
            optimize: 'package:optimize',
            clear: 'package:clear-optimizations',
        );
    }
}

Публичные ресурсы

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../public' => public_path('vendor/courier'),
    ], 'public');
}

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

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

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

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

/**
 * Загрузка любых служб пакета.
 */
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../config/package.php' => config_path('package.php')
    ], 'courier-config');

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

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

php artisan vendor:publish --tag=courier-config