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.