5

I would like to know how to add an optional route parameter for a controller method:

Currently I have a route, shown below:

Route::get('devices/{code}/{area}','HomeController@getDevices');

and a controller method:

public function getDevices($code=NULL,$area) {...}

My get request will look like:

/devices/A/ABC

It's working fine, but I want the {code} parameter to be optional so that I can get data in different ways:

/devices//ABC or 
/devices/ABC

I've tried the following, but all failed with NotFoundHttpException

Route::get('devices/{code?}/{area}','HomeController@getDevices'); Route::get('devices/(:any?)/{area}','HomeController@getDevices');

Thanks for your help.

Jon Winstanley
  • 23,010
  • 22
  • 73
  • 116
davidcoder
  • 898
  • 4
  • 11
  • 25

2 Answers2

12

The optional parameter needs to be at the end of the URL.

So yours is a clear Incorrect usage of default function arguments, as described here. This is the reason your code does not work as you expect it to.

You'll have to reverse the order of those two parameters or implement different methods for those cases, taking into account that you'll need some sort of prefix to differentiate between them:

Route::get('devices/area/{area}','HomeController@getDevicesByArea');
Route::get('devices/code-and-area/{code}/{area}','HomeController@getDevicesByAreaAndCode');

public function getDevicesByAreaAndCode($area, $code = NULL) {...}
public function getDevicesByArea($area) { 
    return $this->getDevicesByAreaAndCode($area);
}

OR, as I said before, reverse the parameters:

Route::get('devices/area-and-code/{area}/{code?}','HomeController@getDevicesByAreaAndCode');

public function getDevicesByAreaAndCode($area, $code = NULL) {...}
Jon Winstanley
  • 23,010
  • 22
  • 73
  • 116
Sergiu Paraschiv
  • 9,929
  • 5
  • 36
  • 47
  • I think it is the implementation of Laravel that causes the "incorrect" usage of the default parameters. If you pass the URL `/entrypoint//area` then the there is unambiguously two path-based parameters that follow the "entrypoint" route: '' and 'area'. Laravel does not pass the empty parameter on to the controller - it just drops it and treats "area" as the first parameter. That is wrong IMO. Having said that, the parameters can be reversed in this case, but it still should be possible to have two optional path-based parameters and only supply the second in the URL. – Jason Sep 27 '13 at 10:29
  • 1
    I somewhat agree with this. I'd fully agree if Laravel was the only framework/CMS/server(!) doing this. As described here http://webmasters.stackexchange.com/questions/8354/what-does-the-double-slash-mean-in-urls/8381#8381 Apache and IIS do this also. – Sergiu Paraschiv Sep 27 '13 at 11:28
  • Interesting read that. In this case - i.e. a framework with a single index.php entry point - the decision on how to handle the double slashes is taken entirely by the framework; Apache does not need to interpret the path to go searching directories for resources to deliver. I expect the argument on whether this is correct, or should be encouraged, will rage on. – Jason Sep 27 '13 at 11:33
  • Well, again, partially agree. _Recomended_ rewrite rules only give control to the framework if the URL does not match a file or folder physically in the public path. If the server decides to treat `//` as `/` then, even though (debatable) wrong, the framework should keep consistency. Good thing is you do have a choice: you can configure/patch the server/framework to act like you _prefer_. – Sergiu Paraschiv Sep 27 '13 at 11:41
2

You can do this with Laravel 4 if you want, and it may be convenient for some JSON calls where a parameter not on the end of the URI may need to be empty.

The key is setting up a route specifically for the empty parameter. This route:

Route::get('devices//{area}','HomeController@getDevicesByArea');

will catch the URI "devices//myarea" and send it to:

public function getDevicesByArea($area) {...}

Where the code is supplied, the main route can catch that:

Route::get('devices/{code}/{area?}','HomeController@getDevicesByCode');

sending the code and optional area to:

public function getDevicesByArea($code, $area = '') {...}

This is not to say that swapping the parameters around in this example is not the better solution especially if the URL is going to be handled by a human. But I'm just adding for the record here that what was originally requested is possible, and can make some AJAX requests easier to deal with.

Jason
  • 4,411
  • 7
  • 40
  • 53