rolies106 / eloquent-sluggable
在 Laravel 中轻松为 Eloquent 模型创建 slugs
Requires
- php: >=5.4.0
- cocur/slugify: 1.1.*
- illuminate/config: 5.*
- illuminate/database: 5.*
- illuminate/support: 5.*
Requires (Dev)
- orchestra/testbench: 3.0.*
- phpunit/phpunit: 4.*
This package is auto-updated.
Last update: 2024-09-09 15:14:28 UTC
README
在 Laravel 5 中为 Eloquent 模型轻松创建 slugs。
注意 如果你在使用 Laravel 4,则请使用
2.x
分支或标记为2.*
的版本。目前,master
只针对 Laravel 5.* 进行了测试。
背景:什么是 slug?
Slug 是字符串的简化版本,通常是 URL 友好的。将字符串“slugging”的行为通常涉及将其转换为单一种类,并删除任何非 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 会让用户更开心(可读性、易于输入等)。
有关更多信息,你可能想阅读 维基百科上的此描述。
slugs 通常也是唯一的。所以如果我又写了一篇标题相同的文章,我需要以某种方式区分它们,通常是在 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 包将自动处理所有这些,只需在开始时进行最小配置。
安装和需求
首先,你需要使用 Composer 包含这个包
$ composer require rolies106/eloquent-sluggable
注意:Eloquent-Sluggable 现在使用 traits,所以你需要运行 PHP 5.4 或更高版本。如果你仍在使用 5.3,则请使用 "1.*" 版本,并遵循该版本 README.md 文件中的说明。
然后,从命令行运行 composer update
。
然后,通过添加服务提供者条目来更新 config/app.php
。
'providers' => [ // ... 'Rolies106\EloquentSluggable\SluggableServiceProvider', ];
最后,再次从命令行运行 php artisan vendor:publish
以发布默认配置文件。
更新你的 Eloquent 模型
你的模型应该实现 Sluggable 的接口并使用它的 trait。你还应该定义一个受保护的属性 $sluggable
,其中包含任何特定于模型的配置(有关详细信息,请参阅下方的 配置)
use Rolies106\EloquentSluggable\SluggableInterface; use Rolies106\EloquentSluggable\SluggableTrait; class Post extends Model implements SluggableInterface { use SluggableTrait; protected $sluggable = [ 'build_from' => 'title', 'save_to' => 'slug', 'generate_path' => true, // Generate path to save to table 'path_with_domain' => false, // Include domain in path 'is_tree' => true, // Generate path for tree e.g categories 'parent_relation' => 'parent', // Function for relation to parent 'path_column' => 'path' // Column where path will be saved ]; }
当然,你的数据库需要一个列来存储 slug。你可以手动操作,或者使用内置的 artisan 命令为你创建迁移。例如
php artisan sluggable:table posts
运行该命令将创建一个迁移,该迁移将向你的文章表添加一个名为 "slug" 的列。如果你想为 slug 列使用不同的名称,你可以将其作为第二个参数提供
php artisan sluggable:table posts slug_column
务必将模型的上 save_to
配置设置为与列名匹配。
就这样...你的模型现在已经是“sluggable”的啦!
使用类
保存模型非常简单
$post = new Post([ 'title' => 'My Awesome Blog Post', ]); $post->save();
检索短链接也同样简单
echo $post->slug; // or, if you don't know the name of the slug attribute: echo $post->getSlug();
请注意,如果您正在使用 Eloquent 的 replicate()
方法复制模型,那么您需要明确告诉包在之后强制重新生成短链接以确保唯一性。
$new_post = $post->replicate()->resluggify();
如果您想在 Laravel 之外使用 Eloquent-Sluggable,请参阅 问题 #37。
该特性还提供了一个方便的辅助函数来根据短链接查找模型。
$post = Post::findBySlug('my-slug');
这基本上是对 Post::where('slug-field','=','my-slug')->first()
的包装。如果您的短链接不是唯一的,则使用 getBySlug()
方法,它将返回一个 Eloquent 集合。
配置
配置设计得尽可能灵活。您可以设置所有 Eloquent 模型的默认值,然后为单个模型覆盖这些设置。
默认情况下,全局配置可以在 app/config/sluggable.php
文件中设置。如果未设置配置,则使用 vendor/rolies106/eloquent-sluggable/config/sluggable.php
中的包默认值。以下是一个配置示例,显示了所有默认设置:
return [ 'build_from' => null, 'save_to' => 'slug', 'max_length' => null, 'method' => null, 'separator' => '-', 'unique' => true, 'include_trashed' => false, 'on_update' => false, 'reserved' => null, ];
build_from
这是构建短链接的字段或字段数组。每个 $model->field
都会连接(用空格分隔)来构建可短链接的字符串。这可以是模型属性(即数据库中的字段)或自定义获取器。例如,这可以工作:
class Person extends Eloquent implements SluggableInterface { use SluggableTrait; protected $sluggable = [ 'build_from' => 'fullname', ] public function getFullnameAttribute() { return $this->firstname . ' ' . $this->lastname; } }
如果 build_from
为空、false 或 null,则使用 $model->__toString()
的值。
save_to
这是模型中存储短链接的属性字段。默认情况下,这是 "slug"。您需要在定义模式时创建此列。
Schema::create('posts', function ($table) { $table->increments('id'); $table->string('title'); $table->string('body'); $table->string('slug'); $table->timestamps(); });
max_length
将此设置为正整数将确保生成的短链接受限于最大长度(例如,以确保它们适合数据库字段)。默认情况下,此值是 null,不强制执行限制。
注意:如果启用了 unique
(默认启用),并且您预计会有多个具有相同短链接的模型,那么您应该将此值设置为数据库字段长度的一小部分。原因在于类将为后续的模型附加 "-1"、" -2"、" -3" 等,以保持唯一性。这些增量扩展不包括在 max_length
计算中。
method
定义将可短链接字符串转换为短链接的方法。此配置有三个可能的选项
-
当
method
为 null(默认设置)时,包使用 Cocur/Slugify 来创建短链接。 -
当
method
是可调用的,则使用该函数或类方法。该函数/方法应期望两个参数:要处理的字符串和分隔符字符串。例如,要复制默认行为,您可以这样做:
'method' => ['Illuminate\\Support\\Str', 'slug'],
- 您还可以将
method
定义为闭包(同样,期望两个参数)
'method' => function ($string, $separator) { return strtolower(preg_replace('/[^a-z]+/i', $separator, $string)); },
method
的任何其他值都将抛出异常。
有关更复杂的短链接要求,请参阅下面的 扩展 Sluggable。
separator
这定义了构建短链接时使用的分隔符,并将其传递给上面定义的 method
。默认值是连字符。
unique
这是一个布尔值,定义了短链接是否应在给定类型的所有模型中唯一。例如,如果您有两个名为 "我的博客文章" 的博客文章,则它们都将短链接化为 "my-blog-post"(使用 Sluggable 的默认设置)。这可能会成为一个问题,例如,如果您在 URL 中使用短链接。
通过开启unique
选项,第二个Post模型将自动转化为“my-blog-post-1”。如果有第三个标题相同的帖子,它将被转化为“my-blog-post-2”,以此类推。每个后续模型都会在其别名末尾附加一个递增值,以确保唯一性。
include_trashed
将此设置为true
将在尝试强制唯一性时检查已删除的模型。这仅影响使用软删除功能的Eloquent模型。默认值为false
,因此软删除的模型在检查唯一性时不计入。
on_update
一个布尔值。如果为false
(默认值),则在模型重新保存时(例如,如果您更改了博客文章的标题,别名将保持不变)或别名值已设置时,别名将不会更新。如果您想覆盖此行为,可以将它设置为true
(或在您自己的代码中手动更改 $model->slug 的值)。
(如果您想使用您模型的可配置的Sluggable设置手动设置别名值,您可以运行$model->resluggify()
来强制Sluggable更新别名字段。)
reserved
一个不允许作为别名使用的值的数组,例如,防止与现有路由或控制器方法等冲突。这可以是一个数组,也可以是一个返回数组的闭包。默认为null
:没有保留的别名名称。
##路由模型绑定
要开始使用别名或ID检索模型,您可以通过添加条目覆盖路由来更新/bootstap/app.php
。在标题为“绑定重要接口”的部分添加以下内容
$app->singleton( 'router', '\Rolies106\EloquentSluggable\SluggableRouter' );
如果您更喜欢在控制器或路由文件中查找模型,您可以使用一些辅助方法
Post::findBySlugOrId('slug-or-id');
或
Post::findBySlugOrIdOrFail('slug-or-id');
扩展 Sluggable
有时配置选项对于复杂需求来说是不够的(例如,可能需要考虑其他属性的唯一性测试,或者可能需要为同一模型生成两个别名)。
在这种情况下,您最好的选择是重载SluggableTrait的一些方法,这些方法可以是基于每个模型,或者是在扩展SluggableTrait的您自己的特质中。每个步骤的别名过程都被分解成单独的方法,并在生成别名时依次调用。
查看SluggableTrait->sluggify()
以查看操作顺序,但您可能需要重载以下受保护方法中的任何一个
needsSlugging()
确定模型是否需要别名。应返回一个布尔值。
getSlugSource()
返回形成别名来源的字符串(通常基于build_from
配置值)。
generateSlug($source)
实际的别名代码。通常实现配置中定义的任何内容,但也可以调用其他别名库。接受上述源字符串并返回一个字符串。
validateSlug($slug)
验证生成的别名是否有效,通常通过将其与配置中定义的任何内容进行比较来实现。应返回一个有效的别名字符串。
makeSlugUnique($slug)
检查给定的别名是否唯一。应返回一个唯一的别名字符串。
generateSuffix($slug, $list)
接受当前别名和一个“类似”的别名列表(例如,“slug-1”,“slug-2”等),并返回系列中的下一个。通常只返回N+1,但可以修改为使用随机、字母后缀等,而不是递增整数。
getExistingSlugs($slug)
返回所有与给定别名“类似”的现有别名。应返回一个键值数组,其中值是Eloquent模型的别名值(来自save_to
字段),键是相应Eloquent模型的ID。
setSlug($slug)
将(生成、有效且唯一的)别名写入模型的属性。
错误、建议和贡献
请使用Github进行错误报告、评论和建议。
- 分支此项目。
- 创建你的修复错误/特性分支并编写你的(有良好注释的)代码。
- 为你的代码创建单元测试。
- 在根目录下运行
composer install --dev
以安装所需的测试包。 - 将你的测试方法添加到
eloquent-sluggable/tests/SluggableTest.php
。 - 运行
vendor/bin/phpunit
来执行新的(以及所有之前的)测试,确保一切通过。
- 在根目录下运行
- 提交你的更改(以及你的测试)并推送到你的分支。
- 针对 eloquent-sluggable 的
master
分支创建一个新的 pull request。
请注意,你必须针对 master
分支创建 pull request 以修复与 Laravel 5 兼容的版本。如果你正在处理 Laravel 4 支持,请使用 2.x
分支。
版权和许可
Eloquent-Sluggable 最初由 Colin Viebrock 编写,并由我(@rolies106)分支以满足我的需求,并按照 MIT 许可证发布。有关详细信息,请参阅 LICENSE 文件。
版权所有 2015 Rolies Deby