I want to use the following URL: mydomen.ru/user/username

User Model:

 namespace App; use Illuminate\Auth\Authenticatable; use Illuminate\Database\Eloquent\Model; use Illuminate\Auth\Passwords\CanResetPassword; use Illuminate\Foundation\Auth\Access\Authorizable; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract; use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract; class User extends Model implements AuthenticatableContract, AuthorizableContract, CanResetPasswordContract { use Authenticatable, Authorizable, CanResetPassword; /** * The database table used by the model. * * @var string */ protected $table = 'user'; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name', 'email', 'password']; /** * The attributes excluded from the model's JSON form. * * @var array */ protected $hidden = ['password', 'remember_token']; public function getRouteKey() { return $this->name; } 

}

The route looks like this:

 Route::get('user/{user}', 'UserController@show'); 

This is the controller:

 <?php namespace App\Http\Controllers; use App\User; use Illuminate\Http\Request; use App\Http\Requests; use App\Http\Controllers\Controller; use Illuminate\Support\Facades\App; class UserController extends Controller { /** ... */ /** * Display the specified resource. * * @param User $user * @return \Illuminate\Http\Response */ public function show(User $user) { return view('user/show', ['user'=>$user->toJson()] ); } /** ... */ } 

This is in the RouteServiceProvider:

 public function boot(Router $router) { $router->model('user', 'App\User'); parent::boot($router); } 

What did I miss? Why is the URL still linked to user.id and not to user.name , as I want?

    1 answer 1

    In RouteServiceProvider you use the code:

     $router->model('user', 'App\User'); 

    Let's see what happens here.

    File \vendor\laravel\framework\src\Illuminate\Routing\Router.php

     /** * Register a model binder for a wildcard. * * @param string $key * @param string $class * @param \Closure|null $callback * @return void * * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException */ public function model($key, $class, Closure $callback = null) { $this->bind($key, function ($value) use ($class, $callback) { if (is_null($value)) { return; } // For model binders, we will attempt to retrieve the models using the first // method on the model instance. If we cannot retrieve the models we'll // throw a not found exception otherwise we will return the instance. $instance = $this->container->make($class); if ($model = $instance->where($instance->getRouteKeyName(), $value)->first()) { return $model; } // If a callback was supplied to the method we will call that to determine // what we should do when the model is not found. This just gives these // developer a little greater flexibility to decide what will happen. if ($callback instanceof Closure) { return call_user_func($callback, $value); } throw new NotFoundHttpException; }); } 

    Notice the code

     if ($model = $instance->where($instance->getRouteKeyName(), $value)->first()) { return $model; } 

    The model method getRouteKeyName() .

    I draw your attention to the fact that you override another method : getRouteKey() . This is your main mistake.

    Consider the code below. We trace the entire chain starting from getRouteKeyName()

    File \vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php

     /** * Get the route key for the model. * * @return string */ public function getRouteKeyName() { return $this->getKeyName(); } 
     /** * Get the primary key for the model. * * @return string */ public function getKeyName() { return $this->primaryKey; } 
     /** * The primary key for the model. * * @var string */ protected $primaryKey = 'id'; 

    So, in this way, it turns out that by default the getRouteKeyName() method of class Illuminate\Database\Eloquent\Model returns the name of the primary key, which is equal to id .

    Therefore, you need to override this method so that it returns the name of the desired column in the table. And it should be a string .

    Thus, from your model you need to delete your method:

     public function getRouteKey() { return $this->name; } 

    And add the following:

     public function getRouteKeyName() { return 'name'; } 

    I recommend you to watch the video Route Model Binding from the Laravel 5 Fundamentals series

    • Thank you so much for the answer! Surely he should help. So far everything has stopped working - but this is a good sign - it means something has changed :) - Razzwan
    • Yes, I understood my mistake. The string should have been the name of the field. Thanks again;) This is how it worked: public function getRouteKeyName() { return 'name'; } public function getRouteKeyName() { return 'name'; } - Razzwan
    • Yes, I misled you by specifying slug instead of name. I beg your pardon - VenZell