Laravel Routing¶
Routes map HTTP requests to controller actions. Defined in routes/web.php (session/CSRF) and routes/api.php (stateless). Routes support parameters, named references, grouping, and resource CRUD conventions. Controllers organize route handlers by domain. Closely tied to [[laravel-middleware]] for access control and [[laravel-blade-templates]] for view rendering.
Key Facts¶
routes/web.php- web routes with session, CSRF protection, cookie middlewareroutes/api.php- API routes, stateless,/apiprefix by default- Route methods:
Route::get(),::post(),::put(),::patch(),::delete(),::any(),::match() - Named routes:
->name('posts.index')- used inroute('posts.index')helper for URL generation - Route parameters:
{id}required,{id?}optional - Route model binding: type-hint Eloquent model in controller to auto-resolve by ID
- Resource routes:
Route::resource('posts', PostController::class)generates 7 CRUD routes - Route groups: share middleware, prefix, namespace across multiple routes
php artisan route:listshows all registered routes
Patterns¶
Basic routes¶
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
// Closure route (simple pages)
Route::get('/', function () {
return view('welcome');
});
// Controller route
Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('posts.destroy');
Resource routes (CRUD)¶
<?php
// Generates all 7 RESTful routes
Route::resource('posts', PostController::class);
// Equivalent to:
// 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 /posts/{post} -> update posts.update
// DELETE /posts/{post} -> destroy posts.destroy
// Partial resource
Route::resource('posts', PostController::class)->only(['index', 'show']);
Route::resource('posts', PostController::class)->except(['destroy']);
// API resource (no create/edit - those are form views)
Route::apiResource('posts', PostController::class);
Route groups¶
<?php
// Admin routes with prefix, middleware, and controller namespace
Route::prefix('admin')
->middleware(['auth', 'admin'])
->name('admin.')
->group(function () {
Route::get('/', [Admin\MainController::class, 'index'])->name('main.index');
Route::resource('posts', Admin\PostController::class);
Route::resource('categories', Admin\CategoryController::class);
});
// Generates: admin/posts, admin/categories, etc.
// Names: admin.posts.index, admin.categories.index, etc.
Route model binding¶
<?php
// Implicit binding - Laravel resolves {post} to Post model by ID
Route::get('/posts/{post}', function (Post $post) {
return view('posts.show', compact('post'));
});
// Automatically returns 404 if post not found
// Custom column binding
Route::get('/posts/{post:slug}', function (Post $post) {
return $post; // resolved by slug column
});
Controllers¶
<?php
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::latest()->paginate(15);
return view('posts.index', compact('posts'));
}
public function show(Post $post)
{
return view('posts.show', compact('post'));
}
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
Post::create($validated);
return redirect()->route('posts.index')
->with('success', 'Post created.');
}
public function destroy(Post $post)
{
$post->delete();
return redirect()->route('posts.index')
->with('success', 'Post deleted.');
}
}
URL generation¶
<?php
// In Blade templates
<a href="{{ route('posts.show', $post) }}">View</a>
<a href="{{ route('posts.edit', ['post' => $post->id]) }}">Edit</a>
// In controllers
return redirect()->route('admin.main.index');
return redirect()->back()->with('error', 'Not found');
// Asset URLs
<link href="{{ asset('assets/admin/css/adminlte.css') }}" rel="stylesheet">
Gotchas¶
| Symptom | Cause | Fix |
|---|---|---|
Route [name] not defined | Typo in route name or route not registered | Run php artisan route:list to verify |
Wrong route matches (e.g., posts/create hits posts/{post}) | Route order matters - specific routes before parameterized | Place posts/create before posts/{post} |
| 405 Method Not Allowed | Form uses GET but route expects POST, or missing @method('PUT') | Add @method('PUT') or @method('DELETE') to form |
| Route model binding returns 404 | Model not found by ID (or soft deleted) | Check database or use withTrashed() in route binding |
| CSRF token mismatch on POST | Missing @csrf directive in form | Add @csrf inside <form> tag |
| Route cache stale after changes | Route cache was generated before new routes | Run php artisan route:clear |
See Also¶
- [[laravel-middleware]] - protecting routes with auth, admin checks
- [[laravel-blade-templates]] - rendering views from controllers
- [[laravel-validation]] - validating request data
- https://laravel.com/docs/11.x/routing
- https://laravel.com/docs/11.x/controllers