Найти тему
Битрикс и не только

Создание пакетов Laravel в локальном окружении

Как настроить локальное окружение для разработки, чтобы тестировать классы или утилиты вашего пакета в рамках локального проекта Laravel.
Неудивительно, что в ходе разработки веб-инструментов мы используем сотни пакетов. Для их получения мы используем менеджер пакетов, например, Packagist, который насчитывает более 350 тысяч пакетов (на конец 2023 года).

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

Создайте папку, которая станет основой для вашего пакета.

-
mkdir package
Создайте файл composer.json, являющийся основой вашего пакета.

package/composer.json
{
"name": "yourname/package",
"description": "Описание вашего пакета",
"type": "library",
"autoload": { "psr-4": { "Yourname\\Package\\": "src/" } }
}


name: Имя должно быть структурировано со схемой "сущность/имя_пакета". Настоятельно рекомендуется использовать описательные имена для облегчения поиска пользователям.
description: Описание пакета должно быть ясным и кратким.
type: Существуют 4 типа: библиотека (library), проект (project), метапакет (metapackage) и плагин композера (composer-plugin). Наиболее часто используется тип библиотека (library). Проект (project) представляет собой, например, шаблон фреймворка.
autoload: Это центральный элемент нашего пакета, определяющий пространство имен для доступа к данным, расположенным в корне проекта. Спецификация протокола автоматической загрузки классов определяется по PSR-4. Это критически важный шаг. Обязательно включите \\, особенно в конце выражения.
Рекомендуется, чтобы информация об имени соответствовала пространству имен. Кроме того, рекомендуется использовать имя папки src в корне проекта.

Кроме того, если вы выполните команду composer init в папке, которая не содержит файла composer.json, мастер поможет вам создать файл composer.json.

Создайте папку src в папке package.
-
cd package
- mkdir src


Расположение файлов не имеет особого значения, кроме их близости как для вас, так и для этой статьи.
Создайте PHP-класс с именем Greeter, содержащий функцию greet, которая возвращает фразу "Hello world!".

package/src/Greeter.php
<?php

namespace Yourname\Package;

class Greeter
{
public function greet(): string
{
return "Hello world!";
}
}


Не забудьте указать пространство имен. В противном случае класс не будет найден.
Теперь можно приступать к тестированию пакета. Для этого необходимо создать тестовое окружение с использованием шаблонного проекта Laravel.

Вернитесь в родительский каталог пакета и создайте шаблонный проект Laravel, который будет использоваться для теста.

-
cd ../../
- composer create-project laravel/laravel template

Команда composer create-project laravel/laravel template генерирует здесь 'type': 'project'.
Чтобы сообщить шаблону, что наш пакет находится в той же родительской папке, необходимо добавить две части информации в файл composer.json.


template/composer.json
...
"minimum-stability": "dev",
"repositories": [{ "type": "path", "url": "../package" }]
...


minimum-stability: Этот параметр позволяет устанавливать пакет через композер, не вызывая исключения. Это необходимо, когда пакет еще не является стабильным, в данном случае наш пакет находится в статусе dev.
repositories: Этот массив позволяет добавлять пути к другим каталогам, на которые композер должен ссылаться для локального поиска пакета.
type: Тип директории может быть composer, package, vcs или path. Опция path позволяет локально использовать пакет, а опция vcs позволяет использовать пакет через систему контроля версий, такую как Github.


Настало время установить наш новый пакет.

-
composer require yourname/package


Теперь данный компонент указан в списке обязательных зависимостей.

package/composer.json

"require": {
...
"yourname/package": "dev-main",
...
}


Поскольку разработка пакета ещё не завершена, используется версия dev-main.
Проверьте это, используя
tinker.

-
php artisan tinker

> Yourname\Package\Greeter::greet()
= "Привет, мир!"


Вы можете изменить файл web.php, чтобы протестировать статический класс Greeter.

package/routes/web.php

<?php
use Illuminate\Support\Facades\Route;
use Yourname\Package\Greeter;

