Master Laravel routing, route parameters, route groups, and controller patterns.
Master Laravel routing, route parameters, route groups, and controller patterns.
Routing is the foundation of any web application. Laravel provides an expressive and powerful routing system that makes defining routes intuitive and flexible.
Routes are defined in the routes/ directory. Web routes go in routes/web.php, and API routes in routes/api.php.
1<?php
2// routes/web.php
3
4use Illuminate\Support\Facades\Route;
5
6// Basic GET route
7Route::get('/', function () {
8 return 'Welcome to Laravel!';
9});
10
11// Return a view
12Route::get('/about', function () {
13 return view('about');
14});
15
16// Return JSON
17Route::get('/api/status', function () {
18 return response()->json([
19 'status' => 'active',
20 'version' => '1.0.0'
21 ]);
22});1Route::get($uri, $callback); // GET request
2Route::post($uri, $callback); // POST request
3Route::put($uri, $callback); // PUT request
4Route::patch($uri, $callback); // PATCH request
5Route::delete($uri, $callback); // DELETE request
6Route::options($uri, $callback); // OPTIONS request
7
8// Match multiple verbs
9Route::match(['get', 'post'], '/form', function () {
10 return 'Handles GET and POST';
11});
12
13// Match any verb
14Route::any('/catch-all', function () {
15 return 'Handles all HTTP verbs';
16});Routes can capture segments of the URI.
1// Single parameter
2Route::get('/users/{id}', function (string $id) {
3 return "User ID: {$id}";
4});
5
6// Multiple parameters
7Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) {
8 return "Post {$postId}, Comment {$commentId}";
9});
10
11// Order matters - parameters are passed in order
12Route::get('/category/{category}/product/{product}',
13 function (string $category, string $product) {
14 return "Category: {$category}, Product: {$product}";
15 }
16);1// Optional parameter with default value
2Route::get('/users/{name?}', function (?string $name = 'Guest') {
3 return "Hello, {$name}!";
4});
5
6// Multiple optional parameters
7Route::get('/archive/{year?}/{month?}', function (?string $year = null, ?string $month = null) {
8 $year = $year ?? date('Y');
9 $month = $month ?? date('m');
10 return "Archive: {$year}/{$month}";
11});1// Only numbers
2Route::get('/users/{id}', function (string $id) {
3 return "User ID: {$id}";
4})->where('id', '[0-9]+');
5
6// Only letters
7Route::get('/users/{name}', function (string $name) {
8 return "User: {$name}";
9})->where('name', '[A-Za-z]+');
10
11// Multiple constraints
12Route::get('/posts/{id}/{slug}', function (string $id, string $slug) {
13 return "Post {$id}: {$slug}";
14})->where(['id' => '[0-9]+', 'slug' => '[a-z-]+']);
15
16// Predefined patterns
17Route::get('/users/{id}', function (string $id) {
18 return "User ID: {$id}";
19})->whereNumber('id');
20
21Route::get('/users/{name}', function (string $name) {
22 return "User: {$name}";
23})->whereAlpha('name');
24
25Route::get('/search/{query}', function (string $query) {
26 return "Searching for: {$query}";
27})->whereAlphaNumeric('query');
28
29Route::get('/uuid/{uuid}', function (string $uuid) {
30 return "UUID: {$uuid}";
31})->whereUuid('uuid');1// In App\Providers\AppServiceProvider::boot()
2use Illuminate\Support\Facades\Route;
3
4Route::pattern('id', '[0-9]+');
5Route::pattern('slug', '[a-z0-9-]+');
6
7// Now all {id} and {slug} parameters are constrained
8Route::get('/users/{id}', function (string $id) {
9 // {id} must be numeric
10});
11
12Route::get('/posts/{slug}', function (string $slug) {
13 // {slug} must be alphanumeric with dashes
14});Named routes allow convenient URL generation and redirects.
1// Using name() method
2Route::get('/users/profile', function () {
3 return view('users.profile');
4})->name('profile');
5
6// With controller
7Route::get('/users/{id}', [UserController::class, 'show'])->name('users.show');
8
9// Resource routes have automatic names
10Route::resource('posts', PostController::class);
11// Generates: posts.index, posts.create, posts.store, posts.show, posts.edit, posts.update, posts.destroy1// Generate URL
2$url = route('profile');
3// /users/profile
4
5$url = route('users.show', ['id' => 1]);
6// /users/1
7
8$url = route('users.show', ['id' => 1, 'preview' => true]);
9// /users/1?preview=true
10
11// In Blade templates
12<a href="{{ route('profile') }}">Profile</a>
13<a href="{{ route('users.show', $user->id) }}">View User</a>
14
15// Redirect to named route
16return redirect()->route('profile');
17return redirect()->route('users.show', ['id' => 1]);Controllers organize request handling logic into classes.
1# Basic controller
2php artisan make:controller UserController
3
4# Resource controller (CRUD methods)
5php artisan make:controller UserController --resource
6
7# API controller (no create/edit)
8php artisan make:controller API/UserController --api
9
10# Invokable controller (single action)
11php artisan make:controller ShowProfile --invokable1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\User;
6use Illuminate\Http\Request;
7
8class UserController extends Controller
9{
10 public function index()
11 {
12 $users = User::all();
13 return view('users.index', compact('users'));
14 }
15
16 public function show(string $id)
17 {
18 $user = User::findOrFail($id);
19 return view('users.show', compact('user'));
20 }
21
22 public function store(Request $request)
23 {
24 $validated = $request->validate([
25 'name' => 'required|string|max:255',
26 'email' => 'required|email|unique:users',
27 ]);
28
29 $user = User::create($validated);
30
31 return redirect()->route('users.show', $user->id)
32 ->with('success', 'User created successfully!');
33 }
34}1use App\Http\Controllers\UserController;
2
3// Single action
4Route::get('/users', [UserController::class, 'index']);
5Route::get('/users/{id}', [UserController::class, 'show']);
6Route::post('/users', [UserController::class, 'store']);
7
8// With route names
9Route::get('/users', [UserController::class, 'index'])->name('users.index');
10Route::get('/users/{id}', [UserController::class, 'show'])->name('users.show');Resource controllers handle CRUD operations with conventional route names.
1php artisan make:controller PostController --resource1<?php
2
3namespace App\Http\Controllers;
4
5use App\Models\Post;
6use Illuminate\Http\Request;
7
8class PostController extends Controller
9{
10 // GET /posts
11 public function index()
12 {
13 $posts = Post::latest()->paginate(10);
14 return view('posts.index', compact('posts'));
15 }
16
17 // GET /posts/create
18 public function create()
19 {
20 return view('posts.create');
21 }
22
23 // POST /posts
24 public function store(Request $request)
25 {
26 $validated = $request->validate([
27 'title' => 'required|max:255',
28 'content' => 'required',
29 ]);
30
31 $post = Post::create($validated);
32
33 return redirect()->route('posts.show', $post)
34 ->with('success', 'Post created!');
35 }
36
37 // GET /posts/{post}
38 public function show(Post $post)
39 {
40 return view('posts.show', compact('post'));
41 }
42
43 // GET /posts/{post}/edit
44 public function edit(Post $post)
45 {
46 return view('posts.edit', compact('post'));
47 }
48
49 // PUT/PATCH /posts/{post}
50 public function update(Request $request, Post $post)
51 {
52 $validated = $request->validate([
53 'title' => 'required|max:255',
54 'content' => 'required',
55 ]);
56
57 $post->update($validated);
58
59 return redirect()->route('posts.show', $post)
60 ->with('success', 'Post updated!');
61 }
62
63 // DELETE /posts/{post}
64 public function destroy(Post $post)
65 {
66 $post->delete();
67
68 return redirect()->route('posts.index')
69 ->with('success', 'Post deleted!');
70 }
71}1use App\Http\Controllers\PostController;
2
3// All CRUD routes at once
4Route::resource('posts', PostController::class);
5
6// Generates these routes:
7/*
8GET /posts posts.index PostController@index
9GET /posts/create posts.create PostController@create
10POST /posts posts.store PostController@store
11GET /posts/{post} posts.show PostController@show
12GET /posts/{post}/edit posts.edit PostController@edit
13PUT /posts/{post} posts.update PostController@update
14DELETE /posts/{post} posts.destroy PostController@destroy
15*/
16
17// Limit routes
18Route::resource('posts', PostController::class)->only(['index', 'show']);
19Route::resource('posts', PostController::class)->except(['destroy']);
20
21// Nested resources
22Route::resource('posts.comments', CommentController::class);
23// /posts/{post}/comments
24// /posts/{post}/comments/{comment}API resources exclude create and edit methods (no HTML forms needed).
1// API resource controller
2php artisan make:controller API/PostController --api
3
4// API resource routes
5Route::apiResource('posts', API\PostController::class);
6
7// Generates:
8/*
9GET /posts posts.index
10POST /posts posts.store
11GET /posts/{post} posts.show
12PUT /posts/{post} posts.update
13DELETE /posts/{post} posts.destroy
14*/Group routes to apply shared attributes.
1// Apply middleware to group
2Route::middleware(['auth'])->group(function () {
3 Route::get('/dashboard', [DashboardController::class, 'index']);
4 Route::get('/profile', [ProfileController::class, 'show']);
5 Route::resource('posts', PostController::class);
6});
7
8// Multiple middlewares
9Route::middleware(['auth', 'verified'])->group(function () {
10 Route::get('/premium', [PremiumController::class, 'index']);
11});1// Add prefix to all routes in group
2Route::prefix('admin')->group(function () {
3 Route::get('/dashboard', [AdminController::class, 'dashboard']);
4 // /admin/dashboard
5
6 Route::get('/users', [AdminController::class, 'users']);
7 // /admin/users
8});
9
10// Combine prefix and middleware
11Route::prefix('admin')->middleware(['auth', 'admin'])->group(function () {
12 Route::get('/dashboard', [AdminController::class, 'dashboard']);
13 Route::resource('users', AdminUserController::class);
14});1// Add name prefix
2Route::name('admin.')->group(function () {
3 Route::get('/dashboard', [AdminController::class, 'dashboard'])
4 ->name('dashboard'); // Route name: admin.dashboard
5
6 Route::resource('users', AdminUserController::class);
7 // Route names: admin.users.index, admin.users.show, etc.
8});1Route::middleware(['auth'])
2 ->prefix('admin')
3 ->name('admin.')
4 ->group(function () {
5 Route::get('/dashboard', [AdminController::class, 'dashboard'])
6 ->name('dashboard');
7 Route::resource('users', AdminUserController::class);
8 Route::resource('posts', AdminPostController::class);
9 });
10
11// Generates routes like:
12// GET /admin/dashboard (admin.dashboard)
13// GET /admin/users (admin.users.index)
14// GET /admin/posts/{post} (admin.posts.show)Automatically inject model instances based on route parameters.
1use App\Models\Post;
2
3// Laravel automatically finds Post by ID
4Route::get('/posts/{post}', function (Post $post) {
5 return view('posts.show', compact('post'));
6});
7
8// In controller
9public function show(Post $post)
10{
11 // $post is automatically loaded
12 return view('posts.show', compact('post'));
13}
14
15// If not found, automatically returns 4041// Use slug instead of ID
2Route::get('/posts/{post:slug}', function (Post $post) {
3 return view('posts.show', compact('post'));
4});
5
6// Or define in model
7public function getRouteKeyName()
8{
9 return 'slug';
10}
11
12// Then all route model binding uses slug
13Route::get('/posts/{post}', function (Post $post) {
14 // Now uses slug
15});1// In AppServiceProvider::boot()
2use App\Models\User;
3use Illuminate\Support\Facades\Route;
4
5Route::bind('user', function (string $value) {
6 return User::where('username', $value)->firstOrFail();
7});
8
9// Now {user} binds to username field
10Route::get('/users/{user}', function (User $user) {
11 // $user loaded by username
12});1// Nested resource with scoping
2Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
3 return view('posts.show', compact('user', 'post'));
4})->scopeBindings();
5
6// Ensures post belongs to user
7// Returns 404 if post doesn't belong to userDifferent ways to return responses from routes and controllers.
1// Return view
2Route::get('/about', function () {
3 return view('about');
4});
5
6// With data
7Route::get('/users/{id}', function (string $id) {
8 $user = User::find($id);
9 return view('users.show', ['user' => $user]);
10});
11
12// Using compact
13return view('users.show', compact('user'));
14
15// Using with
16return view('users.show')->with('user', $user);1// Return JSON
2Route::get('/api/users', function () {
3 $users = User::all();
4 return response()->json($users);
5});
6
7// With status code
8return response()->json(['error' => 'Not found'], 404);
9
10// Custom headers
11return response()->json($data)
12 ->header('X-Custom-Header', 'Value')
13 ->setStatusCode(201);1// Basic redirect
2return redirect('/home');
3
4// To named route
5return redirect()->route('dashboard');
6
7// With route parameters
8return redirect()->route('users.show', ['id' => 1]);
9
10// With flash data
11return redirect()->route('dashboard')
12 ->with('success', 'Welcome back!');
13
14// Back to previous page
15return redirect()->back();
16
17// With input
18return redirect()->back()->withInput();1// File download
2return response()->download($pathToFile);
3
4// With custom name
5return response()->download($pathToFile, 'custom-name.pdf');
6
7// Stream file
8return response()->stream(function () {
9 echo 'File content...';
10}, 200, [
11 'Content-Type' => 'text/plain',
12]);Middleware filters HTTP requests entering your application.
1// Single middleware
2Route::get('/dashboard', [DashboardController::class, 'index'])
3 ->middleware('auth');
4
5// Multiple middlewares
6Route::get('/premium', [PremiumController::class, 'index'])
7 ->middleware(['auth', 'verified', 'subscribed']);
8
9// To controller
10Route::middleware(['auth'])->group(function () {
11 Route::get('/dashboard', [DashboardController::class, 'index']);
12 Route::resource('posts', PostController::class);
13});1// Authentication
2->middleware('auth')
3
4// Guest only (not authenticated)
5->middleware('guest')
6
7// Email verification
8->middleware('verified')
9
10// Throttle (rate limiting)
11->middleware('throttle:60,1') // 60 requests per minute
12
13// CSRF protection (automatic for web routes)
14->middleware('csrf')
15
16// Password confirmation
17->middleware('password.confirm')Laravel's routing system provides:
Next lesson: Blade Templating Engine!
Your progress is saved in this browser, on this device only.