3.话题的回复

未匹配的标注

本节说明

对应视频第 3 小节:A Thread Can Have Replies

本节内容

上一节我们新建了两个功能测试:a_user_can_view_all_threadsa_user_can_read_a_single_thread,均为读取的功能性测试。于是将该文件重命名为ReadThreadsTest.php。这样会更具可读性。
接下来在ReadThreadsTest.php中增加一个新的功能测试:

.
.
/** @test */
public function a_user_can_read_replies_that_are_associated_with_a_thread()
{
   // 如果存在 Thread
   // 并且该 Thread 拥有回复
   // 那么当我们看该 Thread 时
   // 我们也要看到回复
}

这时我们会发现我们总是在重复初始化Thread

.
$thread = factory('App\Thread')->create();
.

把这一过程抽离出来,新建setUp()方法:

.
.
use DatabaseMigrations;

/** @var Thread $thread */
protected $thread;

public function setUp()
{
    parent::setUp(); // TODO: Change the autogenerated stub

    $this->thread = factory('App\Thread')->create();
}
.
.

重构a_user_can_view_all_threads方法:

{
    $response = $this->get('/threads');
    $response->assertSee($this->thread->title);
}

重构a_user_can_read_a_single_thread方法:

{
    $response = $this->get('/threads/' . $this->thread->id);
    $response->assertSee($this->thread->title);
}

运行测试,测试通过:
file

接下来根据写好的逻辑,将代码补充完整:

/** @test */
public function a_user_can_read_replies_that_are_associated_with_a_thread()
{
    // 如果有 Thread
    // 并且该 Thread 有回复
    $reply = factory('App\Reply')
        ->create(['thread_id' => $this->thread->id]);
    // 那么当我们看 Thread 时
    // 我们也要看到回复
    $this->get('/threads/'.$this->thread->id)
        ->assertSee($reply->body);
}

在上面的代码中,我们没有将测试的结果赋值给$response,因为我们不需要这么做。于是我们再次重构:

.
.
/** @test */
public function a_user_can_view_all_threads()
{
    $this->get('/threads')
        ->assertSee($this->thread->title);
}

/** @test */
public function a_user_can_read_a_single_thread()
{
    $this->get('/threads/' . $this->thread->id)
        ->assertSee($this->thread->title);
}
.
.

运行phpunit,发现有报错:
file

给出的是失败的提示,说明我们的功能测试没有通过,我们需要在\..\views\threads\show.blade.php视图文件中加上回复区域:

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        {{ $thread->title }}
                    </div>

                    <div class="panel-body">
                        {{ $thread->body }}
                    </div>
                </div>
            </div>
        </div>

        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                @foreach ($thread->replies as $reply)
                    <div class="panel panel-default">

                        <div class="panel-body">
                            {{ $reply->body }}
                        </div>
                    </div>
                @endforeach
            </div>
        </div>  
    </div>
@endsection

需要注意地是,我们使用了$thread->replies模型关联的方式取出回复,但此时关系还未建立。进行模型关联:
app\Thread.php

.
.
public function replies()
{
    return $this->hasMany(Reply::class);
}

再次运行phpunit即可测试通过,这意味着访问 forum.test/threads/1 将看到内容与回复:
file
我们可以给回复加上作者与时间:

.
.
    <div class="col-md-8 col-md-offset-2">
        @foreach ($thread->replies as $reply)
            <div class="panel panel-default">
                <div class="panel-heading">
                    {{ $reply->owner->name }} 回复于
                    {{ $reply->created_at->diffForHumans() }}
                </div>

                <div class="panel-body">
                    {{ $reply->body }}
                </div>
            </div>
        @endforeach
    </div>
.
.

此时我们仍有两个问题需要解决:

  1. $reply->owner的模型关联关系未建立;
  2. 虽然我们使用了$reply->created_at->diffForHumans()对日期进行友好化处理,但页面显示的日期为英文形式。

不过既然采用的是 TDD 的开发理念,那就让我们先于编写代码之前,先行编写一个单元测试

$ php artisan make:test ReplyTest --unit

app\tests\Unit\Replytest.php

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class ReplyTest extends TestCase
{
    use DatabaseMigrations;

    /** @test */
    public function a_reply_has_an_owner()
    {
        $reply = factory('App\Reply')->create();

        $this->assertInstanceOf('App\User',$reply->owner);
    }
}

运行单元测试:

$  phpunit tests/Unit/ReplyTest.php

file
现在着手解决这两个问题。首先进行模型关联:
app\Reply.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Reply extends Model
{
    protected $guarded = [];

    public function owner()
    {
        return $this->belongsTo(User::class,'user_id');  // 使用 user_id 字段进行模型关联
    }

}

再次运行单元测试:

$  phpunit tests/Unit/ReplyTest.php

测试通过:
file

再来解决日期英文显示的问题:

如果要使用中文时间,则需要对 Carbon 进行本地化设置。Carbon 是 PHP DateTime 的一个简单扩展,Laravel 将其默认集成到了框架中。对 Carbon 进行本地化的设置很简单,只需要在 AppServiceProvider 中调用 Carbon 的 setLocale 方法即可,AppServiceProvider 是框架的核心,在 Laravel 启动时,会最先加载该文件。

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Carbon\Carbon;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Carbon::setLocale('zh');
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

刷新页面即可看到效果:
file

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
贡献者:2
讨论数量: 0
发起讨论 只看当前版本


暂无话题~