Skip to content
Simplify Slug Creation for Eloquent Models in Laravel

Simplify Slug Creation for Eloquent Models in Laravel

Published on
by Raziul Islam

Creating clean and user-friendly URLs is an essential aspect of every website for SEO. Laravel provides an helper Illuminate\Support\Str::slug for transforming string into URL-friendly slug, But this is not enough for our models to generate slugs automatically.

In this article, We’ll create a custom HasSlug trait that simplifies the process of generating slugs and explore how it can seamlessly enhance your Laravel models.

Defining the Custom HasSlug Trait

Let’s start by creating a custom HasSlug trait:

namespace App\Concerns; // your namespace
trait HasSlug
{
protected function slugKey(): string
{
return 'slug';
}
abstract protected function sluggable(): string;
}

Here, the slugKey method returns the name of the key that stores the slug. This method is used to determine the default key for the slug attribute. Default is ‘slug’.

Also, a sluggable abstract method, Which should be implemented by the model and return the name of the attribute that should be used for slug generation.

Boot Method for Slug Generation

Next, let’s implement the boot method for automatic slug generation during model creation:

protected static function bootHasSlug()
{
static::creating(function (Model $model) {
if (! $model->{$model->slugKey()}) {
$model->{$model->slugKey()} = static::generateUniqueSlug($model->{$model->sluggable()});
}
});
}

This boot method ensures that HasSlug trait takes action when a model is being created, automatically generating a unique slug if it is not already set.

Unique Slug Generation Logic

Let’s implement the generateUniqueSlug method that generates a unique slug based on the given string:

public static function generateUniqueSlug(?string $str): ?string
{
if (! $str) {
return null;
}
$counter = 1;
$strSlug = Str::slug($str);
$slug = $strSlug;
while (static::whereSlug($slug)->exists()) {
$slug = $strSlug.'-'.$counter++;
}
return $slug;
}

This method generates a unique slug based on the given string, checking if it already exists in the database. If it does, it appends a counter to the slug to generate a unique value.

Query Scopes and Finder Methods

Next, implement the whereSlug scope and findBySlug, findBySlugOrFail methods:

public function scopeWhereSlug(Builder $query, string $slug): Builder
{
return $query->where($this->slugKey(), $slug);
}
public static function findBySlug(string $slug, array $columns = ['*']): static
{
return static::whereSlug($slug)->first($columns);
}
public static function findBySlugOrFail(string $slug, array $columns = ['*']): static
{
return static::whereSlug($slug)->firstOrFail($columns);
}

These methods simplify the process of querying models based on their slugs, making your code more readable and efficient.

Making it Model-Specific

To tie everything together, your model needs to implement the abstract sluggable method:

abstract protected function sluggable(): string;

In your actual model, you specify which attribute should be used for slugging by returning its name in the sluggable method.

Implementing in Your Model

Now, let’s consider an example with a Post model:

use App\Concerns\HasSlug;
class Post extends Model
{
use HasSlug;
protected $fillable = ['title', 'slug', 'body'];
protected function sluggable(): string
{
return 'title'; // Use the 'title' attribute for slugging
}
// Other model-specific code...
}

By implementing the HasSlug trait and specifying the title attribute for slugging, you enable slugging functionality effortlessly.

Example:

$post = Post::create([
'title' => 'My Post',
'body' => 'This is my post body.',
]);
echo $post->slug; // Output: my-post
$another = Post::create([
'title' => 'My Post',
'body' => 'Another post with same title.',
]);
echo $another->slug; // Output: my-post-1

So, now you can generate slugs for your models seamlessly by just using the HasSlug trait and specifying the sluggable attribute.

Conclusion

Creating a custom HasSlug trait for slug generation allows for a tailored and simplified slug generation logic. It also simplifies the process of querying models based on their slugs, making your code more readable and efficient.

You can check out the Source Code for more details.

Raziul Islam

Raziul Islam

Full-Stack Developer from Bangladesh with expertise in PHP, Laravel, Livewire, JavaScript, and TailwindCSS.


Posts you might like