slug)) { $model->slug = static::uniqueSlugFrom($model->name); } }); } /** * @param int|null $ignoreId the row being renamed, which must not * collide with the slug it already holds */ public static function uniqueSlugFrom(string $name, ?int $ignoreId = null): string { $base = Str::slug($name) ?: static::slugFallback(); $slug = $base; $suffix = 2; $collides = fn (string $candidate): bool => static::query()->withTrashed() ->where('slug', $candidate) ->when($ignoreId !== null, fn (Builder $query) => $query->whereKeyNot($ignoreId)) ->exists(); while ($collides($slug)) { $slug = $base.'-'.$suffix; $suffix++; } return $slug; } /** * Stands in when the name slugs to nothing at all — a name of only * punctuation or of characters Str::slug() drops entirely. */ abstract protected static function slugFallback(): string; }