76.生成话题的 Slug(二)
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
- 对应视频教程第 76 小节:A Thread Should Have a Unique Slug: Part 2
本节内容
本节我们完成上一节未完成的功能:生成唯一的 Slug。我们选择的方式是给相同话题的 Slug 加上递增的后缀,如:title,title-2,title-3等。首先我们来新增一个测试:
forum\tests\Feature\CreateThreadsTest.php
.
.
/** @test */
public function a_thread_requires_a_valid_channel()
{
factory('App\Channel',2)->create(); // 新建两个 Channel,id 分别为 1 跟 2
$this->publishThread(['channel_id' => null])
->assertSessionHasErrors('channel_id');
$this->publishThread(['channel_id' => 999]) // channle_id 为 999,是一个不存在的 Channel
->assertSessionHasErrors('channel_id');
}
/** @test */
public function a_thread_requires_a_unique_slug()
{
$this->signIn();
$thread = create('App\Thread',['title' => 'Foo Title','slug' => 'foo-title']);
$this->assertEquals($thread->fresh()->slug,'foo-title');
$this->post(route('threads'),$thread->toArray());
// 相同话题的 Slug 后缀会加 1,即 foo-title-2
$this->assertTrue(Thread::whereSlug('foo-title-2')->exists());
$this->post(route('threads'),$thread->toArray());
// 相同话题的 Slug 后缀会加 1,即 foo-title-3
$this->assertTrue(Thread::whereSlug('foo-title-3')->exists());
}
.
.
然后我们选择用修改器的方式对 Slug 进行处理。首先我们修改控制器:
forum\app\Http\Controllers\ThreadsController.php
.
.
public function store(Request $request)
{
$this->validate($request,[
'title' => 'required|spamfree',
'body' => 'required|spamfree',
'channel_id' => 'required|exists:channels,id'
]);
$thread = Thread::create([
'user_id' => auth()->id(),
'channel_id' => request('channel_id'),
'title' => request('title'),
'body' => request('body'),
'slug' => request('title')
]);
return redirect($thread->path())
->with('flash','Your thread has been published!');
}
.
.
接着我们增加修改器:
forum\app\Thread.php
.
.
public function setSlugAttribute($value)
{
if(static::whereSlug($slug = str_slug($value))->exists()) {
$slug = $this->incrementSlug($slug);
}
$this->attributes['slug'] = $slug;
}
public function incrementSlug($slug)
{
// 取出最大 id 话题的 Slug 值
$max = static::whereTitle($this->title)->latest('id')->value('slug');
// 如果最后一个字符为数字
if(is_numeric($max[-1])) {
// 正则匹配出末尾的数字,然后自增 1
return preg_replace_callback('/(\d+)$/',function ($matches) {
return $matches[1]+1;
},$max);
}
// 否则后缀数字为 2
return "{$slug}-2";
}
}
运行测试:
运行全部测试: