Search code examples
laravelrouteslaravel-routing

Why is it passing a param when it should not - Laravel Routing


Bizarre issue, lets see some routes:

Route::get('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController@index']);
Route::get('/admin/races/{race}', ['as' => 'races.race', 'uses' => 'RacesController@show']);
Route::get('/admin/races/create', ['as' => 'races.create', 'uses' => 'RacesController@create']);
Route::get('/admin/races/{race}/edit', ['as' => 'races.edit', 'uses' => 'RacesController@edit']);

Seems normal enough, lets see the controller:

class RacesController extends Controller {

    public function index() {
        return view('admin.races.list');
    }

    public function show(GameRace $race) {
        return view('admin.races.race', [
            'race' => $race,
        ]);
    }

    public function create() {
        return view('admin.races.manage', [
            'race' => null,
        ]);
    }

    public function edit(GameRace $race) {
        return view('admin.races.manage', [
            'race' => $race,
        ]);
    }
}

Seems normal enough. The issue is:

When I go to /admin/races/create I get a 404. The reason being is because, exception:

Illuminate\Database\Eloquent\ModelNotFoundException^ {#851
  #model: "App\Flare\Models\GameRace"
  #ids: array:1 [
    0 => "create"
  ]
  #message: "No query results for model [App\Flare\Models\GameRace] new"
  #code: 0
  #file: "./vendor/laravel/framework/src/Illuminate/Routing/ImplicitRouteBinding.php"
  #line: 47
  trace: {
   .....

Why is calling:

<li><a href="{{route('races.create')}}">Create Race</a></li>

Causing Laravel to take the word create and inject it in as a model? No other route that I have, that is similar does this. For context, here's how we create items:

Route::get('/admin/items/create', ['as' => 'items.create', 'uses' => 'ItemsController@create']);

Same concept, just instead of races its items. So how laravel messing this up?

I have run all the cache clears and route clears and everything. Same issue. Even tests are failing on this. No where am I calling this with a param (especially not one called create) so it should not be assuming there is a param.


Solution

  • It's because you have defined Route::get('/admin/races/{race}' .. first, so it'll hit that route regardless of what the value is. Simply moving the create route before the show route will solve your problem.

    Route::get('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController@index']);
    Route::get('/admin/races/create', ['as' => 'races.create', 'uses' => 'RacesController@create']);
    Route::get('/admin/races/{race}', ['as' => 'races.race', 'uses' => 'RacesController@show']);
    Route::get('/admin/races/{race}/edit', ['as' => 'races.edit', 'uses' => 'RacesController@edit']);
    

    That said, you can simplify this a lot more with a simple resource-route.

    Route::resource('/admin/races', ['as' => 'races.list', 'uses' => 'RacesController'])->only("index", "show", "create", "edit");