Routování v routes/web.php, pojmenované routy a Route Model Binding
Jak Laravel 12 registruje a vyhodnocuje HTTP routy, proč používat pojmenované trasy a jak funguje automatický Implicitní a Scoped Route Model Binding.
Úvod do routovací vrstvy Laravelu 12
Routovací vrstva (Routing) představuje vstupní bránu do aplikační logiky frameworku. Jakmile HTTP požadavek projde přes webserver (FrankenPHP) a bootstrap fázi (bootstrap/app.php), jádro frameworku předá objekt Illuminate\Http\Request routovací tabulce definované v routes/web.php.
Hlavním úkolem routeru je porovnat příchozí HTTP metodu (GET, POST, PUT, DELETE atd.) a URI cestu proti registrovaným vzorům a určit cílový handler (akci v Controlleru, Livewire komponentu nebo anonymní funkci Closure).
Příchozí HTTP požadavek (GET /moduly/routing-middleware-a-controllery)
│
▼
routes/web.php ──► Vyhodnocení trasy & HTTP metody
│
▼
Implicitní / Scoped Route Model Binding (vyhledání entity v PostgreSQL)
│
▼
Předání čisté instance CourseModule do CourseController::module()
1. Pojmenované routy (Named Routes) a generování URL
V profesionálním vývoji se nikdy neodkazujeme na cílové stránky „natvrdo vepsaným“ textem URL (např. <a href="/moduly/...">). Místo toho přiřazujeme trasám unikátní název pomocí metody ->name():
Route::get('/moduly/{module:slug}', [CourseController::class, 'module'])
->name('course.module');
V Blade šablonách nebo přesměrováních pak URL sestavujeme pomocí helper funkce route():
<a href="{{ route('course.module', $module) }}">
{{ $module->title }}
</a>
Proč je tento přístup klíčový:
- Refaktoring beze strachu: Změní-li se kdykoliv struktura URL (např. z
/moduly/...na/kurz/kapitoly/...), stačí změnit jeden řádek vroutes/web.php. Veškeré odkazy v šablonách i přesměrování v aplikaci zůstanou okamžitě plně funkční. - Automatická serializace parametrů: Předáte-li do helperu
route('course.module', $module)přímo Eloquent model, Laravel z něj automaticky vytáhne klíč pro URL podle metodygetRouteKeyName().
2. Route Model Binding: Od URL parametru k Eloquent entitě
Jednou z nejelegantnějších vlastností Laravelu je Route Model Binding. Místo ručního dotazování do databáze v controlleru necháme framework vyhledat model automaticky:
A) Implicitní vazba podle sloupce (Scoped / Custom Key)
Ve výchozím stavu Laravel hledá záznamy podle primárního klíče id. V moderních webových aplikacích však vyžadujeme SEO přívětivé URL se slugem:
// routes/web.php
Route::get('/moduly/{module:slug}', [CourseController::class, 'module'])
->name('course.module');
Syntaxe {module:slug} říká frameworku: „Vezmi hodnotu z URL, najdi v tabulce course_modules řádek, kde slug = :hodnota, a předej nalezenou instanci CourseModule do parametru metody controlleru.“ Pokud záznam neexistuje, router okamžitě vyhodí výjimku ModelNotFoundException, která vyústí v čistou HTTP chybu 404 Not Found.
B) Vnořený Scoped Route Model Binding
V naší aplikaci máme trasu pro zobrazení konkrétní lekce v modulu:
Route::get('/moduly/{module:slug}/lekce/{lesson:slug}', [CourseController::class, 'lesson'])
->name('course.lesson');
Zde nastupuje Scoped Binding: Laravel nejenže vyhledá $module a $lesson, ale díky Eloquent relaci mezi CourseModule a CourseLesson automaticky ověří, že daná lekce skutečně patří tomuto konkrétnímu modulu! Pokud by se útočník pokusil podvrhnout cizí slug lekce k jinému modulu, framework automaticky vrátí 404 Not Found.
3. Rozbor reálného kódu v naší aplikaci
Podívej se do našeho souboru routes/web.php. Zde jsou definovány veškeré veřejné trasy platformy:
Route::get('/', [CourseController::class, 'index'])->name('course.index');— Hlavní rozcestník.Route::get('/moduly/{module:slug}', [CourseController::class, 'module'])->name('course.module');— Přehled modulu.Route::get('/moduly/{module:slug}/lekce/{lesson:slug}', [CourseController::class, 'lesson'])->name('course.lesson');— Detail lekce s živou anatomií.Route::get('/api/status', ...)->name('api.status');— JSON API endpoint pro ověření stavu aplikace.
<?php
declare(strict_types=1);
use App\Http\Controllers\AnatomyController;
use App\Http\Controllers\CourseController;
use App\Models\CourseLesson;
use App\Models\CourseModule;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Route;
// 1. Hlavní rozcestník osnovy a živá anatomie
Route::get('/', [CourseController::class, 'index'])->name('course.index');
Route::get('/anatomie', [AnatomyController::class, 'index'])->name('anatomy.index');
// 2. Přepnutí profilu studenta pomocí přenosového kódu (POST požadavek)
Route::post('/profil/prenosovy-kod', [CourseController::class, 'switchProfile'])->name('course.switch-profile');
// 3. Modul a Scoped Route Model Binding pro lekce ({module:slug} a {lesson:slug})
Route::get('/moduly/{module:slug}', [CourseController::class, 'module'])->name('course.module');
Route::get('/moduly/{module:slug}/lekce/{lesson:slug}', [CourseController::class, 'lesson'])->name('course.lesson');
// 4. Diagnostický JSON endpoint vracející stav systému a počty modulů
Route::get('/api/status', function (): JsonResponse {
return response()->json([
'app' => config('app.name'),
'php_version' => PHP_VERSION,
'laravel_version' => app()->version(),
'modules_count' => CourseModule::count(),
'lessons_count' => CourseLesson::count(),
'status' => 'ok',
]);
})->name('api.status');
🎯 Ověření znalostí z lekce
Kvíz otestuje pochopení architektury a reálného kódu probíraného v této lekci.
Co přesně znamená zápis parametru {module:slug} v definici Laravel trasy?
Jak se v Laravelu chová Scoped Route Model Binding při zápisu /moduly/{module:slug}/lekce/{lesson:slug}?
Jaká je hlavní architektonická výhoda používání pojmenovaných tras přes route('course.lesson', [$m, $l])?
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | use App\Http\Controllers\AnatomyController; |
| 6 | use App\Http\Controllers\CourseController; |
| 7 | use App\Models\CourseLesson; |
| 8 | use App\Models\CourseModule; |
| 9 | use Illuminate\Http\JsonResponse; |
| 10 | use Illuminate\Support\Facades\Route; |
| 11 | |
| 12 | Route::get('/', [CourseController::class, 'index'])->name('course.index'); |
| 13 | Route::get('/anatomie', [AnatomyController::class, 'index'])->name('anatomy.index'); |
| 14 | |
| 15 | Route::post('/profil/prenosovy-kod', [CourseController::class, 'switchProfile'])->name('course.switch-profile'); |
| 16 | |
| 17 | Route::get('/moduly/{module:slug}', [CourseController::class, 'module'])->name('course.module'); |
| 18 | Route::get('/moduly/{module:slug}/lekce/{lesson:slug}', [CourseController::class, 'lesson'])->name('course.lesson'); |
| 19 | |
| 20 | Route::get('/api/status', function (): JsonResponse { |
| 21 | return response()->json([ |
| 22 | 'app' => config('app.name'), |
| 23 | 'php_version' => PHP_VERSION, |
| 24 | 'laravel_version' => app()->version(), |
| 25 | 'modules_count' => CourseModule::count(), |
| 26 | 'lessons_count' => CourseLesson::count(), |
| 27 | 'status' => 'ok', |
| 28 | ]); |
| 29 | })->name('api.status'); |
| 30 | |