今参加しているプログラミングスクールでの課題で、LaravelでECsiteを作っているのですが、ユーザー情報を登録する際に、validationを非同期通信で行ったら便利そうということで導入します
作りたいもの
- ECsite(の一部)
- ユーザー情報登録画面で未入力等の項目があった場合にvalidationで弾く
- そのvalidationを非同期で行う(エラーが出たら画面遷移をせずにエラー文を表示)
- エラーがなかったら、入力内容確認画面へ自動遷移
非同期通信とは?
以下の記事がとてもわかりやすかったです。
初心者目線でAjaxの説明
なんとなくわかった気にはなった...
何を使う?
jQueryのajax関数を使うのが簡単でいいらしいです
はじめてのAjax(jQuery) 2018年版
早速作成
まずはひな形を作成
フォームの作り方は省略で、以下のものができているとします
@extends('common.layout')
@section('title')
ユーザー新規登録
@endsection
@section('header')
@endsection
@section('bodyTitle')
<h2>新規登録画面</h2>
@endsection
@section('body')
<form action="user_confirm" method="post" id="form">
@csrf
名前(必須):<br />
<input type="text" name="user_name" size="50" value="{{old('user_name')}}" /><br /><br />
パスワード(必須):<br />
<input type="password" name="password" size="50" value="{{old('password')}}" /><br /><br />
Eメール(必須):<br />
<input type="text" name="email" size="50" value="{{old('email')}}" /><br /><br />
住所(必須):<br />
<input type="text" name="address" size="50" value="{{old('address')}}" /><br /><br />
<input type="submit" name="confirm" value="確認"/>
</form>
@endsection
@extends('common.outerLayout')
@section('title')
ユーザー新規登録
@endsection
@section('bodyTitle')
<h2>確認画面</h2>
@endsection
@section('body')
<form action="user_complete" method="post">
@csrf
<table border="1">
<tr>
<td>名前</td>
<td>{{ $data['user_name'] }}</td>
</tr>
<tr>
<td>Eメールアドレス</td>
<td>{{ $data['email'] }}</td>
</tr>
<tr>
<td>パスワード</td>
<td>{{ $data['password'] }}</td>
</tr>
<tr>
<td>住所</td>
<td>{{ $data['address'] }}</td>
</tr>
</table>
<input type="submit" name="submit" value="送信" />
<input type="submit" name="submit" value="戻る" />
</form>
@endsection
<?php
Route::get('/user_regist', 'UserController@getUserRegist');
Route::post('/user_confirm', 'UserController@getUserConfirm');
Route::post('/user_complete', 'UserController@postUserComplete');
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\UserRegistRequest;
class UserController extends Controller
{
public function getUserRegist(){
return view('user_regist');
}
public function getUserConfirm(Request $request){
$data = $request->all();
return view('user_confirm', compact('data'));
}
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UserRegistRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'user_name' => 'required',
'email' => 'required',
'password' => 'required',
'address' => 'required'
];
}
public function messages()
{
return [
'user_name.required' => '名前を入力してください',
'email.required' => 'メールアドレスを入力してください',
'password.required' => 'パスワードを入力してください',
'address.required' => '住所を入力してください'//,
];
}
}
本当に必要なものしか書いていません。やっぱりフレームワーク使うと簡単にできていいですね。
ajax関数の利用
このままだと、入力画面で確認ボタンを押すとPOST通信が開始されてしまうので、ボタンのtype属性をsubmitから、ただのbutton属性にします。そして、このボタンが押されたときにイベントを発生させたいので、id属性を付与しておきます
<input type="button" name="confirm" value="確認" id="button"/>
次に、このボタンが押されたときに関数が実行されるように、JSを記述します。
<script type="text/javascript">
$('#button').click(function(event) {
//ここに行いたい処理を記述
});
</script>
次に処理の内容を書いていきます。
まずは入力された値をJS内の変数に確保しておきます。この時、入力内容の取得はjQueryのval関数を使えば、対象要素のvalueを取得できます。
jQueryはダウンロードしてもよいですが、面倒なのでwebから直接読み込みます。
@section('header')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
@endsection
//省略
<script type="text/javascript">
$('#button').click(function(event) {
var user_name = $('input[name="user_name"]').val();
var password = $('input[name="password"]').val();
var email = $('input[name="email"]').val();
var address = $('input[name="address"]').val();
var data = {'user_name': user_name,
'password': password,
'email': email,
'address': address};
});
</script>
valを使うセレクタは、’タグ名[属性名=”属性値”]’ で指定しました。
また、iQueryのajaxによってJSON方式でデータを渡すには、各々のデータを連想配列で渡さないとなので、これをdata変数に作っておきます。
そしていよいよajaxを書いていきます。まずはheaderにCSRFトークンの設定を書きつつ、ajaxSetup関数でajax通信の設定を行います
@section('header')
<meta name="csrf-token" content="{{ csrf_token() }}"> //追加
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
@endsection
//省略
<script type="text/javascript">
$('#button').click(function(event) {
//省略
$.ajaxSetup({
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')}
});
});
</script>
CRSFはちょっとまだ勉強不足です。。。
次にajax関数を書いていきます
@section('body')
<ul id="error_message"> //追加
</ul> //追加
//省略
<form action="user_confirm" method="post" id="form">
<script type="text/javascript">
$('#button').click(function(event) {
//省略
$.ajax({
url: 'user_confirm',
type: 'POST',
dataType: "json",
data: data,
}).done(function (results) {
//通信が成功したときの処理
console.log(results);
}).fail(function (jqXHR, textStatus, errorThrown) {
//通信が失敗したときの処理
$('#error_message').empty();
var text = $.parseJSON(jqXHR.responseText);
var errors = text.errors;
for (key in errors) {
var errorMessage = errors[key][0];
$('#error_message').append(`<li>${errorMessage}</li>`);
}
});
});
</script>
まずはurlで送信先のアドレスを指定します。送信方式(type)はPOSTで行います。dataTypeはjsonで、送るデータ(data)は先ほど作成したdata変数を送ります。
そして、doneとfailの中に通信後の処理を書きます。成功時は取り敢えず、console.logで結果を表示するようにします。
失敗時は箇条書き形式でメッセージを表示します。ulタグにid値をつけておいて、jQueryで指定できるようにします。
エラー内容がうまくいかないとき
次に、エラー内容がうまくいかないときは、フォームリクエストに以下を追加して、エラー内容をJSON形式で返すようにします(最初はうまくいかなかったのに、これを書いているときに再現しようとしたら、エラーがでてこなかった。。。)。
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator; //追加
use Illuminate\Http\Exceptions\HttpResponseException; //追加
//省略
protected function failedValidation( Validator $validator )
{
$response['data'] = [];
$response['status'] = 'NG';
$response['summary'] = 'Failed validation.';
$response['errors'] = $validator->errors()->toArray();
throw new HttpResponseException(
response()->json( $response, 422 )
);
}
成功したときの処理をどうするか?
次にvalidationを通るように入力して送信しますが...
と、エラーが返ってきて、画面遷移をしてくれません。
これは恐らく、成功時に返している内容が
public function getUserConfirm(Request $request){
$data = $request->all();
return view('user_confirm', compact('data'));
}
と、JSON形式のファイルではなく、viewが返っているからだと思います。
そこで、JSONのresponseをするようにcontrollerを変えますが、getUserConfirmのreturnを変えるとページ遷移ができなくなってしまうので、
- user_regist.blade -> postUserValidation へJSONで送る
- 結果を user_regist.blade へ送る
- エラーが無かったら user_confirm.blade へ遷移する
これを実装していきます。
Route::get('/user_regist', 'UserController@getUserRegist');
Route::post('/user_validation', 'UserController@postUserValidation'); //追加
Route::get('/user_confirm', 'UserController@getUserConfirm'); //変更
Route::post('/user_complete', 'UserController@postUserComplete');
public function getUserRegist(){
return view('user_regist');
}
public function postUserValidation(UserRegistRequest $request){
$data = $request->all();
$request->session()->put('data', $data);
return response()->json([
'data'=> $data
]);
}
public function getUserConfirm(Request $request){
$data = $request->session()->get('data');
return view('user_confirm', compact('data'));
}
//省略
$.ajax({
url: 'user_validation', //変更
type: 'POST',
dataType: "json",
data: data,
}).done(function (results) {
//console.log(results)
window.location.href = 'user_confirm'; //'user_confirm'へ遷移
}).fail(function (jqXHR, textStatus, errorThrown) {
これで無事送れました!
反省
この方法だとsessionに入れなくてはいけないので、本当は直接遷移させたかったが、いろいろ調べても分からなかった...
参考にしたサイト
Laravel学習帳 Ajax入門
【Laravel5】FormRequestのバリデーション結果をJSON APIで返す