Created
June 11, 2016 15:49
-
-
Save ericlbarnes/3b5d3c49482f2a190619699de660ee9f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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(); | |
} | |
} |
I'm write simple trait which you can use in Eloquent models for slug auto-generation.
<?php
namespace App\Models\Traits;
use Illuminate\Database\Eloquent\Model;
/**
* Trait Sluggable.
*
* Sources:
* @see https://github.com/martinbean/laravel-sluggable-trait/
* @see https://gist.github.com/ericlbarnes/3b5d3c49482f2a190619699de660ee9f
* @see https://interworks.com.mk/the-easiest-way-to-create-unique-slugs-for-blog-posts-in-laravel/
*/
trait Sluggable
{
/**
* Boot the sluggable trait for a model.
*
* @return void
*/
public static function bootSluggable()
{
static::saving(function (Model $model) {
if (empty($model->getSlug())) {
$slug = self::generateUniqueSlug($model);
$model->setSlug($slug);
}
});
}
/**
* The name of the column to use for slugs.
*
* @return string
*/
public function getSlugColumnName()
{
return 'slug';
}
/**
* Get the string to create a slug from.
*
* @return string
*/
public function getSluggableString()
{
return $this->getAttribute('name');
}
/**
* Get the current slug value.
*
* @return string
*/
public function getSlug()
{
return $this->getAttribute($this->getSlugColumnName());
}
/**
* Set the slug to the given value.
*
* @param string $value
* @return $this
*/
public function setSlug($value)
{
$this->setAttribute($this->getSlugColumnName(), $value);
return $this;
}
/**
* @param Model $model
* @return string
* @throws \Exception
*/
private static function generateUniqueSlug(Model $model): string
{
$slug = empty($model->getSlug()) ? trim(str_slug($model->getSluggableString())) : $model->getSlug();
$attribute = trim($model->getSlugColumnName());
if (empty($slug) || empty($attribute)) {
throw new \Exception('Incorrect slug attribute or sluggable string for model! Check your "fillable" array.');
}
$modelsWithRelatedSlug = $model
->withoutGlobalScopes()
->withTrashed()
->where($attribute, 'LIKE', $slug.'%')
->get([$attribute]);
$i = 0;
while ($modelsWithRelatedSlug->contains($attribute, $slug)) {
++$i;
$matches = [];
if (preg_match('/^(.*?)-(\d+)$/', $slug, $matches)) {
$nextNum = $matches[2] + $i;
$slug = "{$matches[1]}-$nextNum";
} else {
$slug = "$slug-$i";
}
}
$model = $model
->withoutGlobalScopes()
->where($attribute, $slug)
->first([$attribute]);
if ($model) {
// Still not unique...
$slug = self::generateUniqueSlug($model);
}
return $slug;
}
}
Usage:
use App\Helpers\CoolSlug;
$slugLibrary = new CoolSlug(\App\Post::class);
return $slugLibrary->createSlug('test');
CoolSlug Class:
<?php
namespace App\Helpers;
class CoolSlug {
private $entity;
/**
* Instantiate a new CoolSlug instance.
*/
public function __construct($entity) {
$this->entity = $entity;
}
/**
* Generate a URL friendly "slug" from the given string.
*
* @param $title String
* @return string
* @throws \Exception
*/
public function createSlug($title) {
// Normalize the title
$slug = \Illuminate\Support\Str::slug($title, '-');
// Get any that could possibly be related.
// This cuts the queries down by doing it once.
$allSlugs = $this->getRelatedSlugs($slug);
// If we haven't used it before then we are all good.
if($allSlugs == 0) {
return $slug;
}
// Just append numbers like a savage until we find not used.
for($i = 1; $i <= 20; $i++) {
$newSlug = $slug. '-'. $i;
if($this->getRelatedSlugs($newSlug) == 0) {
return $newSlug;
}
}
throw new \Exception('Can not create a unique slug.');
}
protected function getRelatedSlugs($slug) {
return call_user_func(array($this->entity, 'select'), 'permalink')->where('permalink', $slug)->count();
}
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I need help because i am new to laravel too sorry my question might look silly but i need to understand .. Is this class is a service provider or a helper or what? and where should i locate this file i am confused a bit because i don't know about
namespace App\Services
is for service provider or something elseIf it's a service provider then why it's not namespaced with
namespace App\Providers
and why it doesn't extendextends ServiceProvider
in addition that class name should end with the wordProvider
When i tried to use this class this error appeared

My code:
use App\Services\Slug;
$slugLibrary = new Slug(\App\Show::class);
$slug = $slugLibrary->createSlug($show->title);