Routing
๐งญ Routing
routes/web.php is Laravel's single routing table โ the same concept as Django's urls.py, expressed as fluent, chainable method calls instead of a list of path() entries. This chapter covers basic routes, parameters, named routes, groups, and the single line that replaces an entire CRUD route set.
Basic Routes
A route maps an HTTP verb and URI to a closure or, more commonly, a controller method (covered next chapter):
// routes/web.php use Illuminate\Support\Facades\Route; Route::get('/books', function () { return 'All books'; });
๐ข Route Parameters
Curly braces capture a URL segment โ directly analogous to Django's angle-bracket converters, just without a built-in type conversion syntax of its own:
Route::get('/books/{id}', function (string $id) { return "Showing book #{$id}"; }); // Optional parameter โ note the ?, and a default value for the closure argument Route::get('/books/{id?}', function (string $id = 'latest') { return "Showing book: {$id}"; }); // Constrain the parameter to digits only, using ->where() Route::get('/books/{id}', function (string $id) { return "Showing book #{$id}"; })->where('id', '[0-9]+');
Django's Converters vs Laravel's where()
Django
path("<int:book_id>/", views.book_detail) # the <int:...> converter validates AND type-converts
Laravel
Route::get('/{id}', ...)->where('id', '[0-9]+'); // a regex constraint validates the FORMAT โ the value still // arrives as a string; there's no built-in type conversion
Named Routes
The exact same idea as Django's name= + reverse() โ never hardcode a URL that a named route already describes:
Route::get('/books/{id}', [BookController::class, 'show'])->name('books.show');
// In a Blade template (Chapter 4) or PHP code: <a href="{{ route('books.show', ['id' => 42]) }}">View Book</a> // -> "/books/42" โ built from the route definition, not a hand-typed string
๐ฆ Route Groups
Laravel doesn't split routes across separate per-app files the way Django's include() does โ instead, Route::prefix() and ->group() apply a shared prefix (and, commonly, shared middleware) to a block of routes within the same file:
Route::prefix('admin')->group(function () { Route::get('/books', [AdminBookController::class, 'index']); Route::get('/orders', [AdminOrderController::class, 'index']); }); // Both routes are now reachable under /admin/books and /admin/orders
Grouping, Not Separate Files
A large Laravel app can split routes across multiple files (require'd into web.php), but there's no built-in per-app routing file the way every Django app gets its own urls.py.
Groups Also Share Middleware
Route::middleware('auth')->group(...) applies an auth check to every route inside โ Course 2 covers middleware and auth in depth.
Resource Routes: One Line, Seven Routes
This is Laravel's version of a pattern this project has seen twice already โ Rails' resources :books and Django REST Framework's router.register() โ one line generating a full set of RESTful routes:
Route::resource('books', BookController::class); // Generates all seven RESTful routes at once: // GET /books -> index // GET /books/create -> create // POST /books -> store // GET /books/{book} -> show // GET /books/{book}/edit -> edit // PUT /books/{book} -> update // DELETE /books/{book} -> destroy
Run php artisan route:list (from the last chapter's challenge) after adding a resource route to see exactly what it expanded into.
๐ป Coding Challenges
Challenge 1: Write Basic Routes With Parameters
Write three routes in routes/web.php: a book list at /books, a book detail at /books/{id} constrained to digits only, and a category listing at /books/category/{slug} โ each with a sensible name().
Goal: Practice route parameters, where() constraints, and naming routes from the start.
Challenge 2: Group Routes Under a Prefix
Group the three routes from Challenge 1 under an /api prefix using Route::prefix()->group(), and state the final full path for each.
Goal: Practice route grouping and reasoning about the combined prefix + pattern.
Challenge 3: Replace Manual Routes With Route::resource()
Given a hand-written set of five separate Route::get()/post()/put()/delete() calls for a Product controller, replace them with a single Route::resource() call and list the exact route names it generates.
Goal: Practice recognizing when a hand-written route set is really just a resource route in disguise.
Laravel matches routes top-to-bottom, same as Django โ the first pattern that matches wins, even if a later one would have been a better fit. Defining Route::get('/books/{id}', ...) before Route::get('/books/create', ...) means visiting /books/create actually matches the {id} route first, with $id literally set to the string "create" โ the more specific static route never gets a chance to run. Always define specific, static routes (like /books/create) before parameterized ones that could shadow them (like /books/{id}).
๐ฏ What's Next
Routes now point somewhere โ the next chapter covers what they point to: Controllers, php artisan make:controller, resource controllers, and how Laravel's controllers compare to Django's views.