How to Add Slugs to a Laravel Project

Slugs are a great way how to make URLs more readable for humans and also they are better for SEO. Although, Laravel has built-in slug helper. It has only basic functionality (just a simple function to make a slug from string). However, there are two popular libraries for Eloquent models. These are Laravel Sluggable (from Spatie) and Eloquent Sluggable (from cviebrock). Both of them solve the same problem. However, Larvel slugglable is more minimalistic and has fewer features.

Installing

We can install simply via Composer:

composer require cviebrock/eloquent-sluggable

Usage

When creating migrations for our model, add a "slug" field for a field, we want to sluggify. We can generate model class, controller and migration via:

php artisan make:model Post -mc
// ...
   public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->timestamps()
            $table->string('title');
            $table->string('slug');
            $table->text('content');
            // ... other fields
        });
    }
// ...

Now open our model file. Don't forget to add fillable fields to $fillable property, slug doesn't need to be there.

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'content',
    ];
}

Now add Sluggable trait from Cviebrock\EloquentSluggable\Sluggable to out model. Also add a public function sluggable() that return an array. Key of the array is a name of slug column, value is an array with options. Minimal options should look like this:

<?php

namespace App\Models;

use Cviebrock\EloquentSluggable\Sluggable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory, Sluggable;

    protected $fillable = [
        'title',
        'content',
    ];

    public function sluggable(): array
    {
        return [
            'slug' => [
                'source' => 'title',
            ],
        ];
    }
}

The source key specifies the column of source for title. We can also provide an array of source columns like: 'source' => ['id', 'title']. Also, we can provide an separator:

...
'source' => ['id', 'title'],
'separator' => '_', //default value is '-'
...

Reserved slugs

However, we might need to reserve some words that should not be slugs. For example, we have route /posts/new for creating new post. But if an user names his post "new", slug would be "new" and this post would never be found. To reserve this word, just add reserved key with an array of reserved values an configuration array:

...
'reserved' => ['new'],
...

Routing

To get model via slug in URL, we need to set a getRouteKeyName function in our model class:

class Post extends Model {
    /// ....

    public function getRouteKeyName(): string
    {
        return 'slug';
    }
    /// ...
}

Now, in our controller, we can just model bind in our model in controller like:

use App\Models\Post;

class PostController extends Controller
{
    /// ...
    public function show(Post $post)
    {
        return view('post.show', [
            'post' => $post, 
        ]);
    }
}

Now it should be all. Feel free to check out documentation. Thanks for reading.