Srdce frameworku: Service Container, Dependency Injection a Service Providers
Pronikněte do fungování IoC kontejneru v Laravelu, pochopte automatické vstřikování závislostí (Dependency Injection) přes PHP reflexi a naučte se správně používat register() a boot() v Service Providerech.
Co je Service Container (IoC Kontejner)
Service Container je centrálním mozkem celého Laravelu. Jedná se o implementaci vzoru Inversion of Control (IoC), která řeší dvě klíčové úlohy:
- Správu závislostí: Ví, jak vytvořit jakoukoliv třídu a jaké další třídy k tomu potřebuje.
- Dependency Injection (vstřikování závislostí): Automaticky předává potřebné instance do konstruktorů a metod controllerů bez nutnosti manuálního
new.
Díky PHP Reflection API Laravel disponuje Zero-Configuration Dependency Injection: pokud controller vyžaduje službu AnatomyInspectorService, kontejner prozkoumá její konstruktor, zjistí, jaké parametry potřebuje, sám je vytvoří a službu do controlleru injektuje.
Rozdíl mezi bind() a singleton()
V kontejneru můžeme služby registrovat dvěma základními způsoby:
-
Transient binding (
$this->app->bind()):- Při každém požadavku na danou třídu kontejner vytvoří novou, čerstvou instanci.
- Vhodné pro lehké, bezstavové pomocníky nebo objekty vyžadující pokaždé unikátní konfiguraci.
-
Singleton binding (
$this->app->singleton()):- Třída se instanciována pouze jednou při prvním vyžádání.
- Při všech dalších voláních v rámci stejného životního cyklu kontejner vrátí tutéž existující instanci.
- V našem projektu takto registrujeme
AnatomyInspectorServiceaStudentProgressService.
$this->app->singleton(AnatomyInspectorService::class);
Životní cyklus Service Provideru: register() vs. boot()
Service Providery (jako je AppServiceProvider) jsou místem, kde se aplikace konfiguruje a propojuje. Každý provider má dvě základní metody a je životně důležité chápat rozdíl mezi nimi:
Metoda register()
- Slouží výhradně pro vazby do kontejneru (
bind,singleton). - V této fázi ještě nejsou ostatní provideři nabootovaní!
- Pravidlo: V metodě
register()se nikdy nesmíte pokoušet volat byznys logiku jiných služeb, registrovat event listenery nebo používat Eloquent modely.
Metoda boot()
- Volá se až poté, co proběhly metody
register()úplně všech providerů v aplikaci. - Zde máte stoprocentní jistotu, že všechny služby jsou v kontejneru připraveny.
- Slouží k registraci eventů, view composerů, routových vzorů a globální konfiguraci modelů.
V našem AppServiceProvider.php využíváme metodu boot() k vynucení striktního chování databázových modelů:
public function boot(): void
{
Model::shouldBeStrict(! $this->app->isProduction());
if ($this->app->isProduction()) {
URL::forceScheme('https');
}
}
Metoda Model::shouldBeStrict() v lokálním vývoji automaticky vyhodí výjimku, pokud:
- Dojde k N+1 línému načítání relací (Lazy Loading Violation).
- Pokusíte se naplnit nechráněný atribut (Mass Assignment Violation).
- Přistoupíte k neexistujícímu sloupci modelu.
<?php
declare(strict_types=1);
namespace App\Providers;
use App\Services\AnatomyInspectorService;
use App\Services\StudentProgressService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;
// Reálný Service Provider naší aplikace (app/Providers/AppServiceProvider.php)
class AppServiceProvider extends ServiceProvider
{
/**
* 1. Fáze register: Pouze vazby do Service Containeru
*/
public function register(): void
{
// Registrace služeb jako sdílených Singletonů
$this->app->singleton(AnatomyInspectorService::class);
$this->app->singleton(StudentProgressService::class);
}
/**
* 2. Fáze boot: Konfigurace po nabootování všech providerů
*/
public function boot(): void
{
// Zapnutí striktního režimu Eloquentu mimo produkci (detekce N+1 dotazů)
Model::shouldBeStrict(! $this->app->isProduction());
// Vynucení HTTPS schématu v produkčním prostředí za Caddy proxy
if ($this->app->isProduction()) {
URL::forceScheme('https');
}
}
}
🎯 Ověření znalostí z lekce
Kvíz otestuje pochopení architektury a reálného kódu probíraného v této lekci.
Jaký je zásadní rozdíl mezi metodami register() a boot() v Service Provideru?
Co znamená registrace služby pomocí $this->app->singleton(MyService::class)?
Co v praxi způsobí volání Model::shouldBeStrict(! $this->app->isProduction()) v metodě boot()?
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace App\Providers; |
| 6 | |
| 7 | use App\Services\AnatomyInspectorService; |
| 8 | use App\Services\StudentProgressService; |
| 9 | use Illuminate\Database\Eloquent\Model; |
| 10 | use Illuminate\Support\Facades\URL; |
| 11 | use Illuminate\Support\ServiceProvider; |
| 12 | |
| 13 | class AppServiceProvider extends ServiceProvider |
| 14 | { |
| 15 | /** |
| 16 | * Register any application services in the Service Container. |
| 17 | */ |
| 18 | public function register(): void |
| 19 | { |
| 20 | $this->app->singleton(AnatomyInspectorService::class); |
| 21 | $this->app->singleton(StudentProgressService::class); |
| 22 | } |
| 23 | |
| 24 | /** |
| 25 | * Bootstrap any application services. |
| 26 | */ |
| 27 | public function boot(): void |
| 28 | { |
| 29 | Model::shouldBeStrict(! $this->app->isProduction()); |
| 30 | |
| 31 | if ($this->app->isProduction()) { |
| 32 | URL::forceScheme('https'); |
| 33 | } |
| 34 | } |
| 35 | } |
| 36 | |