每則貼文都會有一個貼文者,因此我們要來修改 posts 資料表 與 Post Model、以及新增貼文的路由。
5c
。新增貼文的路由應該要在登入後才能造訪,知道是誰進行貼文。
UI 的測試同樣是要使用 Dusk 來進行,測試如果在未登入的狀態,會跳轉到登入的頁面:
// tests/Browser/PostFormTest.php
class PostFormTest extends DuskTestCase
{
use DatabaseMigrations;
public function testRejectIfNotAuth()
{
$this->browse(function (Browser $browser) {
$browser->visit('/post/form')
->assertPathIs('/login');
});
}
}
需要的登入驗證,Laravel 已經幫我們寫好了,只要在需要的路由定義後加上 middleware->('auth')
。
各種 Middle Ware (中介層) 的定義在 app/Http/Kernel.php
,而其中的 auth
是 app/Http/Middleware/Authenticate.php
。
Route::get('/post/form', function () {
return view('post_form');
})->middleware('auth');
其實不算重構,只是順便讓我們的表單美觀一點,套用 Laravel 原本就寫好的模板。
<!-- resources/views/post_form.blade.php -->
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Post Something</div>
<div class="card-body">
<form method="post" action="/post">
@csrf
<input type="text" size="50" name="post_text">
<input type="submit" value="送出貼文">
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
// tests/Browser/PostFormTest.php
public function testPostByFormIfAuth()
{
$user = factory(User::class)->create([
'email' => 'taylor@laravel.com',
]);
$this->browse(function ($browser) use ($user) {
$browser->loginAs($user)
->visit('/post/form')
->type('post_text', "a testing post")
->press('送出貼文');
});
$this->assertDatabaseHas('posts', [
'post_text' => "a testing post"
]);
}
這邊看起來做了比較多事情,來整理一下:
loginAs()
直接進入登入狀態。這裡是原本在 TDD 實戰 D6:Laravel POST 方法 與 表單 中我們就已經完成的,只是之前我們還沒學會 Dusk。
讓我們繼續今天的主軸:修改資料表。
即使是需要更動到資料表,我們依舊可以從編寫測試開始。
來更改剛剛的測試,改成要新增的貼文中,需要有 user_id
欄位。
並且我們加入兩個測試的使用者,新增的貼文必須是關聯到正確的 user_id
。
public function testPostByFormIfAuth()
{
factory(User::class)->create();
$second_user = factory(User::class)->create([
'email' => 'taylor@laravel.com',
]);
$this->assertSame(2, $second_user->id);
$this->browse(function ($browser) use ($second_user) {
$browser->loginAs($second_user)
->visit('/post/form')
->type('post_text', "a testing post")
->press('送出貼文');
});
$this->assertDatabaseHas('posts', [
'post_text' => "a testing post",
'user_id' => $second_user->id
]);
}
測試那邊只修改了一點點,產品程式這邊要修改的就不少了。
$ php artisan make:migration add_user_to_posts_table --table=posts
// database/migrations/5_add_user_to_posts_table.php
class AddUserToPostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id')->default(0);
$table->foreign('user_id')->references('id')->on('users');
});
}
}
我們新增了一個 user_id
欄位,由於 posts 資料表本來就存在,因此需要加上 default()
特性,讓新舊資料不會產生衝突,每個欄位在 Laravel 中預設都是 Non nullable 的,在 migrate 時會把舊資料的新欄位都加上預設值。
foreign()
設定參照到哪個資料表,也就是將 user_id
參照到 users
表的 id
欄位。
設定 Post
Model 會參照到 User
Model [1]。
// app/Post.php
class Post extends Model
{
public function user()
{
return $this->belongsTo(App\User);
}
}
User
Model 則是設定擁有這些 Post
Model。
class User extends Authenticatable
{
...
public function posts()
{
return $this->hasMany(App\Post);
}
}
最後是處理新增貼文的控制器,加上要儲存 user_id。
// app/Http/Controllers/PostController.php
use Illuminate\Support\Facades\Auth;
class PostController extends Controller
{
public function insertPost(Request $request)
{
$post = new Post;
$post->post_text = $request->input('post_text');
$post->user_id = Auth::id(); // user_id
$post->save();
}
}
( $ git checkout 5c
)
這裡透過 Auth::id()
函式來幫忙,可以直接取得目前已登入的使用者 id。
我們加入了 Post 與 User 之間的關聯,以及透過 Dusk 來加入了新增貼文的測試。
$ php artisan dusk
測試順利執行完畢,讚!
然而就在我們準備開心的收工時,赫然發現!
$ ./vendor/bin/phpunit
竟然跳出了一堆錯誤訊息!
不用慌張,這是因為我們剛剛更動的程式碼,影響到了之前寫好的測試。
再一次感受到 TDD 的好處,當我們破壞到之前的程式時,測試們會馬上告訴我們 (regression test),讓我們明天就來著手改寫。