rolies106/eloquent-sluggable

在 Laravel 中轻松为 Eloquent 模型创建 slugs

3.1.1 2015-08-14 07:45 UTC

README

在 Laravel 5 中为 Eloquent 模型轻松创建 slugs。

Build Status Total Downloads Latest Stable Version Latest Stable Version

注意 如果你在使用 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

定义将可短链接字符串转换为短链接的方法。此配置有三个可能的选项

  1. method 为 null(默认设置)时,包使用 Cocur/Slugify 来创建短链接。

  2. method 是可调用的,则使用该函数或类方法。该函数/方法应期望两个参数:要处理的字符串和分隔符字符串。例如,要复制默认行为,您可以这样做:

	'method' => ['Illuminate\\Support\\Str', 'slug'],
  1. 您还可以将 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进行错误报告、评论和建议。

  1. 分支此项目。
  2. 创建你的修复错误/特性分支并编写你的(有良好注释的)代码。
  3. 为你的代码创建单元测试。
    • 在根目录下运行 composer install --dev 以安装所需的测试包。
    • 将你的测试方法添加到 eloquent-sluggable/tests/SluggableTest.php
    • 运行 vendor/bin/phpunit 来执行新的(以及所有之前的)测试,确保一切通过。
  4. 提交你的更改(以及你的测试)并推送到你的分支。
  5. 针对 eloquent-sluggable 的 master 分支创建一个新的 pull request。

请注意,你必须针对 master 分支创建 pull request 以修复与 Laravel 5 兼容的版本。如果你正在处理 Laravel 4 支持,请使用 2.x 分支。

版权和许可

Eloquent-Sluggable 最初由 Colin Viebrock 编写,并由我(@rolies106)分支以满足我的需求,并按照 MIT 许可证发布。有关详细信息,请参阅 LICENSE 文件。

版权所有 2015 Rolies Deby