Route::get('/', fn() => dd(Greeter::greet()));
"Привет, мир!" // routes/web.php


Рабочая среда готова к использованию.

Добавим в класс дополнительный метод say(), чтобы проверить работу средства в реальном времени.

package/src/Greeter.php

<?php

namespace Yourname\Package;

class Greeter
{
public static function greet() : string
{
return "Привет, мир!";
}

public static function say(string $something) : string
{
return $something;
}
}


Протестируйте его, используя tinker.

php artisan tinker

> Yourname\Package\Greeter::say("Это быстрый способ разработки и тестирования пакета!")
= "Это быстрый способ разработки и тестирования пакета!"


Вы можете изменить файл web.php, чтобы протестировать статический класс Greeter.

package/routes/web.php

<?php

use Illuminate\Support\Facades\Route;
use Yourname\Package\Greeter;

Route::get('/', fn() => dd(Greeter::say("Это быстрый способ разработки и тестирования пакета!")));
"Это быстрый способ разработки и тестирования пакета!" // routes/web.php


На данном этапе всё готово к разработке пакета, не зависящего от конкретного PHP-фреймворка. Для создания пакета для Laravel требуются дополнительные шаги. В рамках этой статьи задача состоит в реализации команды php artisan greet, которая будет вызывать статический класс Greeter.

Рекомендуется при создании пакета для Laravel следовать типичной структуре проекта Laravel, что упрощает поиск информации.

Прежде всего, необходимо создать GreetCommand, наследуя команду Illuminate\Console\Command, специфичную для Laravel.

package/src/Commands/GreetCommand.php

<?php

namespace Yourname\Package\Console\Commands;

use Illuminate\Console\Command;
use Yourname\Package\Greeter;

class GreetCommand extends Command
{
protected $signature = "greet";

protected $description = "Приветствует людей с 'Привет, мир!'";

public function handle()
{
dump(Greeter::greet());
}
}


В качестве родителя используется базовый класс Illuminate\Console\Command Laravel.
Метод handle() использует ранее созданный статический класс Greeter. Однако, для тестирования пакета можно просто заменить return Greeter::greet() на return "Привет, мир!".
Чтобы проект-шаблон узнал об этой команде, необходимо уведомить его с помощью ServiceProvider.

package/src/Providers/PackageServiceProvider

<?php

namespace Yourname\Package\Providers;

use Illuminate\Support\ServiceProvider;
use Yourname\Package\Console\Commands\GreetCommand;

class PackageServiceProvider extends ServiceProvider
{
public function boot()
{
$this->commands([GreetCommand::class]);
}
}


В качестве родителя используется базовый класс Illuminate\Support\ServiceProvider Laravel.
Новосозданная команда GreetCommand добавляется в массив, запрашиваемый методом this->commands, позволяя проекту-шаблону получить доступ к команде.
Теперь пакет должен уведомить проект-шаблон о том, что новый ServiceProvider доступен для обнаружения. Это исключает необходимость вручную добавлять его в список ServiceProviders в файле конфигурации template app.php.

package/composer.json

"extra": {"laravel": {"providers": ["Yourname\\Package\\Providers\\PackageServiceProvider"]}},


Проверьте команду php artisan greet.
-
php artisan greet

"Привет, мир!" // ../package/src/Console/Commands/GreetCommand.

Если возникают какие-либо проблемы, рекомендуется выполнить команду composer update для перезагрузки нового пакета.

Вы можете внести изменения в файл web.php, чтобы протестировать команду приветствия через Artisan.

package/routes/web.php

use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Artisan;

Route::get('/', function () {
return Artisan::call('greet');
});


"Привет, мир!" // ../package/src/Console/Commands/GreetCommand.php:17

Если вы хотите, чтобы ваша команда GreetCommand была доступна исключительно в консольном режиме, добавьте следующее условие в PackageServiceProvider.

package/src/Providers/PackageServiceProvider.php

public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([GreetCommand::class]);
}
}

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