Thomas Jensen
Thomas Jensen

Reputation: 2198

Multiple routes defaults to controller in Laravel

I want to be able to get to an article by a short-link, like this articles/ID, but I also want to get there with the full link articles/ID/category/slug. But I can not get the following to work:

// Route file:
Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create).*');
Route::pattern('slug', '^(?!edit).*');
Route::get('articles/{id}/{cat?}/{slug?}', ['as' => 'articles.show', 'uses' => 'ArticlesController@show']);

// Controller file:
public function show($id, $cat = null, $slug = null)
{
    dd('1: ' . $cat . ' | 2:' . $slug);
}

The following link articles/28/ullam/vel-repellendus-aut-est-est-esse-fugiat gives the result:

string(53) "1: ullam/vel-repellendus-aut-est-est-esse-fugiat | 2:"

I don't understand why it's not split, if I remove the ? in my route definition it works.

I have tried this solution https://stackoverflow.com/a/21865488/3903565 and that works, but not when directed at a controller. Why?

Update; I ended up rearranging my routes file:

Route::pattern('id', '[0-9]+');

// Articles
Route::get('articles/create', ['as' => 'articles.create', 'uses' => 'ArticlesController@create']);
Route::get('articles/edit/{id}', ['as' => 'articles.edit', 'uses' => 'ArticlesController@edit']);
Route::get('articles/{id}/{category?}/{slug?}', ['as' => 'articles.show', 'uses' => 'ArticlesController@show']);
Route::get('articles/{category?}', ['as' => 'articles.index', 'uses' => 'ArticlesController@index']);
Route::resource('articles', 'ArticlesController', ['only' => ['store', 'update', 'destroy']]);

Upvotes: 0

Views: 546

Answers (2)

Jarek Tkaczyk
Jarek Tkaczyk

Reputation: 81177

The problem is only in the lookahead patterns.

You need $ and class excluding / in order to make it work.

So here they are:

Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create$)[^/]*');
Route::pattern('slug', '^(?!edit$)[^/]*');

Route::get('articles/{id}/{cat?}/{slug?}', function($id, $cat, $slug)
{
    dd($id.': ' . $cat . ' | 2:' . $slug);
});

Upvotes: 1

Antonio Carlos Ribeiro
Antonio Carlos Ribeiro

Reputation: 87719

Is that your only route? Or you have a different one pointing to show too? I just added this one as my first route:

Route::get('articles/{id}/{cat?}/{slug?}', function($id, $cat, $slug)
{
    dd($id.': ' . $cat . ' | 2:' . $slug);
});

And got this result:

28: ullam | 2:vel-repellendus-aut-est-est-esse-fugiat

Change the order of your routes to have the first one as your most specific routes and the last as your most generic one. Also, if you have any patterns, they can be inteferring with your results.

Looks like there's a problem with some patterns and optional parameters (?), so if you do

Route::pattern('id', '[0-9]+');
Route::pattern('cat', '^(?!create).*');
Route::pattern('slug', '^(?!edit).*');

Route::get('articles/{id}/{cat}/{slug}', function($id, $cat, $slug)
{
    dd($id.': ' . $cat . ' | 2:' . $slug);
});

It will work fine. My advice is to not reuse routes too much, if you have an specific route, you create a route command for it and add it before your most generic ones:

Route::get('articles/{id}/create', function($id)
{
    dd('this is the create route');
});

Route::get('articles/{id}/{cat}/{slug}', function($id, $cat, $slug)
{
    dd($id.': ' . $cat . ' | 2:' . $slug);
});

The cost of creating new routes is low in comparison to the complexity of looking to regex patterns and try to find your way amongst them. Having new collaborators on projects, or even for your future self, the simpler, the better.

In Laravel 4.3 you'll have access to route caching, which makes routes being fired almost instantly.

Upvotes: 2

Related Questions