Search code examples
laravellaravel-routing

Laravel resource: How define the name of the key parameter?


When you define a resource with Route::resource('recipe', 'RecipeController');, among others, the following route is defined: /photo/{photo}/edit, and once you define all your resources you have something like this:

  • /recipes/{recipes}/edit
  • /allergens/{allergens}/edit
  • /ingredients/{ingredients}/edit

Because all my records use id as primary key (MongoDB), I'd like to have {id} instead, like so:

  • /recipes/{id}/edit
  • /allergens/{id}/edit
  • /ingredients/{id}/edit

I dug in the Router class but I don't see how to specify this.

More over when I create a form with Form::model($record) I get actions like /recipes/{recipes} because recipes is a property of $record.

How can I define the name of the key parameter to id instead of recipes, allergens, ingredients?


Solution

  • In order to change the param name for Route::resource, you need custom ResourceRegistrar implementation.

    Here's how you can achieve that in a shortest possible way:

    // AppServiceProvider (or anywhere you like)
    public function register()
    {
      $this->app->bind('Illuminate\Routing\ResourceRegistrar', function ($app) {
    
        // *php7* anonymous class for brevity,
        // feel free to create ordinary `ResourceRegistrar` class instead
        return new class($app['router']) extends \Illuminate\Routing\ResourceRegistrar 
        {
    
          public function register($name, $controller, array $options = [])
          {
            if (str_contains($name, '/')) {
              return parent::register($name, $controller, $options);
            }
    
            // ---------------------------------
            // this is the part that we override
            $base = array_get($options, 'param', $this->getResourceWildcard(last(explode('.', $name))));
            // ---------------------------------
    
            $defaults = $this->resourceDefaults;
    
            foreach ($this->getResourceMethods($defaults, $options) as $m) {
              $this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
            }
          }
        };
      });
    }
    

    Now your routes will look like:

    Route::resource('users', 'UsersController', ['param' => 'some_param'])
    /users/{some_param}
    
    // default as fallback
    Route::resource('users', 'UsersController')
    /users/{users}
    

    Mind that this way can't work for nested resources and thus they will be a mix of default and custom behaviour, like this:

    Route::resource('users.posts', 'SomeController', ['param' => 'id'])
    /users/{users}/posts/{id}