sibche / eloquent-sluggable
在 Laravel 5 中轻松为 Eloquent 模型创建 slug。
Requires
- php: ^7.2
- cocur/slugify: ^3.2
- illuminate/config: ^6.0
- illuminate/database: ^6.0
- illuminate/support: ^6.0
Requires (Dev)
- limedeck/phpunit-detailed-printer: ^5.0
- mockery/mockery: ^1.2.3
- orchestra/database: 4.*
- orchestra/testbench: 4.*
- phpunit/phpunit: ^8.0
- dev-master
- 6.0.2
- 6.0.1
- 6.0.0
- 4.10.0
- 4.9.1
- 4.9
- 4.8.x-dev
- 4.8.0
- 4.7.0
- 4.6.0
- 4.5.x-dev
- 4.5.1
- 4.5.0
- 4.4.x-dev
- 4.4.1
- 4.4.0
- 4.3.0
- 4.2.x-dev
- 4.2.5
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 4.1.2
- 4.1.1
- 4.1.0
- 4.0.4
- 4.0.3
- 4.0.2
- 4.0.1
- 4.0.0
- 4.0.0-beta
- 3.1.4
- 3.1.3
- 3.1.2
- 3.1.1
- 3.1.0
- 3.0.0
- 3.0.0-beta
- 3.0.0-alpha
- 2.x-dev
- 2.0.5
- 2.0.4
- 2.0.3
- 2.0.2
- 2.0.1
- 2.0.0
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.0
- 1.0.0-beta
- 1.0.0-alpha3
- dev-support-php-74
This package is auto-updated.
Last update: 2024-08-29 05:24:39 UTC
README
在 Laravel 中为 Eloquent 模型轻松创建 slug。
注意:以下说明适用于 Laravel 5.8。如果您使用的是 Laravel 5.7,请参阅上一个版本的文档。
- 背景:什么是 slug
- 安装
- 更新您的 Eloquent 模型
- 使用方法
- SlugService 类
- 事件
- 配置
- 扩展 Sluggable
- SluggableScopeHelpers 特性
- 路由模型绑定
- 错误、建议、贡献和支持
- 版权和许可证
背景:什么是 slug?
slug 是字符串的简化版本,通常是 URL 友好的。对字符串进行“slug”操作通常涉及将其转换为单一种类,并删除任何非 URL 友好的字符(空格、带重音的字母、和号等)。生成的字符串可以用来标识特定的资源。
例如,如果您有一个博客,有文章,您可以通过 ID 来引用每篇文章
http://example.com/post/1
http://example.com/post/2
...但这并不友好(尤其是对于 SEO)。您可能更希望使用文章的标题作为 URL,但如果文章的标题是“我的晚餐与安德烈和弗朗索瓦”,这会显得很丑
http://example.com/post/My+Dinner+With+Andr%C3%A9+%26+Fran%C3%A7ois
解决方案是为标题创建一个 slug 并使用它。您可以使用 Laravel 内置的 Str::slug()
方法将标题转换为更友好的格式
http://example.com/post/my-dinner-with-andre-francois
这样的 URL 会使用户更满意(它易于阅读、易于输入等)。
有关更多信息,您可以阅读维基百科上的此描述。
slug 往往是唯一的。因此,如果您再写一篇具有相同标题的文章,您需要以某种方式区分它们,通常是在 slug 的末尾添加一个递增的计数器
http://example.com/post/my-dinner-with-andre-francois
http://example.com/post/my-dinner-with-andre-francois-1
http://example.com/post/my-dinner-with-andre-francois-2
这保持了 URL 的唯一性。
针对 Laravel 5 的 Eloquent-Sluggable 包旨在自动处理所有这些,配置最少。
安装
注意:根据您的 Laravel 版本,您应该安装该包的不同版本
Laravel 的旧版本可以使用该包的旧版本,尽管它们不再受支持或维护。有关详细信息,请参阅CHANGELOG.md和UPGRADING.md,并确保您正在阅读您版本的正确 README.md(Github 默认显示 master 分支的版本,这可能不是您想要的)。
-
通过 Composer 安装包
$ composer require cviebrock/eloquent-sluggable:^4.8
该包将自动注册其服务提供者。
-
可选地,如果您想更改任何默认值,可以发布配置文件
php artisan vendor:publish --provider="Cviebrock\EloquentSluggable\ServiceProvider"
更新您的 Eloquent 模型
您的模型应使用Sluggable特性,它包含一个抽象方法sluggable()
,您需要对其进行定义。在这里设置任何模型特定的配置(有关详细信息,请参阅下方的配置)
use Cviebrock\EloquentSluggable\Sluggable; class Post extends Model { use Sluggable; /** * Return the sluggable configuration array for this model. * * @return array */ public function sluggable() { return [ 'slug' => [ 'source' => 'title' ] ]; } }
当然,您的模型和数据库需要一列用于存储slug。您可以使用slug
或其他您想要的任何适当名称;您的配置数组将确定数据将存储在哪个字段。您需要手动通过自己的迁移添加此列。
就是这样...您的模型现在“可生成slug”了!
使用方法
保存模型很容易
$post = new Post([ 'title' => 'My Awesome Blog Post', ]); $post->save();
检索slug也同样简单
echo $post->slug;
请注意,如果您正在使用Eloquent的replicate()
方法复制模型,该包将在之后自动重新生成slug以确保唯一性。
$post = new Post([ 'title' => 'My Awesome Blog Post', ]); $post->save(); // $post->slug is "my-awesome-blog-post" $newPost = $post->replicate(); // $newPost->slug is "my-awesome-blog-post-1"
注意,空字符串、非字符串或其他“奇怪”的源值将导致不同的slug
(上述值将受到任何唯一或其他检查的影响。)
SlugService 类
生成slug的所有逻辑都由\Cviebrock\EloquentSluggable\Services\SlugService
类处理。
通常,您不需要直接访问此类,尽管有一个静态方法可以用来为给定的字符串生成slug,而无需实际创建或保存相关模型。
use \Cviebrock\EloquentSluggable\Services\SlugService; $slug = SlugService::createSlug(Post::class, 'slug', 'My First Post');
这对于Ajax控制器或其他类似情况很有用,在这些情况下,您想在实际上创建模型之前,向用户展示给定测试输入的slug将是什么。该方法的前两个参数是要测试的模型和slug字段,第三个参数是用于测试slug的源字符串。
您还可以传递一个可选的配置值数组作为第四个参数。这些值将优先于正在测试的slug字段的正常配置值。例如,如果您的模型配置为使用唯一slug,但您出于某种原因想要生成slug的“基础”版本,则可以这样做
$slug = SlugService::createSlug(Post::class, 'slug', 'My First Post', ['unique' => false]);
事件
注意:事件应该可以工作,但尚未完全测试。请帮助我!
Sluggable模型将触发两个Eloquent模型事件:“slugging”和“slugged”。
“slugging”事件在生成slug之前触发。如果此事件的回调返回false,则不会执行slugging。
“slugged”事件在生成slug之后触发。在模型不需要slugging的情况下(由needsSlugging()
方法确定),不会调用此事件。
您可以通过与其他Eloquent模型事件一样的方式挂钩到这两个事件之一
Post::registerModelEvent('slugging', function($post) { if ($post->someCondition()) { // the model won't be slugged return false; } }); Post::registerModelEvent('slugged', function($post) { Log::info('Post slugged: ' . $post->getSlug()); });
配置
配置旨在尽可能灵活。您可以设置所有Eloquent模型的默认值,然后为单个模型覆盖这些设置。
默认情况下,全局配置在app/config/sluggable.php
文件中设置。如果未设置配置,则使用包的默认值。以下是一个配置示例,其中显示了所有默认设置
return [ 'source' => null, 'maxLength' => null, 'maxLengthKeepWords' => true, 'method' => null, 'separator' => '-', 'unique' => true, 'uniqueSuffix' => null, 'includeTrashed' => false, 'reserved' => null, 'onUpdate' => false, ];
对于单个模型,配置由您需要实现的sluggable()
方法处理。该方法应返回一个索引数组,键代表存储slug值的字段,值是该字段的配置。这意味着您可以为同一模型创建多个slug,基于不同的源字符串和不同的配置选项。
public function sluggable() { return [ 'title-slug' => [ 'source' => 'title' ], 'author-slug' => [ 'source' => ['author.lastname', 'author.firstname'], 'separator' => '_' ], ]; }
source
这是构建slug的字段或字段数组。每个$model->field
通过空格分隔连接起来,构建可生成slug的字符串。这些可以是模型属性(即数据库中的字段)、关系属性或自定义getter。
要引用相关模型的字段,使用点表示法。例如,以下书籍的slug将从其作者的名字和书的标题生成
class Book extends Eloquent { use Sluggable; protected $fillable = ['title']; public function sluggable() { return [ 'slug' => [ 'source' => ['author.name', 'title'] ] ]; } public function author() { return $this->belongsTo(Author::class); } } ... class Author extends Eloquent { protected $fillable = ['name']; }
使用自定义getter的示例
class Person extends Eloquent { use Sluggable; public function sluggable() { return [ 'slug' => [ 'source' => 'fullname' ] ]; } public function getFullnameAttribute() { return $this->firstname . ' ' . $this->lastname; } }
如果 source
为空、false 或 null,则使用 $model->__toString()
的值作为生成 slug 的源。
maxLength
将此设置为正整数将确保生成的 slug 限制在最大长度内(例如,以确保它们适合您的数据库字段)。默认情况下,此值为 null,不强制执行限制。
注意:如果启用 unique
(默认情况下是启用的),并且您预计会有几个具有相同 slug 的模型,则应将此值设置为比您的数据库字段长度少几个字符。原因是类将为后续模型追加 "-1"、" "-2"、" "-3" 等,以保持唯一性。这些增量扩展不包括在 maxLength
计算的部分中。
maxLengthKeepWords
如果您正在使用 maxLength
设置截断 slug,那么您可能想确保 slug 不会被截断在单词的中间。例如,如果您的源字符串是 "My First Post",并且您的 maxLength
是 10,则生成的 slug 将是 "my-first-p",这并不理想。
默认情况下,maxLengthKeepWords
的值设置为 true,这将从 slug 的末尾修剪部分单词,结果为 "my-first" 而不是 "my-first-p"。
如果您想保留部分单词,则将此配置设置为 false。
method
定义将可变字符串转换为 slug 所使用的的方法。此配置有三个可能的选项
-
当
method
为 null(默认设置)时,包使用默认的 slugging 引擎 -- cocur/slugify -- 来创建 slug。 -
当
method
是可调用的,那么将使用该函数或类方法。函数/方法应期望两个参数:要处理的字符串和分隔符字符串。例如,要使用 Laravel 的Str::slug
,可以这样做
'method' => ['Illuminate\\Support\\Str', 'slug'],
- 您还可以将
method
定义为闭包(同样,期望两个参数)
'method' => function ($string, $separator) { return strtolower(preg_replace('/[^a-z]+/i', $separator, $string)); },
method
的其他任何值都将抛出异常。
有关更复杂的 slugging 要求,请参阅下方的 扩展 Sluggable。
onUpdate
默认情况下,更新模型时不会尝试生成新的 slug 值。假设一旦生成 slug,您就不希望它更改(这在使用 slug 作为 URL 时尤其如此,并且不希望破坏您的 SEO 力量)。
如果您想重新生成一个或多个模型中的 slug 字段,可以在更新之前将这些字段设置为 null 或空字符串
$post->slug = null; $post->update(['title' => 'My New Title']);
如果您希望在每次更新模型时都执行此行为,则将 onUpdate
选项设置为 true。
separator
这定义了构建 slug 时使用的分隔符,并将其传递给上面定义的 method
。默认值是破折号。
unique
这是一个布尔值,定义 slug 是否应在给定类型的所有模型中保持唯一。例如,如果您有两个名为 "My Blog Post" 的博客文章,那么如果 unique
为 false,则它们都会 slugging 为 "my-blog-post"。这可能是一个问题,例如,如果您使用 slug 作为 URL。
将 unique
设置为 true,则第二个 Post 模型将 slugging 为 "my-blog-post-1"。如果有第三个具有相同标题的文章,则它将 slugging 为 "my-blog-post-2",依此类推。每个后续模型都将获得附加到 slug 末尾的增量值,以确保唯一性。
uniqueSuffix
如果您想使用不同的方法来识别唯一性(而不是自动递增整数),可以将 uniqueSuffix
配置设置为函数或可调用的,该函数或可调用为您生成 "唯一" 值。
该函数应接受三个参数:基本路径名(即非唯一的路径名)、分隔符字符串以及所有以相同路径名开头的其他路径名的 \Illuminate\Support\Collection
。然后您可以进行任何操作来创建一个新后缀,该后缀在集合中的任何路径名都没有使用过。例如,如果您想使用字母而不是数字作为后缀,这是实现此目标的一种方法。
'uniqueSuffix' => function ($slug, $separator, Collection $list) { $size = count($list); return chr($size + 96); }
includeTrashed
将此设置为 true
还会在尝试强制唯一性时检查已删除的模型。这仅影响使用 软删除 特性的 Eloquent 模型。默认为 false
,因此软删除的模型在检查唯一性时不计入。
reserved
一个永远不会被允许作为路径名的值数组,例如,防止与现有路由或控制器方法等冲突。这可以是一个数组,或者一个返回数组的闭包。默认为 null
:没有保留的路径名。
简短配置
如果您的确很懒,该包支持非常简短的配置语法。
public function sluggable() { return [ 'slug' ]; }
这将使用 app/config/sluggable.php
中的所有默认选项,使用模型的 __toString()
方法作为源,并将路径名存储在 slug
字段中。
扩展 Sluggable
有时配置选项不足以满足复杂需求(例如,唯一性测试可能需要考虑其他属性)。
在这些情况下,该包提供了对路径名工作流程的挂钩,您可以在每个模型的基础上使用自己的函数,或者在扩展包特性的自定义特性中。
customizeSlugEngine
/** * @param \Cocur\Slugify\Slugify $engine * @param string $attribute * @return \Cocur\Slugify\Slugify */ public function customizeSlugEngine(Slugify $engine, $attribute) { ... }
如果您的模型上存在此方法,则可以在路径名之前自定义 Slugify 引擎。这可能是在这里更改使用的字符映射或修改语言文件等的地方。
您可以根据模型和属性对引擎进行自定义(也许您的模型有两个路径名字段,其中一个需要自定义)。
请参阅 tests/Models/PostWithCustomEngine.php
以获取示例。
scopeWithUniqueSlugConstraints
/** * @param \Illuminate\Database\Eloquent\Builder $query * @param \Illuminate\Database\Eloquent\Model $model * @param string $attribute * @param array $config * @param string $slug * @return \Illuminate\Database\Eloquent\Builder */ public function scopeWithUniqueSlugConstraints(Builder $query, Model $model, $attribute, $config, $slug) { ... }
如果您的模型上存在此作用域,则它也将应用于确定给定路径名是否唯一的查询所使用的查询。传递给作用域的参数是
$model
-- 正在被路径名化的对象$attribute
-- 正在生成的路径名字段$config
-- 给定模型和属性的配置数组$slug
-- "基本"路径名(在应用任何唯一后缀之前)
请随意在查询作用域中使用这些值。例如,请参阅 tests/Models/PostWithUniqueSlugConstraints.php
,其中为帖子从其标题生成路径名,但路径名限制在作者上。因此,Bob 可以有一个与 Pam 的帖子相同的标题的帖子,但两者将具有相同的路径名。
scopeFindSimilarSlugs
/** * Query scope for finding "similar" slugs, used to determine uniqueness. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $attribute * @param array $config * @param string $slug * @return \Illuminate\Database\Eloquent\Builder */ public function scopeFindSimilarSlugs(Builder $query, $attribute, $config, $slug) { ... }
这是为模型查找“类似”路径名的默认作用域。基本上,该包查找与 $slug
参数相同的现有路径名,或者以 $slug
加上分隔符字符串开始的路径名。传递给 uniqueSuffix
处理器的结果是此集合。
通常,这个查询作用域(在 Sluggable 特性中定义)应保持不变。然而,您可以在模型中自由重载它。
SluggableScopeHelpers 特性
将可选的 SluggableScopeHelpers
特性添加到您的模型中允许您处理模型及其路径名。例如
$post = Post::whereSlug($slugString)->get(); $post = Post::findBySlug($slugString); $post = Post::findBySlugOrFail($slugString);
由于模型可以有多个路径名,这需要更多的配置。有关所有详细信息,请参阅 SCOPE-HELPERS.md。
路由模型绑定
有关详细信息,请参阅 ROUTE-MODEL-BINDING.md。
错误、建议、贡献和支持
感谢所有为这个项目做出贡献的人!特别感谢JetBrains的开放源代码许可计划...以及当然,优秀的PHPStorm IDE!
请使用GitHub来报告错误,或提出评论或建议。
有关如何贡献更改,请参阅CONTRIBUTING.md。
版权和许可证
eloquent-sluggable由Colin Viebrock编写,并发布在MIT许可证下。
版权所有 © 2013 Colin Viebrock