Все любят кричать "пиши переиспользуемый код". Только потом этот "код" превращается в копипасту из проекта в проект. Решение старое как говно мамонта — вынеси в пакет. Laravel это умеет. Но тут есть нюансы.

Сколько раз было: сидишь такой, пилишь проект на Laravel, и вдруг понимаешь, что уже четвёртый раз пишешь один и тот же хелпер для работы с каким-нибудь API или свою логику уведомлений. И вроде руки чешутся "переиспользовать", но по факту ты просто тащишь файлы по папкам как бродячий цирк.

А потом начинается ад: где-то у тебя Utils.php, где-то HelperTrait.php, и хрен вспомнишь, в какой версии проекта лежит последний "правильный" вариант. Вот для этого и существуют пакеты. Не потому что "правильно и модульно", а потому что задолбало копипастить.

Laravel здесь не изобретает велосипед — просто подсовывает тебе удобный способ втащить свой код как полноценную библиотеку.

Скелет пакета

Старт банален:

mkdir my-package
cd my-package

Внутри пилим composer.json. Да-да, без него это даже не пакет, а просто папка.

{
    "name": "your-vendor/my-package",
    "description": "Just another package",
    "type": "library",
    "require": {
        "php": "^7.3 || ^8.0",
        "illuminate/support": "^8.0"
    },
    "autoload": {
        "psr-4": {
            "YourVendor\\MyPackage\\": "src/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "YourVendor\\MyPackage\\MyPackageServiceProvider"
            ]
        }
    } } 

Вот тут важный момент: autoload и providers. Если их забудешь — Laravel даже не узнает о твоём чуде.

Сам пакет

Создаём src/ и туда кидаем свой "мозг":

<?php

namespace YourVendor\MyPackage;

class MyPackage
{
    public function doSomething()
    {
        // твой код, который ты устал копипастить
    }
}

Вот и весь "фреймворк". Пакет — это просто папка с классами.

ServiceProvider: без него ты никто

Laravel грузит всё через ServiceProvider. Не будет его — твой пакет так и останется мёртвым архивом.

<?php

namespace YourVendor\MyPackage;

use Illuminate\Support\ServiceProvider;

class MyPackageServiceProvider extends ServiceProvider
{
    public function boot()
    {
        // маршруты, вьюхи, переводы
    }

    public function register()
    {
        // бинды в контейнер, синглтоны, сервисы
    }
}

В boot() ты можешь подсунуть Laravel свои маршруты, вьюхи или конфиги. В register() — повесить зависимости.

Подключаем в проект

В корне Laravel-проекта:

composer require your-vendor/my-package

И вуаля — пакет встал, Laravel его видит.

Теперь можно использовать:

use YourVendor\MyPackage\MyPackage;

$myPackage = new MyPackage();
$myPackage->doSomething();

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

Если у тебя там лежат свои config, views, lang — Laravel даёт команду:

php artisan vendor:publish --provider="YourVendor\MyPackage\MyPackageServiceProvider" 

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

Зачем это вообще?

— Чтобы не копипастить одни и те же классы в десяти проектах.
— Чтобы не искать, "а в какой версии у нас был фикс бага с JWT?".
— Чтобы твой код можно было воткнуть командой composer require, а не ручным слиянием файлов.

Это всё.

Тёмная сторона

Тут без сказок:

  • Если ты любишь менять API пакета каждые два дня — ненавидеть тебя будут даже твои коллеги.
  • Если твой пакет жёстко завязан на конкретную версию Laravel — рано или поздно ты упрёшься лбом в апгрейд.
  • Если пакет настолько "уникален", что нужен только тебе — может, он вообще не нужен как пакет?

Итог

Создание пакетов в Laravel — это не про "модульность ради модульности". Это про лень и здравый смысл. Устал тащить одни и те же костыли из проекта в проект? Запихни их в пакет. Не устал — забей, катайся дальше на копипасте.