<?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(); | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
@AucT Hi AucT, what is the function & variable to call? Example please. |
This comment has been minimized.
This comment has been minimized.
I found this little snippet in my old project: public function setSlugAttribute($value)
{
$slug = str_slug($value);
// Look for exisiting slugs
$existingSlugs = static::whereRaw("slug REGEXP '^{$slug}(-[0-9]*)?$'");
// If no matching slugs were found, return early
if ($existingSlugs->count() === 0)
return $slug;
// Get slugs in reversed order, and pick the first
$lastSlugNumber = intval(str_replace($slug . '-', '', $existingSlugs->orderBy('slug', 'desc')->first()->slug));
return $slug . '-' . ($lastSlugNumber + 1);
} |
This comment has been minimized.
This comment has been minimized.
//invoking for default (e.g. \App\Post)
$slugLibrary = new \App\Services\Slug();
//invoking for custom entity
$slugLibrary = new \App\Services\Slug(\App\Person::class);
//usage
$slug = $slugLibrary->createSlug('George Lucas'); |
This comment has been minimized.
This comment has been minimized.
@ericbarnes and @AucT, a while loop will be much better than for, for loop has limit till 10 slug after that it will throw exceptions |
This comment has been minimized.
This comment has been minimized.
Helpful utility class. Also on @AucT's edit, added option to set the model slug attribute right in the construct function.
|
This comment has been minimized.
This comment has been minimized.
Hey folks, I kinda changed: <?php
namespace App\Services;
/**
* Slug Class
*/
class Slug
{
public $entity;
public $attribute;
/**
*
*/
public function __construct($entity, $attribute = 'slug')
{
$this->entity = $entity;
$this->attribute = $attribute;
}
/**
* @param $title
* @param int $id
* @return string
* @throws \Exception
*/
public function create($title)
{
if (empty($title)) {
throw new \Exception('Title is empty');
}
// Normalize the title
$slug = str_slug($title);
$slugStoraged = $this->getRelated($slug);
if ($slugStoraged == null) {
return $slug;
}
$lastNum = intval(str_replace($slug . '-', '', $slugStoraged->{$this->attribute}));
if (is_numeric($lastNum)) {
return $slug . '-' . ++$lastNum;
}
}
public function getRelated($slug)
{
return call_user_func([$this->entity, 'select'], $this->attribute)
->where($this->attribute, 'like', $slug . '%')
->orderBy($this->attribute, 'desc')
->first();
}
} |
This comment has been minimized.
This comment has been minimized.
I'm new to laravel, I have created a file called Slug.php and put it into App\Providers folder, When I Call |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
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;
}
} |
This comment has been minimized.
This comment has been minimized.
Usage:
CoolSlug Class:
|
This comment has been minimized.
you can make more reusable by adding entity. So you can use it not only for posts but for other entity.
here simple example