skip to Main Content

I have a User table I want to view each User by navigating /users/{slug}. e.g. for a User named John Doe the route would look like this /users/john-doe. Currently I am using the id to view a User (/users/1). To achieve this I could just iterate over all Users create the slug and check if the given slug is the same. But this wouldn’t be that efficient and if I have hundreds of Users this would take time. I thought about adding a new column.

Schema::table('users', function (Blueprint $table) {
    $table->string('slug')->after('name');
});

The problem is this won’t work because the column needs a default value and the "default" value depends on the value of name.

I could also just add the column in the Schema::create(...) function and run php artisan migrate:refresh. But like this I would lose all other Users.

Does someone has an idea?

3

Answers


  1. You can literally just add a resolveRouteBinding function to your users model to resolve the model.

    /**
     * Retrieve the model for a bound value.
     *
     * @param  mixed  $value
     * @param  string|null  $field
     * @return IlluminateDatabaseEloquentModel|null
    */
    public function resolveRouteBinding($value, $field = null)
    {
        if ($field != null) {
            return parent::resolveRouteBinding($value, $field);
        } elseif (is_numeric($value)) {
            return parent::resolveRouteBinding($value, 'id');
        } else {
            return parent::resolveRouteBinding($value, 'slug');
        }
    }
    

    With this function Laravel will auto resolve your model by your slug or by an id if the given value is numeric.

    For generating the slug, I would go with an Observer that will set the slug on creating. You should be able to to easily fill it before saving the model to the database. To keep the slug up to date you can just use the updating event of the observer.

    /**
     * Handle the User "creating" event.
     */
    public function creating(User $user): void
    {
        $user->slug = Str::slug($user->name);
    }
    

    For the first migration I would go with a default value or allow it to be nullable. You can change that in another migration when you have initially set the slug for each user.

    BR,
    Alex

    Login or Signup to reply.
  2. I do this exact same thing; a slug is generated from the name (or title, etc.) column, but I don’t do this in the database.

    When you’re creating a User model instance, set the slug, something like:

    public function store(Request $request) {
      $request->merge(['slug' => Str::slug($request->input('name'))]);
    
      // Validate `$request`
    
      User::create($request);
    
      // OR
    
      $user = new User();
      $user->name = $request->input('name');
      $user->slug = Str::slug($user->name);
      $user->save();
    }
    

    Additionally, if using a FormRequest, this can be done like:

    public function store(MyFormRequest $request) { 
      User::create($request);
    }
    

    Inside of MyFormRequest.php:

    class MyFormRequest extends FormRequest {
      protected function prepareForValidation() {
        $this->merge(['slug' => Str::slug($this->name)]);
      }
    
      public function rules() {
        // ...
      }
    }
    

    In your case, since you’ve got a bunch of existing Users, you’d need to run something like:

    foreach(User::all() as $user) {
      $user->update(['slug' => Str::slug($user->name)]);
    }
    

    After running the migration to "backfill" your slugs.

    Sidenote, be careful with relying on names for slugs; people do not have unique names, so you might end up with 2 "John Doe" slugs, which will require some custom logic to differentiate them.

    Login or Signup to reply.
  3. In SQL in general, you can’t really set a "dynamic" default value (depending on another column). However, what you can do is to set the column nullable, then compute its value programmatically for the existing rows, and finally changing the column to be not null.

    Example:

    Schema::table('users', function (Blueprint $table) {
        $table->string('slug')->nullable()->after('name');
    });
    
    // Compute slug values
    
    Schema::table('users', function (Blueprint $table) {
        $table->string('slug')->nullable(false)->change();
    });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search