Chapter 2 of 9

Laravel 13 tutorial #2: routing, parameters and model binding

verified on 7 September 2026 · 5 min

Quick answer

Routes are declared in routes/web.php with Route::get('/chemin', [MonController::class, 'methode']). Name them with ->name() to build your links with route('nom') rather than hard-coding them. The string syntax 'Controller@methode' has not worked since Laravel 8.

By the end of this chapter, you will know how to point an address at a controller, read a URL parameter, name a route and let Laravel load a model for you.

A route ties an address to some code. It is the entry point of every request: Laravel reads the requested URL, looks for the route that matches and runs whatever it points at. This chapter goes through the four shapes that link can take.

The simplest route

Every route of the public site is declared in routes/web.php. Here is what the file holds right after installation:

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

That route says: when someone requests / with GET, return the welcome view, that is the resources/views/welcome.blade.php file.

Route::get() takes two arguments: the address, and what to do with it. That second argument can take several forms.

Returning a string

routes/web.php
Route::get('/bonjour', function () {
    return 'Bonjour !';
});

Visit http://127.0.0.1:8000/bonjour: the browser prints the raw text. Handy to confirm a route is reached, rarely useful beyond that.

Returning a view

To check that welcome really is the view shown on the home page, open resources/views/welcome.blade.php and add a visible line anywhere inside the <body>:

resources/views/welcome.blade.php
<h1>Bonjour tout le monde !</h1>

Reload the home page: the heading shows up. You have just changed a view without touching the route.

Calling a controller

In a real project the logic does not live in the routes file but in a controller. The route then only names a class and a method:

routes/web.php
use App\Http\Controllers\PostController;

Route::get('/article', [PostController::class, 'show']);

The [PostController::class, 'show'] array means “call the show method of the PostController class”. The controller does not exist yet: the next chapter creates it. Note the use at the top of the file, which imports the class. Without it, PHP looks for PostController in the global namespace and never finds it.

The string syntax is gone

Plenty of tutorials written for Laravel 7 or earlier use Route::get('/article', 'PostController@show'). The automatic namespace prefix that made this form work was removed in Laravel 8. On Laravel 13 it fails with this message:

code
Illuminate\Contracts\Container\BindingResolutionException
Target class [PostController] does not exist.

Laravel looks for the class at the root of the namespace instead of App\Http\Controllers. Always use the array form.

URL parameters

A blog needs variable addresses: /article/mon-premier-article, /article/un-autre. The part that changes becomes a parameter, written between braces:

routes/web.php
Route::get('/article/{slug}', function (string $slug) {
    return "Vous avez demandé l'article : {$slug}";
});

Laravel captures everything after /article/ and passes it to the function under the name $slug. The parameter name in the URL and the argument name have to match.

You can constrain the shape you expect. Here the route only answers if the identifier is numeric:

routes/web.php
Route::get('/article/{id}', function (int $id) {
    return "Article numéro {$id}";
})->whereNumber('id');

A URL such as /article/bonjour then returns a 404 instead of reaching your code with an unexpected value.

Named routes

Hard-coding URLs in your templates is fragile: the day you rename /article/ to /blog/, you have to track down every link. Naming the route settles it:

routes/web.php
Route::get('/article/{slug}', [PostController::class, 'show'])->name('post');

The link is then built from the name, never from the address:

php
route('post', ['slug' => 'mon-premier-article'])
// http://127.0.0.1:8000/article/mon-premier-article

In a Blade view, it reads:

markup
<a href="http://%20route('post',%20$post)%20">{{ $post->title }}</a>

Change the address in web.php and every link follows. That is how this tutorial builds all its links from chapter 7 onwards.

Route model binding

Laravel can go one step further. If the parameter carries the name of a model, the framework fetches the record itself:

routes/web.php
use App\Models\Post;

Route::get('/article/{post}', function (Post $post) {
    return $post->title;
});

Laravel reads the URL segment, queries the posts table and returns a 404 on its own if nothing matches. You get the object straight away, without writing the query. By default the lookup uses the id column; chapter 6 shows how to make it use the slug instead.

Route model binding removes half of the code a controller usually carries. It is used everywhere in the rest of the tutorial.

The other HTTP verbs

Route::get() answers read requests. For forms and APIs, Laravel exposes the other methods:

php
Route::post('/article', [PostController::class, 'store']);
Route::put('/article/{post}', [PostController::class, 'update']);
Route::delete('/article/{post}', [PostController::class, 'destroy']);

This tutorial only uses get: writes go through the admin panel installed in chapter 4.

Listing every route

This command lists what your application knows how to serve:

bash
php artisan route:list --except-vendor

On the finished project of this tutorial, once the chapter 4 admin panel is installed, it also lists the back-office routes: they are declared by classes in your app/Filament folder, not by the package itself. To see only the site pages, add --except-path=admin:

bash
php artisan route:list --except-vendor --except-path=admin
code
  GET|HEAD       / .................................... home › IndexController
  GET|HEAD       article/{post} ................... post › PostController@show
  GET|HEAD       categorie/{category} .......... category › CategoryController
  GET|HEAD       etiquette/{tag} ......................... tag › TagController
  GET|HEAD       recherche .................... search › PostController@search

The --except-vendor option hides routes added by installed packages, --except-path=admin leaves out those of the admin panel, which really are yours. It is the first command to run when an address returns a 404 although you thought you had declared it.

The next chapter creates the controllers and the views these routes point at.

Common errors

Target class [MonController] does not exist The 'MonController@methode' syntax has not been supported since Laravel 8: Laravel looks for the class at the root of the namespace. Use the [MonController::class, 'methode'] form with the matching use statement.
A fixed route swallowed by a route with a parameter Laravel stops at the first route that matches. Declare /recherche before /{slug}, otherwise the second one intercepts the first.
A declared route returns 404 anyway Run php artisan route:list --except-vendor to see what the application really knows how to serve.
Newsletter

New tests, tutorials and projects, by e-mail.

Reproducible tests, versioned code, dated results. Never any spam.