Через контекстні атрибути чи контекстну прив'язку - тоді залежність видно в конструкторі, а не всередині методів.
Атрибутами (найкоротше):
class ReportExporter
{
public function __construct(
#[Storage('reports')] private Filesystem $disk,
#[Config('reports.per_page')] private int $perPage,
#[Log('reports')] private LoggerInterface $log,
) {}
}
Контекстною прив'язкою в провайдері - коли атрибут на чужому класі не поставиш:
$this->app->when(ReportExporter::class)
->needs('$perPage')
->giveConfig('reports.per_page');
$this->app->when(ReportExporter::class)
->needs(Filesystem::class)
->give(fn () => Storage::disk('reports'));
Чим це краще за Storage::disk('reports') усередині методу:
- залежності чесно перелічені в конструкторі - видно, від чого клас залежить;
- у тесті їх підставляють напряму, без підміни фасадів;
- різним класам можна дати різні реалізації того самого інтерфейсу.
Власні атрибути створюють, реалізувавши контракт ContextualAttribute з методом resolve().