Les 4 · Module 1 · Laravel-basis
Request lifecycle, service container en facades
Begrijp wat Laravel doet tussen verzoek en antwoord: de levenscyclus, middleware, de service container met dependency injection, service providers en facades.
🎯 Na deze les kun je
- de stappen van een verzoek door Laravel benoemen
- een eigen service via de container laten injecteren
- een binding registreren in een service provider
- uitleggen wat een facade is en hoe hij zich verhoudt tot injectie
Tot nu toe "werkte het gewoon": je zette Request $request in een methode en Laravel gaf je een request. Om fouten te
begrijpen en je code netjes op te delen, moet je weten hoe dat gebeurt. In deze les volg je een verzoek door
Laravel en maak je een eigen service voor de btw-berekening van de kassa.
De levenscyclus van een verzoek
public/index.php
└─ bootstrap/app.php app maken, providers registreren
└─ HTTP-kernel globale middleware (trim strings, sessie, CSRF …)
└─ router route zoeken, route-middleware (auth …)
└─ controller jouw code, met geïnjecteerde afhankelijkheden
◀─ response terug door de middleware (cookies, headers)
Middleware is code die om elk verzoek heen zit, zoals lagen van een ui. Voorbeelden die standaard aanstaan:
TrimStrings (spaties weg uit invoer), ConvertEmptyStringsToNull, StartSession, VerifyCsrfToken.
Een eigen middleware die de duur van elk verzoek logt:
php artisan make:middleware MeetDuur
public function handle(Request $request, Closure $next): Response
{
$start = microtime(true);
$response = $next($request); // hier draait de rest van de app
$ms = round((microtime(true) - $start) * 1000);
$response->headers->set('X-Duur-ms', (string) $ms);
return $response;
}
Registreren in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\App\Http\Middleware\MeetDuur::class);
})
De service container
De service container is een fabriek die objecten maakt en hun afhankelijkheden invult. Zet je een klasse als type in een controller-methode of constructor, dan vraagt Laravel de container om een exemplaar.
// app/Services/BtwCalculator.php
namespace App\Services;
class BtwCalculator
{
public function __construct(private float $tarief = 0.21) {}
public function btwDeel(float $inclusief): float
{
return round($inclusief * $this->tarief / (1 + $this->tarief), 2);
}
}
class KassaController extends Controller
{
public function __construct(private BtwCalculator $btw) {}
public function totaal(int $bedrag)
{
return view('kassa.totaal', ['bedrag' => $bedrag, 'btw' => $this->btw->btwDeel($bedrag)]);
}
}
Je schrijft nergens new BtwCalculator(). De container ziet het type, maakt het object en geeft het door. Dat heet
dependency injection: de controller hoeft niet te weten hoe hij een calculator maakt. In een test kun je een andere
calculator meegeven.
Voorspel eerst: wat gebeurt er als de constructor __construct(private float $tarief) zonder standaardwaarde heeft?
Uitkomst
BindingResolutionException: Unresolvable dependency resolving [Parameter #0 [ <required> float $tarief ]]. De container
kan klassen automatisch maken, maar weet niet welk getal hij voor $tarief moet nemen. Dat vertel je hem in een service
provider.
Bindings in een service provider
// app/Providers/AppServiceProvider.php
public function register(): void
{
$this->app->singleton(BtwCalculator::class, fn () => new BtwCalculator(config('kassa.btw')));
}
Met config/kassa.php:
return ['btw' => (float) env('KASSA_BTW', 0.21)];
bind: elke keer een nieuw object.singleton: één object per verzoek/proces.register()is voor bindings,boot()voor dingen die andere services nodig hebben (view composers, macro's).- Gebruik
env()alleen in configbestanden. Naphp artisan config:cachegeeftenv()eldersnull.
Interfaces binden
Het echte voordeel: je kunt een interface binden aan een implementatie.
interface Betaalprovider { public function start(int $centen): string; }
$this->app->bind(Betaalprovider::class, MollieBetaalprovider::class);
Een controller vraagt om Betaalprovider; in tests bind je een NepBetaalprovider. De controller verandert niet.
Facades
Je zag al Route::get(...). Route is een facade: een statische snelweg naar een object in de container.
use Illuminate\Support\Facades\Cache;
Cache::put('omzet', 312.40, 60); // is hetzelfde als app('cache')->put(...)
Facades zijn kort en goed testbaar (Cache::shouldReceive(...)). Nadeel: je ziet niet aan de constructor welke
afhankelijkheden een klasse heeft. Vuistregel: facades in routes, views en kleine code; injectie in services met logica.
Typische fouten
BindingResolutionException: de container kan een parameter niet invullen (scalar zonder standaard, interface zonder binding).config('kassa.btw')geeftnull: het bestand heet anders, of de config is gecachet (php artisan config:clear).- Middleware draait niet: niet geregistreerd in
bootstrap/app.php, of alleen op een route-groep gezet.
Oefenen
- Maak de middleware
MeetDuuren controleer de header metcurl -I. - Maak
BtwCalculatoren laatKassaControllerhem via de constructor krijgen. - Lees het tarief uit
config/kassa.phpen.enven bind de calculator als singleton. - Bewijs met
php artisan tinkerdatapp(BtwCalculator::class) === app(BtwCalculator::class)bijsingletontrue is en bijbindfalse.
Modelantwoord oefening 1
curl -sI http://127.0.0.1:8000/ | grep -i x-duur
X-Duur-ms: 14
Modelantwoord oefening 2
Zie het voorbeeld hierboven. /totaal/121 geeft nu btw: 21,00: het btw-deel van een bedrag inclusief btw.
Modelantwoord oefening 3
# .env
KASSA_BTW=0.09
Na php artisan config:clear toont /totaal/109 een btw van 9,00.
Modelantwoord oefening 4
> app(App\Services\BtwCalculator::class) === app(App\Services\BtwCalculator::class)
= true
Met bind is het false: twee verschillende objecten. Kies singleton voor objecten zonder staat per gebruiker.
Lokaal oefenen
Geen live lab. php artisan tinker is je speeltuin: probeer app(), config() en resolve() uit.
Samenvatting
- Een verzoek gaat door middleware naar de router en de controller, en terug.
- De container maakt objecten en injecteert afhankelijkheden; providers vertellen hem wat hij niet kan raden.
- Facades zijn een statische toegang tot containerobjecten.
- Volgende les: formulieren en validatie.