Creating Title Slugs with Laravel
When storing posts in the database a typical pattern for retrieving it is to create a unique “slug” based on the title. This gives users SEO-friendly way to access it through the URL.
For example, a typical URL might look like, awesome.com/post/1. The id in the URL gives no hints at what the content is about and is not user-friendly. Slugs, on the other hand, are designed to turn this into awesome.com/post/awesome-article
Creating slugs from an existing string is easy in Laravel as it includes a helper for just this use case:
$title = str_slug('Awesome Title', '-');
The results of this would be a lower-cased string with a dash (-) separator between the words:
awesome-title
While this is a huge help it doesn’t account for situations where you have two pieces of content with the same title.
So how to check two slugs are the same
<?php
namespace App\Services;
use App\Post;
class Slug
{
/**
* @param $title
* @param int $id
* @return string
* @throws \Exception
*/
public function createSlug($title, $id = 0)
{
// Normalize the title
$slug = str_slug($title);
// Get any that could possibly be related.
// This cuts the queries down by doing it once.
$allSlugs = $this->getRelatedSlugs($slug, $id);
// If we haven't used it before then we are all good.
if (! $allSlugs->contains('slug', $slug)){
return $slug;
}
// Just append numbers like a savage until we find not used.
for ($i = 1; $i <= 10; $i++) {
$newSlug = $slug.'-'.$i;
if (! $allSlugs->contains('slug', $newSlug)) {
return $newSlug;
}
}
throw new \Exception('Can not create a unique slug');
}
protected function getRelatedSlugs($slug, $id = 0)
{
return Post::select('slug')->where('slug', 'like', $slug.'%')
->where('id', '<>', $id)
->get();
}
}
How to create slug?
// On create
$post->slug = $slug->createSlug($request->title);
How to get post from a by its slug:
Route::get('/post/{slug}', function(){
$post = \App\Post::where('slug', $slug)->firstOrFail();
});
Subscribe to my channel :