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:
- A GET route for
/services that returns a view called 'services'
- A GET route for
/pricing that returns the string "Pricing Page"
- A GET route for
/blog that returns a view with data: ['posts' => 25]
- Test all routes by visiting them in your browser
Practice Exercise 2: Route Parameters
Create these routes with parameters:
- A route
/products/{id} where id must be numeric
- A route
/categories/{name?} with optional parameter (default: 'all')
- A route
/search/{term}/{category?} with one required and one optional parameter
- Add appropriate constraints to ensure data types
Practice Exercise 3: Named Routes & Groups
Practice with route groups:
- Create a route group with prefix 'admin' containing 3 routes (dashboard, users, settings)
- Give each route a name with 'admin.' prefix
- Generate URLs to these named routes using the
route() helper
- 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!