Routing is one of the most important concepts in Laravel, It is used to bind URLs to their appropriate controller actions.
We discussed routing briefly in earlier articles in this series, however we need to have a deeper look on this important topic.
Default Route files
Laravel routes are defined inside the /routes folder. When you create a new project, the default route file /route/web.php is created automatically with it.
Web.php file has the routes that handles your web site, and web middleware’s that provide features like sessions and CSRF protection.
You’ll find other files created in the routes folder, including api.php that handles API middleware, which will be very essential in case your site also works as a backend for a mobile application for example.
You can also create your own custom route file, or even rename the default ones, this can be done in the App\Providers\RouteServiceProvider.php file.
Basic Routing
To add a very basic route to your application, all you need is to define the URL and a closure to handle the request.
Route::get('/welcome', function () {
return 'Welcome to my App';
});
Let’s analyze this simple route. We use the Route class to define all our routes. In the above example, the request is getting information from the backend, so we use the method get().
There are other methods that can be used like post(), delete(), put() and others, which we will explain later. In the get() method we defined two things, the actual route itself, which is /welcome here, and what to do when a user requests this route. In this example the action is defined by a closure which returns only the sentence ‘Welcome to my App’.
We can upgrade this route to a more practical one, instead of showing the response text directly in the route file, we can put that response in a view, and point the route closure to that view as follows:
Route::get('/welcome', function () {
return view('welcome');
});
This is better, we now separated the content of the response from the route, if you need to change the response, you don’t need to touch your route file. And that response can be as big as you need, with HTML and Java Script.
Now let’s give our route another upgrade. The above example can be mainly used for static responses, but what if we want to load data from the database, and process them, and pass this data to the view?
Then we should use a controller, and this is the standard way of using routes in general.
Route::get('/welcome', [WelcomeController::class,'index']);
Here instead of defining a closure to provide the response, we defined the controller WelcomeController, and an action in this controller index(), this action will provide the response for this request.
In our example here, it will return the welcome view.
Routing Methods
Laravel routes accept all HTTP methods, including GET, POST, PATCH and DELETE.
// GET is used to retrieve data from backend
Route::get($url, $callback);
// POST is used to submit data to the backend
Route::post($url, $callback);
// PATCH is used to submit data but is usually uses to update records
Route::patch($url, $callback);
// DELETE is used for requesting record deletions
Route::delete($url, $callback);
You should use these routing methods wisely, only call the relevant method for the appropriate request.
Please note that HTML form only uses GET and POST methods, however Laravel Blade provides @method() directive to define PUT and DELETE in your forms easily.
CSRF Protection
Any HTML forms pointing to POST, PUT, PATCH, or DELETE routes that are defined in the web routes file should include a CSRF token field. Otherwise, the request will be rejected.
<form method="POST" action="/signup">
@csrf
...
</form>
Route Parameters
In many cases you might want to capture parts of the request URL, it can be a record id for example, a category name, or a report date or any other parameter.
In the most basic form, it can be defined like this:
Route::get('/post/{id}', function (Request $request, $id) {
return 'This is post number '.$id;
});
Here we passed the post id as a parameter in the url, and captured it in the closure function, then we used it in the response.
We can use the parameters in the controller actions as well:
//In the route file
Route::get('/post/{id}', [PostController::class,'show']);
//In the PostController
public function show($id)
{
$post = Post::find($id);
return view('post', compact('post'));
}
You can also capture several parameters in URLs:
Route::get('/post/{id}/comment/{cid}', function (Request $request, $id) {
return 'This is post number '.$id. ' and showing comment number '.$cid;
});
Optional Parameters
Suppose you want to make the parameter in your URL optional, that if provided you will return a specific response related to that parameter, but if not provided your code will either return a default response or do something else like prompt the user to add new data.
Laravel gives you the chance to use optional parameters. This can be done by placing a ‘?’ after the parameter name as follows:
Route::get('/post/{id?}', function (Request $request, $id = null) {
if($id != null) {
return 'This is post number '.$id;
} else {
return 'This is the first post in the blog';
}
});
Regular Expression Constrained parameters
In many cases you might want to constrain the format of your route parameters,
For example you might want to be sure it consists only of digits, or only alphabet, or be sure it follows a specific regex rule. Laravel accepts regular expressions (regex) rules to constrain the parameters using the where() method:
Route::get('/category/{name}', function ($name) {
//
})->where('name', '[A-Za-z]+'); //category name can be only have alphabet
Route::get('/post/{id}', function ($id) {
//
})->where('id', '[0-9]+'); //post id can be only have digits
Route::get('/category/{name}/post/{id}', function ($id, $name) {
//
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']); //combining several constraints
Laravel also provides convenient method to use for parameters constraining:
Route::get('/category/{name}/post/{id}', function ($id, $name) {
//
})->whereAlpha('name')->whereNumber('id');
Route::get('/user/{name}', function ($name) {
//
})->whereAlphaNumeric('name');
Route::get('/reference/{id}', function ($id) {
//
})->whereUuid('id');
Now you constrained the parameter format, but what if the user sends a request with parameters that do not match that constraint? A 404 not found HTTP response will be returned.
Setting regular expression parameter constraints for route group in Laravel
Named Routing in Laravel
Suppose you defined a route like ‘/home’ in your web.php file, and you used it in your links and forms in your frontend. Then one day you decided that you need to change the homepage URL to be ‘/main’ instead.
This means you will have to go through all the locations where you used this URL, and change it to the new one. This might get messy, and you might miss some of these locations. Which will lead to errors in your website.
Laravel has a solution to this issue, called named routes. You define a name for each of your routes, and use that name instead of the actual URL in your code.
Later if you need to change the URL, you will only need to change it in the routing file. And all the other locations in your code will point to the new URL automatically.
Named routes also have the benefit of having your code more organized and cleaner. URLs can get long for large applications, but using named routes you can give them more readable names.
So how do we define named routes? Very simple just chain the method name() to the defined route as follows:
Route::get('/main/admin/dashboard', function () {
//
})->name('dashboard');
To use named route in your code:
$url = route('dashboard');
//Or
return redirect()->route('dashboard');
//Or
return to_route('dashboard');
// in Blade
<a href="{{route('dashboard')}}">Admin Dashboard</a>
So how do we use named routes if our route has parameters?
Simple, just define the route as usual in the routing file, and then in your code pass the parameters as an array:
Route::get('/posts/{id}', function ($id) {
//
})->name('posts');
$url = route('posts', ['id' => 1]);
You can also use prefixes to your route for easier management and readability, this way you can easily recognize the routes related to a certain functionality with ease.
Route::get('/admin/dashboard', function () { ... })->name('admin.dashboard');
Route::get('/admin/reports', function () { ... })->name('admin.reports');
Route::get('/admin/finance', function () { ... })->name('admin.finance');
Route::get('/admin/logs', function () { ... })->name('admin.logs');
Route Groups
Route groups are a convenient way to share route attributes, such as prefixes, controllers and middleware across multiple routes, eliminating the need to define those attributes on each route.
There are several ways to group your routes, let’s explore them.
Controllers Route Groups
If you have multiple routes, that use the same controller, you can group these route using the controller() method as follows:
Route::controller(PostController::class)->group(function () {
Route::get('/posts/{id}', 'show');
Route::post('/posts', 'store');
Route::delete('/posts', 'destroy');
});
Grouping using Route Prefixes
Route using the same prefixes in the URL, like /admin/dashboard and /admin/reports can be also grouped using the prefix() method:
Route::prefix('admin')->group(function () {
Route::get('/dashboard', function () {
// same as "/admin/dashboard" URL
});
Route::get('/reports', function () {
// same as "/admin/reports" URL
});
Route::get('/finance', function () {
// same as "/admin/finance" URL
});
});
Grouping using Route Name Prefixes
If you use route naming prefixes like explained earlier in this article, you can group them using the name() method
Route::name('admin')->group(function () {
Route::get('/admin/dashboard', function () {
// this route will be named ‘admin.dashboard’
})->name('dashboard');
Route::get('/admin/reports', function () {
// this route will be named ‘admin.reports
})->name('dashboard');
Route::get('/admin/finance', function () {
// this route will be named ‘admin.finance
})->name('finance');
});
Middleware Route Grouping
Middleware provide a mechanism to filter and inspect HTTP requests before they are used by your application, we will discuss this concept in coming articles, but for now, you need to know that you can also group routes sharing the same middleware as follows:
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', function () {
// Uses auth middleware...
});
Route::get('/profile', function () {
// Uses auth middleware...
});
});
Resource Routes
If you use a resource controller for one of your models, you can use a resource route to point to that controller:
Route::resource('posts', PostController::class);
This resource route is equivalent to the seven standard controller actions, and also will automatically generate named routes for them as follows:
Method | URI | Action | Route Name |
GET | /posts | index | posts.index |
GET | /posts/create | create | posts.create |
POST | /posts | store | posts.store |
GET | /posts/{post} | store | posts.show |
GET | /posts/{post}/edit | edit | posts.edit |
PUT/PATCH | /posts/{post} | update | posts.update |
DELETE | /posts/{post} | destroy | posts.destroy |
Route Model Binding
If you have a route that requests a model using its id, Laravel can automatically resolve the Eloquent model in the controller if you provide the type to the variable in the controller action, here is an example:
// Define the route in the routing file
Route::get('/posts/{post}', [PostController::class, 'show']);
// Controller method definition with Type
public function show(Post $post)
{
return view('post.show', ['post' => $post]);
}
This way you don’t need to retrieve the model yourself using the find() method, Laravel will do the work for you.
Redirect Routes
Sometimes you need to redirect a route to a different URL, Laravel provides you with an easy method to handle this, you can use the redirect() method to define the old and new routes:
Route::redirect('/main', '/home');
This will make a 302 redirect from the /main route to the /home route. This is called a temporary redirect. If however you want to make a permanent redirect (301), then you to use the permanentRedirect() method instead:
Route::permanentRedirect('/main', '/home');
Listing Routes
If you need to generate a list of all the routes in your application, you can use the Artisan command route:list, which will provide you with an overview of all the routes:
php artisan route:list
Here is an example of the result of this command, as you can see it shows the route, its name, the controller and action.
GET|HEAD books .......................... books.index › BookController@index
POST books .......................... books.store › BookController@store
GET|HEAD books/create ................... books.create › BookController@create
GET|HEAD books/{book} ................... books.show › BookController@show
PUT|PATCH books/{book} ................... books.update › BookController@update
DELETE books/{book} ................... books.destroy › BookController@destroy
GET|HEAD books/{book}/edit .............. books.edit › BookController@edit
Conclusion
In this article we discussed Routing in Laravel, and how you can make your life easier with concepts like route names, resource routes and route grouping. There are still many more concepts and features of Laravel to explore, so follow us.