How to get unique slug to same post-title for other time too?
I had the same problem, that's my solution:
- I would check if there is the same title in the database.
- If yes return rows with the same title
- Increment return value by 1
- If there are no posts/listing with the same title than if check will not be executed
/**
* Create a slug from title
* @param string $title
* @return string $slug
*/
protected function createSlug(string $title): string
{
$slugsFound = $this->getSlugs($title);
$counter = 0;
$counter += $slugsFound;
$slug = str_slug($title, $separator = "-", app()->getLocale());
if ($counter) {
$slug = $slug . '-' . $counter;
}
return $slug;
}
/**
* Find same listing with same title
* @param string $title
* @return int $total
*/
protected function getSlugs($title): int
{
return Listing::select()->where('title', 'like', $title)->count();
}
Create our own function that generate unique slug from any title. Look at the code and that is pretty much clear.
Reference code from this link.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
class ProductController extends Controller
{
public function store(Request $request)
{
$product = new Product;
$product->title = $request->title;
$product->slug = $this->createSlug($request->title);
$product->save();
}
public function createSlug($title, $id = 0)
{
$slug = str_slug($title);
$allSlugs = $this->getRelatedSlugs($slug, $id);
if (! $allSlugs->contains('slug', $slug)){
return $slug;
}
$i = 1;
$is_contain = true;
do {
$newSlug = $slug . '-' . $i;
if (!$allSlugs->contains('slug', $newSlug)) {
$is_contain = false;
return $newSlug;
}
$i++;
} while ($is_contain);
}
protected function getRelatedSlugs($slug, $id = 0)
{
return Product::select('slug')->where('slug', 'like', $slug.'%')
->where('id', '<>', $id)
->get();
}
}
Finally. We've created an Unique slug in Laravel.
localhost:8000/kwee-dev
localhost:8000/kwee-dev-1
If you are facing a slug colision, in this case best way to go would be adding an extra integer at the end example :
mysite.dev/my-post
mysite.dev/my-post-1
For this you could use a package to generate a slug
Of if you want to do it yourself then in the model add slugattribute
public function setSlugAttribute($value) {
if (static::whereSlug($slug = str_slug($value))->exists()) {
$slug = $this->incrementSlug($slug);
}
$this->attributes['slug'] = $slug;
}
So the setSlugAtrribute will always check if the slug for given model exists if so then it will increment the slug as I meantioned above, by simply calling the below method.
public function incrementSlug($slug) {
$original = $slug;
$count = 2;
while (static::whereSlug($slug)->exists()) {
$slug = "{$original}-" . $count++;
}
return $slug;
}
Basically we are always checking if the slug exists in the database, if yes then we use an accessor to change the slug by adding an integer at the end this way you will never end having the slug duplication issue.