Route Definitions in Laravel

In a Laravel application, you will define your web routes in routes/web.php, closure-based Artisan commands in routes/console.php, and, if you choose to publish them, API routes in routes/api.php and event broadcasting channel registration in routes/channels.php. Web routes are those that will be visited by your end users; API routes are those for your API, if you have one. The routes/api.php and _routes/channels.php files are only created if you run the install:api or install:broadcast commands, respectively. For now, we’ll primarily focus on the routes in routes/web.php.
The simplest way to define a route is to match a path (e.g., /) with a closure, as seen in Example 1.
Example 1. Basic route definition
// routes/web.php
Route::get('/', function () {
return 'Hello, World!';
});
What’s a Closure?
Closures are PHP’s version of anonymous functions. A closure is a function that you can pass around as an object, assign to a variable, pass as a parameter to other functions and methods, or even serialize.
You’ve now defined that if anyone visits / (the root of your domain), Laravel’s router should run the closure defined there and return the result. Note that we return our content and don’t echo or print it.
Route Verbs
You might’ve noticed that we’ve been using Route::get() in our route definitions. This means we’re telling Laravel to only match for these routes when the HTTP request uses the GET action. But what if it’s a form POST, or maybe some JavaScript sending PUT or DELETE requests? There are a few other options for methods to call on a route definition, as illustrated in Example 2.
Example 2. Route verbs
Route::get('/', function () {
return 'Hello, World!';
});
Route::post('/', function () {
// Handle someone sending a POST request to this route
});
Route::put('/', function () {
// Handle someone sending a PUT request to this route
});
Route::delete('/', function () {
// Handle someone sending a DELETE request to this route
});
Route::any('/', function () {
// Handle any verb request to this route
});
Route::match(['get', 'post'], '/', function () {
// Handle GET or POST requests to this route
});
Route Handling
As you’ve probably guessed, passing a closure to the route definition is not the only way to teach it how to resolve a route. Closures are quick and simple, but the larger your application gets, the clumsier it becomes to put all of your routing logic in one file.
The other common option is to pass a controller FQCN (fully qualified class name) and method as a “tuple” array of strings in place of the closure, as in Example 4.
Example 4. Routes calling controller methods
use AppHttpControllersWelcomeController;
Route::get('/', [WelcomeController::class, 'index']);
This is telling Laravel to pass requests to that path to the index() method of the AppHttpControllersWelcomeController controller. This method will be passed the same parameters and treated the same way as a closure you might’ve alternatively put in its place.
Laravel’s Controller/Method Reference Syntax
Laravel has a convention for how to refer to a particular method in a given controller: [ControllerName::class, methodName], known as tuple syntax or callable array syntax. Sometimes this is just a casual communication convention, but it’s also used in real bindings, like in Example 4. The first item on the array identifies the controller and the second the method.
Laravel also supports an older “string” syntax (Route::get(‘/’, ‘WelcomeController@index’)), and this is still a common way to describe a method in written communication.
Route Parameters
If the route you’re defining has parameters — segments in the URL structure that are variable — it’s simple to define them in your route and pass them to your closure (see Example 5).
Example 5. Route parameters
Route::get('users/{id}/friends', function ($id) {
//
});
You can also make your route parameters optional by including a question mark (?) after the parameter name, as illustrated in Example 6. In this case, you should also provide a default value for the route’s corresponding variable.
Example 6. Optional route parameters
Route::get('users/{id?}', function ($id = 'fallbackId') {
//
});
And you can use regular expressions (regex) to define that a route should only match if a parameter meets particular requirements, as in Example 7.
Example 7. Regular expression route constraints
Route::get('users/{id}', function ($id) {
//
})->where('id', '[0-9]+');
Route::get('users/{username}', function ($username) {
//
})->where('username', '[A-Za-z]+');
Route::get('posts/{id}/{slug}', function ($id, $slug) {
//
})->where(['id' => '[0-9]+', 'slug' => '[A-Za-z]+']);
As you’ve probably guessed, if you visit a path that matches a route string but the regex doesn’t match the parameter, it won’t be matched. Since routes are matched top to bottom, users/abc would skip the first closure in Example 7, but it would be matched by the second closure, so it would get routed there. On the other hand, posts/abc/123 wouldn’t match any of the closures, so it would return a 404 (Not Found) error.
Laravel also offers convenience methods for common regular expression matching patterns, as you can see in Example 8.
Example 8. Regular expression route constraint helpers
Route::get('users/{id}/friends/{friendname}', function ($id, $friendname) {
//
})->whereNumber('id')->whereAlpha('friendname');
Route::get('users/{name}', function ($name) {
//
})->whereAlphaNumeric('name');
Route::get('users/{id}', function ($id) {
//
})->whereUuid('id');
Route::get('users/{id}', function ($id) {
//
})->whereUlid('id');
Route::get('friends/types/{type}', function ($type) {
//
})->whereIn('type', ['acquaintance', 'bestie', 'frenemy']);
The Naming Relationship Between Route Parameters
and Closure/Controller Method Parameters
As you can see in Example 5, it’s most common to use the same names for your route parameters ({id}) and the method parameters they inject into your route definition (function ($id)). But is this necessary?
Unless you’re using route model binding, no. The only thing that defines which route parameter matches with which method parameter is their order (left to right), as you can see here:
Route::get('users/{userId}/comments/{commentId}', function (
$thisIsActuallyTheUserId,
$thisIsReallyTheCommentId
) {
//
});
That having been said, just because you can make them different doesn’t mean you should. I recommend keeping them the same for the sake of future developers, who could get tripped up by inconsistent naming.
Route Names
The simplest way to refer to these routes elsewhere in your application is just by their path. There’s a url() global helper to simplify that linking in your views, if you need it; see Example 9 for an example. The helper will prefix your route with the full domain of your site.
Example 9. The url() helper
<a href="<?php echo url('/'); ?>">
// Outputs <a href="http://myapp.com/">
However, Laravel also allows you to name each route, which enables you to refer to it without explicitly referencing the URL. This is helpful because it means you can give simple nicknames to complex routes, and also because linking them by name means you don’t have to rewrite your frontend links if the paths change (see Example 10).
Example 10. Defining route names
// Defining a route with name() in routes/web.php:
Route::get('members/{id}', [AppHttpControllerMemberController::class, 'show'])
->name('members.show');
// Linking the route in a view using the route() helper:
<a href="<?php echo route('members.show', ['id' => 14]); ?>">
This example illustrates a few new concepts. First, we’re using fluent route definition to add the name, by chaining the name() method after the get() method. This method allows us to name the route, giving it a short alias to make it easier to reference elsewhere.
In our example, we’ve named this route members.show; resourcePlural.action is a common convention within Laravel for route and view names.
Route Naming Conventions
You can name your route anything you’d like, but the common convention is to use the plural of the resource name, then a period, then the action. So, here are the routes most common for a resource named photo:
photos.index
photos.create
photos.store
photos.show
photos.edit
photos.update
photos.destroy
To learn more about these conventions, see “Resource Controllers”.
This example also introduced the route() helper. Just like url(), it’s intended to be used in views to simplify linking to a named route. If the route has no parameters, you can simply pass the route name (route(‘members.index’)) and receive a route string (“http://myapp.com/members”). If it has parameters, pass them as an array in the second parameter like we did in Example 10.
In general, I recommend using route names instead of paths to refer to your routes, and therefore using the route() helper instead of the url() helper. Sometimes it can get a bit clumsy—for example, if you’re working with multiple subdomains—but it provides an incredible level of flexibility to later change the application’s routing structure without major penalty.
Passing Route Parameters to the route() Helper
When your route has parameters (e.g., users/id), you need to define those parameters when you’re using the route() helper to generate a link to the route.
There are a few different ways to pass these parameters. Let’s imagine a route defined as users/userId/comments/commentId. If the user ID is 1 and the comment ID is 2, let’s look at a few options we have available to us:
Option 1:
route('users.comments.show', [1, 2])
// http://myapp.com/users/1/comments/2
Option 2:
route('users.comments.show', ['userId' => 1, 'commentId' => 2])
// http://myapp.com/users/1/comments/2
Option 3:
route('users.comments.show', ['commentId' => 2, 'userId' => 1])
// http://myapp.com/users/1/comments/2
Option 4:
route('users.comments.show', ['userId' => 1, 'commentId' => 2, 'opt' => 'a'])
// http://myapp.com/users/1/comments/2?opt=a
As you can see, nonkeyed array values are assigned in order; keyed array values are matched with the route parameters matching their keys, and anything left over is added as a query parameter.
Route Definitions in Laravel was originally published in Javarevisited on Medium, where people are continuing the conversation by highlighting and responding to this story.
This post first appeared on Read More

