Routing System
Learn how URLs map to controllers
Lesson 2: Routing System
The router is the traffic controller of your application, directing requests to the right destination.
What is Routing?
Routing maps incoming URLs to controller files:
URL: /posts/123
↓
Route: /posts/\{id\}
↓
Controller: posts/show.php
↓
Result: Display post #123Defining Routes
Routes are defined in routes/routes.php:
$router = new Router();
// Basic routes
$router->get('/', 'welcome.php');
$router->get('/about', 'about.php');
// Routes with parameters
$router->get('/posts/\{id\}', 'posts/show.php');
$router->get('/users/\{id\}/posts', 'users/posts.php');
// Different HTTP methods
$router->post('/posts', 'posts/store.php');
$router->delete('/posts/\{id\}', 'posts/destroy.php');Route Parameters
Extract dynamic values from URLs:
// Route definition
$router->get('/posts/\{id\}', 'posts/show.php');
// In controller (posts/show.php)
$id = $_GET['id']; // Automatically injected by router
$post = $db->query('SELECT * FROM posts WHERE id = :id', [
'id' => $id
])->findOrFail();Multiple Parameters
$router->get('/users/\{userId\}/posts/\{postId\}', 'users/posts/show.php');
// Accessing in controller
$userId = $_GET['userId'];
$postId = $_GET['postId'];Route Order Matters! ⚠️
This is the most common routing bug:
// ❌ WRONG - Generic route first
$router->get('/posts/\{id\}', 'posts/show.php');
$router->get('/posts/create', 'posts/create.php'); // Never matches!
// ✅ CORRECT - Specific route first
$router->get('/posts/create', 'posts/create.php');
$router->get('/posts/\{id\}', 'posts/show.php');Why? The router checks routes in order. /posts/create matches /posts/\{id\} with id = "create".
Common Bug: Always put specific routes before generic routes with parameters!
How Route Matching Works
The router uses regex to match patterns:
// Pattern: /posts/\{id\}
// Regex: #^/posts/([^/]+)$#
// Matches:
✅ /posts/123
✅ /posts/abc
✅ /posts/hello-world
// Doesn't match:
❌ /posts
❌ /posts/123/edit
❌ /post/123HTTP Methods
Different methods for different actions:
// GET - Retrieve data
$router->get('/posts', 'posts/index.php');
$router->get('/posts/\{id\}', 'posts/show.php');
// POST - Create data
$router->post('/posts', 'posts/store.php');
// PATCH - Update data
$router->patch('/posts/\{id\}', 'posts/update.php');
// DELETE - Delete data
$router->delete('/posts/\{id\}', 'posts/destroy.php');Method Spoofing
HTML forms only support GET and POST, so we use method spoofing:
<form method="POST" action="/posts/123">
<input type="hidden" name="_method" value="DELETE">
<button type="submit">Delete</button>
</form>// Router checks for _method
$method = $_POST['_method'] ?? $_SERVER['REQUEST_METHOD'];Middleware on Routes
Protect routes with middleware:
// Require authentication
$router->get('/dashboard', 'dashboard.php')->only('auth');
// Multiple middleware
$router->post('/posts', 'posts/store.php')->only(['auth', 'csrf']);
// Guest only (not authenticated)
$router->get('/login', 'auth/login.php')->only('guest');RESTful Routing
Follow REST conventions for resource routes:
| Method | URI | Controller | Action |
|---|---|---|---|
| GET | /posts | posts/index.php | List all posts |
| GET | /posts/create | posts/create.php | Show create form |
| POST | /posts | posts/store.php | Store new post |
| GET | /posts/{id} | posts/show.php | Show single post |
| GET | /posts/{id}/edit | posts/edit.php | Show edit form |
| PATCH | /posts/{id} | posts/update.php | Update post |
| DELETE | /posts/{id} | posts/destroy.php | Delete post |
Remember: /posts/create and /posts/\{id\}/edit must come before /posts/\{id\}!
Debugging Routes
View All Routes
// In routes/routes.php
dd($router->routes);Output:
[
'GET' => [
'/' => 'welcome.php',
'/posts/\{id\}' => 'posts/show.php',
],
'POST' => [
'/posts' => 'posts/store.php',
]
]Test Route Matching
// Add to Router::route() method
dd([
'uri' => $uri,
'method' => $method,
'matched' => $matchedRoute ?? 'none'
]);Common Route Issues
404 on valid route:
- Check route is registered
- Verify HTTP method matches
- Check route order
Wrong controller executed:
- Check route order (specific before generic)
- Verify route pattern is correct
Parameters not available:
- Check
$_GETin controller - Verify route has parameter placeholder
Practice Examples
Blog Routes
// List all posts
$router->get('/posts', 'posts/index.php');
// Create post form
$router->get('/posts/create', 'posts/create.php')->only('auth');
// Store new post
$router->post('/posts', 'posts/store.php')->only(['auth', 'csrf']);
// Show single post
$router->get('/posts/\{id\}', 'posts/show.php');
// Edit post form
$router->get('/posts/\{id\}/edit', 'posts/edit.php')->only('auth');
// Update post
$router->patch('/posts/\{id\}', 'posts/update.php')->only(['auth', 'csrf']);
// Delete post
$router->delete('/posts/\{id\}', 'posts/destroy.php')->only(['auth', 'csrf']);User Profile Routes
$router->get('/users/\{id\}', 'users/show.php');
$router->get('/users/\{id\}/posts', 'users/posts.php');
$router->get('/users/\{id\}/edit', 'users/edit.php')->only('auth');Ready for the Challenge?
Now that you understand routing, try fixing the broken routing challenge!