Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Фасади, контракти й залежності в пакеті: як не прив'язати користувача до зайвого і дати змогу замінити реалізацію?

Пакет живе в чужому застосунку. Що менше він нав'язує, то легше його встановити, налаштувати й замінити.

Залежності в коді пакета - через інтерфейси й контейнер:

namespace Acme\Invoices\Contracts;

interface PdfRenderer
{
    public function render(string $html): string;
}
// InvoicesServiceProvider::register()
$this->app->singleton(PdfRenderer::class, fn ($app) => new DompdfRenderer(
    $app['config']->get('invoices.pdf'),
));

$this->app->singleton(InvoiceGenerator::class);
final class InvoiceGenerator
{
    public function __construct(
        private PdfRenderer $renderer,
        private Filesystem $files,     // Illuminate\Contracts\Filesystem\Filesystem
    ) {}
}

Застосунок замінює реалізацію одним рядком у своєму провайдері - без форку пакета:

$this->app->singleton(PdfRenderer::class, BrowsershotRenderer::class);

Фасади:

  • фасад - зручність для користувача, а не спосіб писати внутрішній код пакета. Усередині пакета краще впровадження залежностей: код тестується без фасадів і не залежить від глобального стану;
  • фасад пакета вказує на прив'язку в контейнері (getFacadeAccessor повертає клас чи ключ), тож заміна реалізації працює й для нього;
  • реєструвати аліас через extra.laravel.aliases - необов'язково: багато користувачів імпортують клас фасаду напряму.

Необов'язкові залежності:

"require": { "illuminate/support": "^12.0|^13.0" },
"suggest": { "spatie/browsershot": "Для рендерингу PDF через Chrome" }
if (! class_exists(\Spatie\Browsershot\Browsershot::class)) {
    throw new MissingDependency('Install spatie/browsershot to use the browsershot driver.');
}

Важка залежність (headless Chrome, SDK хмари) не має ставитися всім, кому потрібна лише частина пакета.

Моделі застосунку:

  • не імпортувати App\Models\User - модель береться з конфігурації: config('invoices.user_model', config('auth.providers.users.model'));
  • власні моделі пакета дозволяти розширювати: config('invoices.models.invoice') і використовувати їх через цей ключ.

Події замість жорстких викликів: InvoicePaid дозволяє застосунку реагувати (лист, вебхук, бухгалтерія), не змінюючи код пакета.

Ознака добре спроєктованого пакета: його можна використовувати без фасадів, без публікації файлів і з власною реалізацією кожної зовнішньої залежності.

Докладніше в документації: Пакети: про фасади

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання