Laravel Framework

Routing Fundamentals

18 min Lesson 3 of 45

Introduction to Routing

Routing is the mechanism that maps HTTP requests to specific code that handles those requests. In Laravel, routes are defined in files located in the routes directory.

What is a Route? A route defines how your application responds to a specific HTTP request (GET, POST, PUT, DELETE, etc.) to a specific URI. Think of routes as the entry points to your application.

Route Files

Laravel provides several route files for different purposes:

  • routes/web.php - For web interface routes (includes session, CSRF protection, cookie encryption)
  • routes/api.php - For API routes (stateless, no sessions)
  • routes/console.php - For console commands
  • routes/channels.php - For broadcast channels

For this lesson, we'll focus on routes/web.php.

Basic Routes

Simple GET Route

The most basic Laravel route accepts a URI and a closure:

// routes/web.php use Illuminate\Support\Facades\Route; Route::get('/', function () { return 'Welcome to Laravel!'; }); Route::get('/about', function () { return 'About Page'; }); Route::get('/contact', function () { return 'Contact Us'; });

Available Router Methods

Laravel provides methods for all common HTTP verbs:

Route::get($uri, $callback); // GET request Route::post($uri, $callback); // POST request Route::put($uri, $callback); // PUT request Route::patch($uri, $callback); // PATCH request Route::delete($uri, $callback); // DELETE request Route::options($uri, $callback); // OPTIONS request

Multiple HTTP Verbs

Sometimes you need to register a route that responds to multiple HTTP verbs:

// Respond to both GET and POST Route::match(['get', 'post'], '/form', function () { return 'This route handles GET and POST'; }); // Respond to all HTTP verbs Route::any('/any-method', function () { return 'This route handles any HTTP verb'; });

Returning Views

Instead of returning strings, you typically return views:

Route::get('/home', function () { return view('home'); }); // Passing data to views Route::get('/welcome', function () { return view('welcome', [ 'name' => 'John Doe', 'role' => 'Developer' ]); }); // Using compact() helper Route::get('/profile', function () { $name = 'Jane Smith'; $email = 'jane@example.com'; return view('profile', compact('name', 'email')); });
View Shortcut: For routes that only return a view without logic, you can use the Route::view() method: Route::view('/about', 'about', ['title' => 'About Us']);

Route Parameters

Required Parameters

You can capture segments of the URI using route parameters:

// Single parameter Route::get('/users/{id}', function ($id) { return "User ID: " . $id; }); // Multiple parameters Route::get('/posts/{postId}/comments/{commentId}', function ($postId, $commentId) { return "Post: $postId, Comment: $commentId"; }); // Using parameters with views Route::get('/articles/{id}', function ($id) { return view('articles.show', ['articleId' => $id]); });

Optional Parameters

Make parameters optional by placing a ? after the parameter name and providing a default value:

Route::get('/users/{name?}', function ($name = 'Guest') { return "Hello, " . $name; }); // Multiple optional parameters Route::get('/posts/{category?}/{year?}', function ($category = 'all', $year = null) { if ($year) { return "Posts in $category from $year"; } return "All posts in $category"; });

Regular Expression Constraints

Constrain route parameters using the where method:

// ID must be numeric Route::get('/users/{id}', function ($id) { return "User: $id"; })->where('id', '[0-9]+'); // Name must be alphabetic Route::get('/users/{name}', function ($name) { return "User: $name"; })->where('name', '[a-zA-Z]+'); // Multiple constraints Route::get('/posts/{id}/{slug}', function ($id, $slug) { return "Post $id: $slug"; })->where(['id' => '[0-9]+', 'slug' => '[a-z-]+']); // Using helper methods Route::get('/users/{id}', function ($id) { return "User: $id"; })->whereNumber('id'); Route::get('/users/{name}', function ($name) { return "User: $name"; })->whereAlpha('name'); Route::get('/posts/{slug}', function ($slug) { return "Post: $slug"; })->whereAlphaNumeric('slug'); Route::get('/search/{term}', function ($term) { return "Searching: $term"; })->whereIn('term', ['users', 'posts', 'comments']);
Important: If a route parameter doesn't match its constraint, Laravel will return a 404 Not Found response automatically.

Named Routes

Named routes allow you to generate URLs or redirects for specific routes without hardcoding the URI:

Defining Named Routes

Route::get('/dashboard', function () { return view('dashboard'); })->name('dashboard'); Route::get('/user/profile', function () { return view('profile'); })->name('profile'); Route::get('/posts/{id}', function ($id) { return view('posts.show', compact('id')); })->name('posts.show');

Generating URLs to Named Routes

// In controllers or views $url = route('dashboard'); // Returns: http://yourapp.com/dashboard $url = route('posts.show', ['id' => 5]); // Returns: http://yourapp.com/posts/5 // With query parameters $url = route('posts.show', ['id' => 5, 'highlight' => 'comment-42']); // Returns: http://yourapp.com/posts/5?highlight=comment-42

Redirecting to Named Routes

// Redirect to named route return redirect()->route('dashboard'); // With parameters return redirect()->route('posts.show', ['id' => 5]); // With flash data return redirect() ->route('dashboard') ->with('status', 'Profile updated!');

Using Named Routes in Blade Templates

<!-- Simple link --> <a href="{{ route('dashboard') }}">Dashboard</a> <!-- With parameters --> <a href="{{ route('posts.show', ['id' => $post->id]) }}"> View Post </a> <!-- Form action --> <form action="{{ route('posts.store') }}" method="POST"> @csrf <!-- form fields --> </form>
Best Practice: Always use named routes instead of hardcoding URLs. This makes your application more maintainable and allows you to change URIs without breaking links throughout your application.

Route Groups

Route groups allow you to share attributes across multiple routes:

Prefix Groups

// All routes will have /admin prefix Route::prefix('admin')->group(function () { Route::get('/users', function () { // Matches: /admin/users }); Route::get('/posts', function () { // Matches: /admin/posts }); });

Name Prefix

Route::name('admin.')->group(function () { Route::get('/admin/users', function () { // Route named: admin.users })->name('users'); Route::get('/admin/posts', function () { // Route named: admin.posts })->name('posts'); });

Middleware Groups

Route::middleware(['auth', 'verified'])->group(function () { Route::get('/dashboard', function () { // Only accessible to authenticated, verified users }); Route::get('/profile', function () { // Also requires auth and verified }); });

Combining Group Attributes

Route::prefix('admin') ->name('admin.') ->middleware('auth') ->group(function () { Route::get('/dashboard', function () { // URI: /admin/dashboard // Name: admin.dashboard // Middleware: auth })->name('dashboard'); Route::get('/users', function () { // URI: /admin/users // Name: admin.users // Middleware: auth })->name('users'); });

Route Model Binding

Route model binding provides automatic injection of model instances into your routes:

Implicit Binding

Laravel automatically resolves Eloquent models defined in routes or controllers:

use App\Models\Post; // Automatic model injection Route::get('/posts/{post}', function (Post $post) { // $post is automatically loaded based on the ID in the URL return view('posts.show', compact('post')); }); // If post with given ID doesn't exist, Laravel returns 404

Custom Key for Binding

By default, Laravel uses the id column. You can use a different column:

// In your Post model (app/Models/Post.php) public function getRouteKeyName() { return 'slug'; // Use slug instead of id } // Now this route uses slug Route::get('/posts/{post}', function (Post $post) { // Resolves by slug: /posts/my-first-post return view('posts.show', compact('post')); }); // Or specify per route Route::get('/posts/{post:slug}', function (Post $post) { return view('posts.show', compact('post')); });

Explicit Binding

Define custom resolution logic in RouteServiceProvider:

// app/Providers/RouteServiceProvider.php use App\Models\Post; use Illuminate\Support\Facades\Route; public function boot() { Route::bind('post', function ($value) { return Post::where('slug', $value) ->where('published', true) ->firstOrFail(); }); }

Resource Routes

Resource routes assign CRUD routes to a controller with a single line:

use App\Http\Controllers\PostController; // Creates 7 routes for typical CRUD operations Route::resource('posts', PostController::class);

Routes Created by Resource

GET /posts index posts.index GET /posts/create create posts.create POST /posts store posts.store GET /posts/{post} show posts.show GET /posts/{post}/edit edit posts.edit PUT/PATCH /posts/{post} update posts.update DELETE /posts/{post} destroy posts.destroy

Partial Resource Routes

// Only create specified routes Route::resource('posts', PostController::class)->only([ 'index', 'show' ]); // Create all except specified routes Route::resource('posts', PostController::class)->except([ 'create', 'store', 'destroy' ]);

API Resource Routes

API resources exclude create and edit routes (which display HTML forms):

Route::apiResource('posts', PostController::class); // Creates: index, store, show, update, destroy

Nested Resources

// Posts have many comments Route::resource('posts.comments', CommentController::class); // Creates routes like: // GET /posts/{post}/comments // POST /posts/{post}/comments // GET /posts/{post}/comments/{comment} // etc.

Practice Exercise 1: Basic Routes

Create the following routes in your routes/web.php:

  1. A GET route for /services that returns a view called 'services'
  2. A GET route for /pricing that returns the string "Pricing Page"
  3. A GET route for /blog that returns a view with data: ['posts' => 25]
  4. Test all routes by visiting them in your browser

Practice Exercise 2: Route Parameters

Create these routes with parameters:

  1. A route /products/{id} where id must be numeric
  2. A route /categories/{name?} with optional parameter (default: 'all')
  3. A route /search/{term}/{category?} with one required and one optional parameter
  4. Add appropriate constraints to ensure data types

Practice Exercise 3: Named Routes & Groups

Practice with route groups:

  1. Create a route group with prefix 'admin' containing 3 routes (dashboard, users, settings)
  2. Give each route a name with 'admin.' prefix
  3. Generate URLs to these named routes using the route() helper
  4. Create a Blade template with links using named routes

Viewing All Routes

Laravel provides a helpful command to view all registered routes:

# View all routes php artisan route:list # View routes with middleware php artisan route:list -v # Filter by name php artisan route:list --name=admin # Filter by URI php artisan route:list --path=posts # Show routes in compact format php artisan route:list --compact

Route Caching

For production performance, cache your routes:

# Cache routes (faster route registration) php artisan route:cache # Clear route cache php artisan route:clear
Caching Limitation: Route caching doesn't work with closure-based routes. If you use route caching, you must convert all closure routes to controller methods.

Summary

In this lesson, you've learned:

  • How to define basic routes with different HTTP verbs
  • Working with route parameters (required, optional, with constraints)
  • Creating and using named routes for maintainable URLs
  • Organizing routes with route groups and prefixes
  • Implementing route model binding for automatic model injection
  • Using resource routes for RESTful CRUD operations
  • Viewing and caching routes for better performance

Routing is the foundation of your Laravel application. In the next lesson, we'll learn how to create controllers to handle the logic for these routes!