Routing

Laravel Fundamentals
Course 1 ยท Chapter 2 ยท 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.

โ†’ Solution

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.

โ†’ Solution

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.

โ†’ Solution

โš ๏ธ Gotcha: Route Order Matters

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.