'Laravel generate slug before save
Im trying to learn laravel 5 with help of this wondefull website. For my activity model I want to generate slugs before I save one to my database so I've created the following model.
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Activity extends Model {
protected $table = 'activitys';
protected $fillable = [
'title',
'text',
'subtitle'
];
// Here I want to auto generate slug based on the title
public function setSlugAttribute(){
$this->attributes['slug'] = str_slug($this->title , "-");
}
//
}
But when I save an object with help of the Activity model slug is not filled, i tried changing it to $this->attributes['title'] = "test" for testing but it didnt run. Also I tried adding parameters $title, $slug to setSlugAttribute() but it didnt help.
What am I doing wrong and could someone explain the parameter that is used in some examples for setSomeAttribute($whyParameterHere).
Note : there is a slug field in my database.
As suggested by user3158900 I've tried :
public function setTitleAttribute($title){
$this->title = $title;
$this->attributes['slug'] = str_slug($this->title , "-");
}
//
This makes my title field empty but saves the slug the way I want it, why is $this->title empty then ? If I remove $this->title = $title; both title and slug are empty
Solution 1:[1]
You got 2 ways:
1. Add localy in your controller method this line:
$request['slug'] = Str::slug($request->title);
Example:
//use Illuminate\Support\Str;
public function store(Request $request)
{
$request['slug'] = Str::slug($request->title);
auth()->user()->question()->create($request->all());
return response('Created!',Response::HTTP_CREATED);
}
2. Add it in your model to check it every save in db
//use Illuminate\Support\Str;
protected static function boot() {
parent::boot();
static::creating(function ($question) {
$question->slug = Str::slug($question->title);
});
}
Example:
<?php
namespace App\Model;
use Illuminate\Database\Eloquent\Model;
use App\User;
use Illuminate\Support\Str;
class Question extends Model
{
protected static function boot() {
parent::boot();
static::creating(function ($question) {
$question->slug = Str::slug($question->title);
});
}
//The rest of methods
In each way you have to add this code before class declaration:
use Illuminate\Support\Str;
Solution 2:[2]
One way to accomplish this would be to hook into model events. In this instance, we want to generate a slug upon creating.
/**
* Laravel provides a boot method which is 'a convenient place to register your event bindings.'
* See: https://laravel.com/docs/4.2/eloquent#model-events
*/
public static function boot()
{
parent::boot();
// registering a callback to be executed upon the creation of an activity AR
static::creating(function($activity) {
// produce a slug based on the activity title
$slug = \Str::slug($news->title);
// check to see if any other slugs exist that are the same & count them
$count = static::whereRaw("slug RLIKE '^{$slug}(-[0-9]+)?$'")->count();
// if other slugs exist that are the same, append the count to the slug
$activity->slug = $count ? "{$slug}-{$count}" : $slug;
});
}
You will also need to add the following to your applications list of aliases (app.php):
'Str' => Illuminate\Support\Str::class,
Solution 3:[3]
You could use this package which I use https://github.com/cviebrock/eloquent-sluggable or check how it applies an observer on the model saving and how it generates a unique Slug, then do the same.
Solution 4:[4]
You want to set the slug based off the title when the title attribute is being set.
public function setTitleAttribute($value)
{
$this->attributes['title'] = $value;
$this->attributes['slug'] = str_slug($value);
}
/// Later that same day...
$activity->title = 'Foo Bar Baz';
echo $activity->slug; // prints foo-bar-baz
Another alternative would be to use a ModelObserver and listen to the saving event. This will allow you to generate the slug right before the model is written to the database.
class ActivityObserver {
public function saving($activity)
{
$activity->slug = str_slug($activity->title);
}
}
In both cases you probably want to add some logic to test if the slug already exists in the DB, adding an incrementing number if it does. ie foo-bar-baz-2. The safest place for this logic would be in the ModelObserver as it is executed immediately prior to the write action.
Solution 5:[5]
You can use this method. This is one that I am using to get unique SEO friendly slug. This will work in all laravel versions. https://stackoverflow.com/a/72137537/7147060
Solution 6:[6]
It's an old post, but this is what you find these days when searching for modern solutions, so here is a modern solution:
When using Laravel ^9.x, the Attribute mutator can be used, and must look something like this:
use Str;
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function name(): Attribute
{
return Attribute::make(
set: fn($value) => ['slug' => Str::slug($value), 'name' => $value]
);
}
Setting it with $this->slug inside the closure won't work as the result vanishes in a merge.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Community |
| Solution 2 | Dilworth |
| Solution 3 | Mahmoud Zalt |
| Solution 4 | Collin James |
| Solution 5 | Mohit Prajapati |
| Solution 6 | SomeOne_1 |
