前回の続き
www.tohuandkonsome.site
やること
登録ページを作成して、Threadを新規に登録できるようにする。
テストコード
こんな感じのコードを書いてみるよ。
ThreadTest.php
<?php
@return
public function test_user_can_post_thread()
{
$thread = factory('App\Thread')->make();
$this->post('/thread', $thread->toArray());
}
factory('App\Thread')->make()
は、createと違って、データベースに保存はされないけれども、テストデータのインスタンスを作成してくれる。
そのインスタンスをtoArray()
でpostの第二引数に渡してあげることで、postリクエストとして渡すことができるみたい。
また、本当はログインしたユーザーが作成できること、というテストを行うんだけれども、認証周りは後回しにすることにしたよ。
実装
ルーティングの定義はこんな感じ。
ThreadController@show
より下に投稿の定義を持ってちゃうと、{thread}
のせいかうまくいかなかったりする。
web.php
<?php
Route::get('/thread', 'ThreadController@index');
Route::post('/thread', 'ThreadController@store');
Route::get('/thread/{thread}', 'ThreadController@show');
コントローラーはこんな感じ。
ものすごくシンプルだね。
ThreadController.php
<?php
public function store(Request $request, Thread $thread)
{
$thread->create($request->all());
return redirect('/thread');
}
そして、忘れがちだけれどもモデルについても、マスアサイントメント機能を有効にしておくよ。
ThreadContoroller@store
のようにリクエストの内容全部をcreateみたいにしていると、リクエストの中に本来さわっちゃいけないname属性をこっそりいれられちゃったりした場合に、意図せずデータがつくられたり、更新されたりやべーよ!ということでこういった機能があるみたいだね。
Thread.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Thread extends Model
{
protected $guarded = [];
}
テストの前に
実装が終えたので、テストを実行するよ!とする前に、ちょっと寄り道。
今回のテストは何をもって成功とればいいんだろう。
少し考えてみると、こんなような候補があがってくるね!
$thread()->create()
でデータが保存されていること
- エラーでなければ、
return redirect()
リダイレクトしているから、リダイレクトされること
- いっそのことHTTPステータスコード200ってのも
正解はなんだろう、というところだけれども自分もよくわからないんだ。
そもそもホワイトボックス的に、最終的なアウトプットだけをみればいいのか、ブラックボックス的なことが必要なのかとかそのへんがまだあんまりわかってないです。
今回に関していえば、Laracastの動画に則り、
というアプローチで成功かどうかを判断するよ。
ということで直したテストコードはこんな感じ。
ThreadTest.php
<?php
@return
public function test_user_can_post_thread()
{
$thread = factory('App\Thread')->make();
$this->post('/thread', $thread->toArray());
$this->get('/thread/'.$thread->id)
->assertSee($thread->title)
->assertSee($thread->body);
}
テストを実行すると、問題なく通りました。
登録ページをつくる
さて、なんとなくThreadを登録する機能ができあがってきたので、画面をつくっていきます。
このへんは慣れてきたのでさくっといきます。
ルーティングを書いて、
web.php
<?php
Route::get('/thread', 'ThreadController@index');
Route::get('/thread/create', 'ThreadController@create');
Route::post('/thread', 'ThreadController@store');
Route::get('/thread/{thread}', 'ThreadController@show');
コントローラーに定義を追加して
ThreadController.php(抜粋)
<?php
public function create()
{
return view('thread.create');
}
画面を作成しよう。
スタイルをいろいろあてているけれども、scssは省略しちゃうよ。
需要はまったくないだろうけれどもgithubに上げておくよ!
create.blade.php
@extends('layouts.app')
@section('content')
<div class="container justfy-center">
<form class="form" action="/thread" method="post">
{{ csrf_field() }}
<h2 class="form-header">何かを投稿しよう</h2>
<div class="form-group">
<label for="title">タイトル</label>
<input type="text" name="title">
</div>
<div class="form-group">
<label for="body">本文</label>
<textarea name="body" rows="10" cols="50"></textarea>
</div>
<div class="form-group">
<label for="user_id">ユーザーID</label>
<input type="text" name="user_id" placeholder="とりあえずの実装">
</div>
<div class="form-group">
<button>投稿する</button>
</div>
</form>
</div>
@endsection
こんな感じの画面になりました。
ユーザーIDについては、ログイン機能をつくったらつくった人のIDが入ってくるイメージになりますが、今は適当に入力します。
試しに投稿してみると、問題なく投稿できることが確認できました。
バリデーションを作成する
現在の状態で、タイトルなどを空っぽにして投稿すると、タイトルにnullはだめだよ!というエラーでExceptionが発生してしまいます。
なので、入力が必須の項目が未入力の場合は、入力してね!ってメッセージを出してあげましょう。
なにはともあれはテストコードを先に書きます。
ThreadTest.php
<?php
@return
public function test_thread_require_title()
{
$thread = factory('App\Thread')->make(['title' => null]);
$this->post('/thread', $thread->toArray())
->assertSessionHasErrors('title');
}
ここで使うのはassertSessionHasErrors
になります。
バリデーションに失敗してエラーになったかどうかの判断が、なんでセッションなんだろう、と思いませんか。
気になったので調べてみました。
ではやって来たリクエストの入力が指定したバリデーションルールに当てはまらなかった場合はどうなるんでしょう? 既に説明した通り、Laravelは自動的にユーザーを以前のページヘリダイレクトします。付け加えて、バリデーションエラーは全部自動的にフラッシュデータとしてセッションへ保存されます。
GETルートのビューへエラーメッセージを明示的に結合する必要がないことに注目してください。これはつまり、Laravelはいつもセッションデータの中にエラーの存在をチェックしており、見つけた場合は自動的に結合しているからです。
公式のバリデーションには上記のことが書いてあります。
つまり、バリデーションエラーの結果はセッションに書いていて、セッションにそのエラーが書かれていれば$errorsという変数に突っ込んでおくから、View(blade)で使ってね、ということみたいです。
なるほどね!
実装
ということでバリデーションの実装をしていきます。
バリデーションもコントローラーではなくって、フォームリクエストバリデーションというものを利用してみます。
artisanで雛形を作成して、
フォームリクエストの作成
php artisan make:request ThreadRequest
フォームリクエストをこんな感じで書きます。
ThreadRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ThreadRequest extends FormRequest
{
@return
public function authorize()
{
return true;
}
@return
public function rules()
{
return [
'title' => 'required',
'body' => 'required',
];
}
public function messages()
{
return [
'title.required' => 'タイトルが未入力だよ',
'body.required' => '本文が未入力だよ',
];
}
}
コントローラーも、ThreadRequestを使うようにしてあげます。
ThreadController.php(抜粋)
use App\Http\Requests\ThreadRequest;
public function store(ThreadRequest $request, Thread $thread)
{
$thread->create($request->all());
return redirect('/thread');
}
次回は登録ページにエラーを表示していきたいと思います。
ここまでの実装
github.com