LaravelRouting

Basic Routing

Routing defines a map between HTTP methods and URIs on one side, and actions on the other. Routes are normally written in the app/Http/routes.php file.

In its simplest form, a route is defined by calling the corresponding HTTP method on the Route facade, passing as parameters a string that matches the URI (relative to the application root), and a callback.

For instance: a route to the root URI of the site that returns a view home looks like this:

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

A route for a post request which simply echoes the post variables:

Route::post('submit', function() {
   return Input::all();
});

//or

Route::post('submit', function(\Illuminate\Http\Request $request) {
   return $request->all();
});

Routes pointing to a Controller method

Instead of defining the callback inline, the route can refer to a controller method in [[email protected]] syntax:

Route::get('login', '[email protected]');

A route for multiple verbs

The match method can be used to match an array of HTTP methods for a given route:

Route::match(['GET', 'POST'], '/', '[email protected]');

Also you can use all to match any HTTP method for a given route:

Route::all('login', '[email protected]');

Route Groups

Routes can be grouped to avoid code repetition.

Let's say all URIs with a prefix of /admin use a certain middleware called admin and they all live in the App\Http\Controllers\Admin namespace.

A clean way of representing this using Route Groups is as follows:

Route::group([
    'namespace' => 'Admin',
    'middleware' => 'admin',
    'prefix' => 'admin'
    ], function () {
        
        // something.dev/admin
        // 'App\Http\Controllers\Admin\IndexController'
        // Uses admin middleware
        Route::get('/', ['uses' => '[email protected]']);    
        
        // something.dev/admin/logs
        // 'App\Http\Controllers\Admin\LogsController'
        // Uses admin middleware
        Route::get('/logs', ['uses' => '[email protected]']);    
});

Named Route

Named routes are used to generate a URL or redirects to a specific route. The advantage of using a named route is, if we change the URI of a route in future, we wouldn't need to change the URL or redirects pointing to that route if we are using a named route. But if the links were generated using the url [ eg. url('/admin/login')], then we would have to change everywhere where it is used.

Named routes are created using as array key

Route::get('login', ['as' => 'loginPage', 'uses' => '[email protected]']);

or using method name

Route::get('login', '[email protected]')->name('loginPage');

Generate URL using named route

To generate a url using the route's name

$url = route('loginPage');

If you are using the route name for redirection

$redirect = Redirect::route('loginPage');

Route Parameters

You can use route parameters to get the part of the URI segment. You can define a optional or required route parameter/s while creating a route. Optional parameters have a ? appended at the end of the parameter name. This name is enclosed in a curly braces {}

Optional Parameter

Route::get('profile/{id?}', ['as' => 'viewProfile', 'uses' => '[email protected]']);

This route can be accessed by domain.com/profile/23 where 23 is the id parameter. In this example the id is passed as a parameter in view method of ProfileController. Since this is a optional parameter accessing domain.com/profile works just fine.

Required Parameter

Route::get('profile/{id}', ['as' => 'viewProfile', 'uses' => '[email protected]']);

Note that required parameter's name doesn't have a ? at the end of the parameter name.

Accessing the parameter in controller

In your controller, your view method takes a parameter with the same name as the one in the routes.php and can be used like a normal variable. Laravel takes care of injecting the value:

public function view($id){
    echo $id;
}

Catch all routes

If you want to catch all routes, then you could use a regular expression as shown:

Route::any('{catchall}', '[email protected]')->where('catchall', '.*');

Important: If you have other routes and you don't want for the catch-all to interfere, you should put it in the end. For example:

Catching all routes except already defined

Route::get('login',  '[email protected]');
Route::get('logout', '[email protected]');
Route::get('home',   '[email protected]');

// The catch-all will match anything except the previous defined routes.
Route::any('{catchall}', '[email protected]')->where('catchall', '.*');

Routes are matched in the order they are declared

This is a common gotcha with Laravel routes. Routes are matched in the order that they are declared. The first matching route is the one that is used.

This example will work as expected:

Route::get('/posts/{postId}/comments/{commentId}', '[email protected]');
Route::get('/posts/{postId}', '[email protected]');

A get request to /posts/1/comments/1 will invoke [email protected]. A get request to /posts/1 will invoke [email protected].

However, this example will not work in the same manner:

Route::get('/posts/{postId}', '[email protected]');
Route::get('/posts/{postId}/comments/{commentId}', '[email protected]');

A get request to /posts/1/comments/1 will invoke [email protected]. A get request to /posts/1 will invoke [email protected].

Because Laravel uses the first matched route, the request to /posts/1/comments/1 matches Route::get('/posts/{postId}', '[email protected]'); and assigns the variable $postId to the value 1/comments/1. This means that [email protected] will never be invoked.

Case-insensitive routes

Routes in Laravel are case-sensitive. It means that a route like

Route::get('login', ...);

will match a GET request to /login but will not match a GET request to /Login.

In order to make your routes case-insensitive, you need to create a new validator class that will match requested URLs against defined routes. The only difference between the new validator and the existing one is that it will append the i modifier at the end of regular expression for the compiled route to switch enable case-insensitive matching.

<?php namespace Some\Namespace;

use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use Illuminate\Routing\Matching\ValidatorInterface;

class CaseInsensitiveUriValidator implements ValidatorInterface
{
  public function matches(Route $route, Request $request)
  {
    $path = $request->path() == '/' ? '/' : '/'.$request->path();
    return preg_match(preg_replace('/$/','i', $route->getCompiled()->getRegex()), rawurldecode($path));
  }
}

In order for Laravel to use your new validator, you need to update the list of matchers that are used to match URL to a route and replace the original UriValidator with yours.

In order to do that, add the following at the top of your routes.php file:

<?php
use Illuminate\Routing\Route as IlluminateRoute;
use Your\Namespace\CaseInsensitiveUriValidator;
use Illuminate\Routing\Matching\UriValidator;

$validators = IlluminateRoute::getValidators();
$validators[] = new CaseInsensitiveUriValidator;
IlluminateRoute::$validators = array_filter($validators, function($validator) { 
  return get_class($validator) != UriValidator::class;
});

This will remove the original validator and add yours to the list of validators.