Создание Laravel пакета в локальной среде
Вас не должно удивлять, что в процессе разработки веб-инструментов мы используем сотни пакетов. Чтобы получить их, достаточно посетить менеджер пакетов, например Packagist, который в октябре 2023 года насчитывал 382 000 таких пакетов.
Если вы хотите разработать свой пакет, то вполне резонно задаться вопросом, как протестировать его в реальных условиях. Публикация пакета на Packagist во время разработки — не вариант. Другой подход заключается в том, чтобы интегрировать его в новый проект без использования Composer. Метод, описанный в этой статье, в значительной степени имитирует реальный сценарий, но требует некоторых настроек среды.
Создайте папку, которая станет основой для вашего пакета.
mkdir package
Создайте файл composer.json
, являющийся важнейшей основой пакета.
{
"name": "capsulescodes/package",
"description": "Capsules Codes Package",
"type": "library",
"autoload": { "psr-4" : { "CapsulesCodes\\Package\\" : "src/" } }
}
name
: Имя должно быть структурировано так, чтобы слева располагалась сущность, а справа — имя пакета. Настоятельно рекомендуется использовать описательное имя пакета, упрощающее поиск для пользователей.description
: Описание пакета должно быть чётким и лаконичным.type
: Существует четыре типа:library
,project
,metapackage
иcomposer-plugin
. Наиболее часто используется типlibrary
.project
представляет проект, например, шаблон фреймворка.autoload
: Это основной элемент нашего пакета, определяющий пространство имён для доступа к данным, расположенным в корне проекта. В PSR-4 определён протокол спецификации автозагрузки классов. Это очень важный шаг. Обязательно включите\\
, особенно в конце выражения.
Желательно, чтобы информация о name
соответствовала namespace
. Кроме того, в качестве имени папки в корне проекта рекомендуется использовать src
.
Кроме того, если запустить composer init
в папке, не содержащей файла composer.json
, при помощи помощника будет выполнен процесс создания файла composer.json
.
Создайте папку src
внутри папки с пакетом.
cd package
mkdir src
- Расположение файлов не имеет особого значения, кроме их близости, как для вас, так и для данной статьи.
Создайте PHP-класс с именем Greeter
, содержащий функцию greet
, возвращающую фразу Hello world!
<?php
namespace CapsulesCodes\Package;
class Greeter
{
public function greet() : string
{
return "Hello world!";
}
}
- Не забудьте указать
namespace
. В противном случае класс не будет найден.
Теперь можно протестировать этот пакет. Для этого необходимо создать тестовую среду, используя шаблонный проект Laravel.
Вернитесь в родительский каталог пакета и создайте шаблонный Laravel-проект, выполняющий роль теста.
cd ../../
composer create-project laravel/laravel template
- Команда
composer create-project laravel/laravel template
формирует здесь'type': 'project'
.
Чтобы сообщить template
(нашему Laravel проекту), что наш пакет находится в той же родительской папке, необходимо добавить в файл composer.json
два фрагмента информации.
template/composer.json
...
"minimum-stability": "dev",
"repositories": [ { "type" : "path", "url" : "../package" } ]
...
minimum-stability
: Эта опция позволяет установить пакет через composer без генерации исключения. Это необходимо, если пакет не являетсяstable
, в данном случае наш пакет являетсяdev
.repositories
: Этот массив позволяет добавить пути к другим каталогам, к которым должен обратиться composer, чтобы найти пакет локально.type
: В качестве типа каталога может быть указанcomposer
,package
,vcs
илиpath
. Опцияpath
позволяет использовать пакет локально, а опцияvcs
— через систему контроля версий, например Github.
Настало время установить наш новый пакет.
composer require capsulescodes/package
Теперь он указан в списке зависимостей require
.
package/composer.json
"require": {
...
"capsulescodes/package": "dev-main",
...
}
- Поскольку пакет находится в стадии разработки, используется версия
dev-main
.
Протестируйте с помощью php artisan tinker
php artisan tinker
> CapsulesCodes\Package\Greeter::greet()
= "Hello world!"
Вы можете модифицировать файл web.php
для проверки статического класса Greeter
.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use CapsulesCodes\Package\Greeter;
Route::get( '/', fn() => dd( Greeter::greet() ) );
"Hello world!" // routes/web.php:7`
Рабочая среда готова.
Было бы интересно добавить дополнительный метод say()
для тестирования инструмента в реальном времени.
package/src/Greeter.php
<?php
namespace CapsulesCodes\Package;
class Greeter
{
public static function greet() : string
{
return "Hello world!";
}
public static function say( string $something ) : string
{
return $something;
}
}
Протестируйте с помощью php artisan tinker
. Возможно, вам следует перезагрузить его.
php artisan tinker
> CapsulesCodes\Package\Greeter::say( "That's a quick way to develop and test a package!" )
= "That's a quick way to develop and test a package!"
Вы можете модифицировать файл web.php
для проверки статического класса Greeter
.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use CapsulesCodes\Package\Greeter;
Route::get( '/', fn() => dd( Greeter::say( "That's a quick way to develop and test a package!" ) ) );
"That's a quick way to develop and test a package!" // routes/web.php:7
На данном этапе все готово для разработки PHP Framework пакета. Для создания пакета Laravel потребуются дополнительные шаги. В данной статье ставится задача реализовать команду php artisan greet
, вызывающую статический класс Greeter
.
При создании Laravel пакета рекомендуется придерживаться типовой структуры Laravel проекта. Это облегчает поиск информации для тех, кому она необходима.
Сначала необходимо создать команду GreetCommand
, расширив команду Illuminate\Console\Command
, специфичную для Laravel.
package/src/Commands/GreetCommand.php
<?php
namespace CapsulesCodes\Package\Console\Commands;
use Illuminate\Console\Command;
use CapsulesCodes\Package\Greeter;
class GreetCommand extends Command
{
protected $signature = "greet";
protected $description = "Greet people with a 'Hello world!'";
public function handle()
{
dump( Greeter::greet() );
}
}
- В качестве расширения используется базовый класс
Illuminate\Console\Command
из Laravel. - В методе
handle()
используется созданный ранее статический классGreeter
. Однако для тестирования пакета можно просто заменитьreturn Greeter::greet()
наreturn "Hello world!"
.
Для того чтобы проект модели распознал эту команду, необходимо уведомить его об этом с помощью ServiceProvider
.
package/src/Providers/PackageServiceProvider
<?php
namespace CapsulesCodes\Package\Providers;
use Illuminate\Support\ServiceProvider;
use CapsulesCodes\Package\Console\Commands\GreetCommand;
class PackageServiceProvider extends ServiceProvider
{
public function boot()
{
$this->commands( [ GreetCommand::class ] );
}
}
- В качестве расширения используется базовый класс
Illuminate\Support\ServiceProvider
из Laravel. - Вновь созданная команда
GreetCommand
добавляется в массив, запрашиваемый методомthis->commands
, что позволяет проектуtemplate
получить доступ к этой команде.
Теперь пакет должен сообщить проекту модели, что новый ServiceProvider
доступен для обнаружения. Это избавляет от необходимости вручную добавлять его в список ServiceProviders
в конфигурационном файле template
app.php
.
package/composer.json
...
"extra" : { "laravel" : { "providers" : [ "CapsulesCodes\\Package\\Providers\\PackageServiceProvider" ] } },
...
Протестируйте команду php artisan greet
.
php artisan greet
"Hello world!" // ../package/src/Console/Commands/GreetCommand.php:17
При возникновении проблем рекомендуется выполнить команду composer update
для перезагрузки нового пакета.
Для проверки команды greet
через Artisan можно модифицировать файл web.php
.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Artisan;
Route::get( '/', fn() => Artisan::call( 'greet' ) );
"Hello world!" // ../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 ] );
}
Также можно реализовать файл конфигурации, миграции, тесты, маршруты или представления. Возможно, об этом мы поговорим позже.