77.生成话题的 Slug(三)
- 本系列文章为
 laracasts.com的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
 - 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
 
本节说明
- 对应视频教程第 77 小节:We Need a Regression Test
 
本节内容
本节我们对生成话题 Slug 功能做一些完善。按照上一节的逻辑,对于一个标题为 He is Number 24 的话题而言,我们生成的 Slug 为 he-is-number-24。如果有人再次创建标题为 He is Number 24 的话题,会生成的 Slug 为 he-is-number-25。所以本节我们选择该话题的 id 作为 Slug 的后缀,这样可以确保 Slug 唯一。我们依然从新建测试开始:
forum\tests\Feature\CreateThreadsTest.php
    .
    .
    /** @test */
    public function a_thread_with_a_title_that_ends_in_a_number_should_generate_the_proper_slug()
    {
        $this->signIn();
        $thread = create('App\Thread',['title' => 'Something 24']);
        $thread = $this->postJson(route('threads'),$thread->toArray())->json();
        $this->assertEquals("something-24-{$thread['id']}",$thread['slug']);
    }
    /** @test */
    public function unauthorized_users_may_not_delete_threads()
    {
        .
        .
    }
    .
    .
由于我们生成 Slug 的逻辑改变了,所以我们要进行相应修改:
forum\app\Thread.php
.
.
class Thread extends Model
{
    .
    .
    protected static function boot()
    {
        parent::boot();
        static::deleting(function ($thread) {
            $thread->replies->each->delete();
        });
        static::created(function ($thread) {
           $thread->update([
               'slug' => $thread->title
           ]);
        });
    }
    .
    .
    public function setSlugAttribute($value)
    {
        $slug = str_slug($value);
        if (static::whereSlug($slug)->exists()) {
            $slug = "{$slug}-" . $this->id;
        }
        $this->attributes['slug'] = $slug;
    }
}
我们利用模型事件created来更新slug:当模型实例被创建时,slug为空;当模型实例被创建后,模型事件更新slug。所以我们要设置slug字段可以为空:
forum\database\migrations{timestamp}_create_threads_table.php
    .
    .
    public function up()
    {
        Schema::create('threads', function (Blueprint $table) {
            $table->increments('id');
            $table->string('slug')->unique()->nullable();
            $table->unsignedInteger('user_id');
            $table->unsignedInteger('channel_id');
            $table->unsignedInteger('replies_count')->default(0);
            $table->unsignedInteger('visits')->default(0);
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }
    .
    .
现在我们不用在控制器中保存slug,并且可以增加json返回:
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')
        ]);
        if (request()->wantsJson()) {
            return response($thread,201);
        }
        return redirect($thread->path())
            ->with('flash','Your thread has been published!');
    }
    .
    .
现在我们运行测试:
由于我们修改了逻辑,所以我们上一节建立的测试需要进行修改:
forum\tests\Feature\CreateThreadsTest.php
    .
    .
    /** @test */
    public function a_thread_requires_a_unique_slug()
    {
        $this->signIn();
        create('App\Thread',[],2);
        $thread = create('App\Thread',['title' => 'Foo Title']);
        $this->assertEquals($thread->fresh()->slug,'foo-title');
        $thread = $this->postJson(route('threads'),$thread->toArray())->json();
        $this->assertEquals("foo-title-{$thread['id']}",$thread['slug']);
    }
    /** @test */
    public function a_thread_with_a_title_that_ends_in_a_number_should_generate_the_proper_slug()
    {
        .
        .
    }
    .
    .
最后我们运行全部测试:
          
TDD 构建 Laravel 论坛笔记
                    
                    
            
            
                关于 LearnKu
              
                    
                    
                    
 
推荐文章: