14

I have a project in laravel and there are many routes in that project.

But i just discovered that the routes are all case sensitive, means /advertiser/reports is different than /advertiser/Reports .

So what i want is both the routes should redirect to same view. Currently /advertiser/Reports gives RouteNotFound Exception.

I have read about the Route::pattern() way of doing it but since there are many routes i'll have to put in a lot of efforts for that. So, what i want is a better way of doing it, if there is any.

jedrzej.kurylo
  • 39,591
  • 9
  • 98
  • 107
Nishant Srivastava
  • 379
  • 1
  • 2
  • 12
  • maybe this will help : http://stackoverflow.com/a/21752884/1409771 – DNReNTi Aug 12 '15 at 12:17
  • i have already seen it.. i have written this in the question too .. but for this i will have to change each of my routes which is like writing whole routes.php again for me so what i am looking for is a better alternative. – Nishant Srivastava Aug 12 '15 at 12:22
  • The most important question that you should ask yourself: why? Why would you do that? – N.B. Aug 12 '15 at 14:36
  • Sir because i want urls in my application case insensitive because urls are case insensitive as far as i know – Nishant Srivastava Aug 12 '15 at 14:42
  • Ok, let's try again - why should your app support case-insensitive routes? So you can type anything in or what? I don't see the need for you to support that. If there is no need for that, then you have no problem. – N.B. Aug 12 '15 at 14:55
  • Sir because there should be no difference between www.xyz.com/abc and www.xyz.com/Abc .. but in my application the difference exists due to case sensitivity in routes – Nishant Srivastava Aug 12 '15 at 15:07
  • Really? Since when should there be no difference between two completely different strings? You're using HTTP wrong and you invented a problem for yourself. Your accepted solution is ugly and you just did more work than you have to. All because you won't understand a simple fact: **there is no problem**. Just use lowercase routes in your application and be smart about it. No one likes ugly shitty code, and no one will tell you "good job" if you complicate a simple thing such as routing. Programmers should come up with *smart* ways to solve problems, not stupid ways to introduce them. – N.B. Aug 14 '15 at 15:00
  • Well, I just had this same problem. Someone printed (in a magazine) the URL to one of our pages with all uppercase letters, because it "looks nicer". Of course the page is 404 when entered. – Tominator Apr 26 '16 at 13:58

3 Answers3

16

In order to make routes case-insensitive you'll need to modify the way routes are matched with the URLs. In Laravel, it all happens in UriValidator object so you'll need to create your own validator.

Luckily, like most tasks in Laravel, it's not really complicated.

First, create the new validator class - the only difference between this one and the original is that you'll append the i modifier at the end of regular expression for the compiled route to switch enable case-insensitive matching.

<?php namespace Your\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));
  }
}

Secondly, 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.

Keep in mind that this code has not been tested by running. Let me know if there are any typos or something doesn't work as expected. I'll be more than happy to get that working for you :)

Nishant Srivastava
  • 379
  • 1
  • 2
  • 12
jedrzej.kurylo
  • 39,591
  • 9
  • 98
  • 107
  • First of all thanx for the reply. Now i used your code the below exception was thrown: Missing argument 2 for array_except() – Nishant Srivastava Aug 12 '15 at 13:59
  • Sir its still not working and i have no idea what to do. It is giving this exception: [Symfony\Component\Debug\Exception\FatalErrorException] Object of class Illuminate\Routing\Matching\MethodValidator could not be converted to string – Nishant Srivastava Aug 12 '15 at 14:35
  • Good, we're getting there. I updated the answer - the code you need to add to routes.php – jedrzej.kurylo Aug 12 '15 at 14:45
  • the code you have in the top of your routes.php file here should really be placed in an your AppServiceProvider, at the very least. – Jim Rubenstein Jul 28 '21 at 18:11
4

I know this is an old question but I came across this same problem and I just want to share my solution.

On method render(...) at Exceptions/Handler.php, catch 404 exceptions and validate the case of the URL then redirect like this:

public function render($request, Exception $exception)
{
    $url = $request->url();
    $loweredCaseUrl = strtolower($url);
    if (
        $exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException &&
        $url !== $loweredCaseUrl
    ) {
        return redirect($loweredCaseUrl);
    }

    return parent::render($request, $exception);
}

That's it.

rmondesilva
  • 1,732
  • 3
  • 17
  • 29
0

I wrote a gist which does this: https://gist.github.com/samthomson/f670f9735d200773e543

Edit your app/filters.php to check for uppercase characters in the route and redirect them to a converted route.

S..
  • 5,511
  • 2
  • 36
  • 43