3.话题的回复
- 本系列文章为
laracasts.com
的系列视频教程——Let's Build A Forum with Laravel and TDD 的学习笔记。若喜欢该系列视频,可去该网站订阅后下载该系列视频, 支持正版 ;- 视频源码地址:github.com/laracasts/Lets-Build-a-...;
- 本项目为一个 forum(论坛)项目,与本站的第二本实战教程 《Laravel 教程 - Web 开发实战进阶》 类似,可互相参照。
本节说明
对应视频第 3 小节:A Thread Can Have Replies
本节内容
上一节我们新建了两个功能测试:a_user_can_view_all_threads
和a_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);
}
运行测试,测试通过:
接下来根据写好的逻辑,将代码补充完整:
/** @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
,发现有报错:
给出的是失败
的提示,说明我们的功能测试没有通过,我们需要在\..\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 将看到内容与回复:
我们可以给回复
加上作者与时间:
.
.
<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>
.
.
此时我们仍有两个问题需要解决:
$reply->owner
的模型关联关系未建立;- 虽然我们使用了
$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
现在着手解决这两个问题。首先进行模型关联:
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
测试通过:
再来解决日期英文显示的问题:
如果要使用中文时间,则需要对 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()
{
//
}
}
刷新页面即可看到效果: