環境
Laravel 6.5.0
Homestead
使っているライブラリ
→flatpickr(日付)、datetimepicker(時間)、moment.js、holiday_jp.js
作成しているもの
画像のようなある施設の利用予約を申請するシステムについて、
決定ボタンが押されるとテーブルからその日の予約情報を取得して、それを参照しDatetimepickerの時間選択で予約が入っている時間は利用開始・利用終了時刻から選択できないようになる。
また、実際は決定ボタンを押したあとに、利用時刻の選択とフォームの全体の情報をPOSTするSubmitボタンが出現し、それを押すと確認画面が表示され訂正か確定かを選択させるといった処理になります。
また、今回の場合は9時13時、13時14時、15時~17時で予約が入っていることが前提条件となっています。
質問したいこと
フォームの仕様は以下の通りです。
このようなコードを書いて冒頭の画像の利用開始時刻と、利用終了時刻の選択内容をバインドしています。
ですが、例えばこの場合利用開始に14:00、利用終了に18:00を選択して14:00~18:00の時間帯を予約したいというリクエストができてしまいます。
当然それでは前述の前提条件とバッティングしてしまうのでその場合、確認画面に遷移せずフォームにリダイレクトしてエラーメッセージを表示させたいのですがこの場合バリデーションでやるのか自分で独自例外を作成してtry-catchでその例外に投げるのかどちらにするのかがわからないのでお聞きしたいです。
2020/01/10
コメント欄でご指摘いただいた箇所を修正、かつ自分なりにエラーハンドリングをしてみたのでControllerにそれを反映。
以下コード他です。
AjaxでPHPから帰ってくる予約情報
ちなみにid3は2019-11-30とは別の日付の予約情報なので弾かれています。
{ "id": "1", "start_time": "2019-11-30 09:00:00", "end_time": "2019-11-30 13:00:00" }, { "id": "2", "start_time": "2019-11-30 15:00:00", "end_time": "2019-11-30 17:00:00" }, { "id": "4", "start_time": "2019-11-30 13:00:00", "end_time": "2019-11-30 14:00:00" }
バリデーション
とりあえず基本的なところだけ。rulesメソッドのみ抜粋。
php
1<?php 2 3namespace App\Http\Requests; 4 5use Illuminate\Foundation\Http\FormRequest; 6// use App\Http\Requests\Request; 7 8class CreateReserveRequest extends FormRequest 9{ 10 11 public function rules() 12 { 13 return [ 14 'facility_name' => 'not_in:0', 15 'dateinfo' => 'required|date', 16 'start_time' => 'required|date_format:H:i', 17 'end_time' => 'required|date_format:H:i', 18 ]; 19 } 20} 21 22
model
php
1<?php 2 3namespace App; 4 5use Illuminate\Database\Eloquent\Model; 6 7class Reserve extends Model { 8 public $incrementing = false; 9 10 protected $fillable = [ 11 'user_id', 12 'facility_id', 13 'start_time', 14 'end_time', 15 16 ]; 17 18 public function user() { 19 return $this->belongsTo('App\User'); 20 } 21 22 public function facility() { 23 return $this->belongsTo('App\Facility'); 24 } 25 26 static function SearchReserveDates($facility_name, $dateinfo) { 27 28 $result = \App\Reserve::whereHas('Facility', function($query) use($facility_name, $dateinfo) { 29 $query 30 ->where('facility_name',$facility_name) 31 ->whereDate('start_time',$dateinfo) 32 ->whereDate('end_time',$dateinfo); 33 })->select('id', 'start_time', 'end_time')->get()->all(); 34 return $result; 35 } 36}
Controller(自力で予約確定まで組んでみたものです)
php
1<?php 2 3class ReserveController extends Controller { 4 5 // フォームから受け取った情報をもとにテーブルを検索、施設名から施設IDを引っ張り、日付と合わせて予約情報を取得しAjaxに返す。 6 public function searchReservation(Request $request) { 7 8 $data = $request->all(); 9 10 if(isset($data['dateinfo']) && isset($data['facility_name'])) { 11 12 $dateinfo = $data['dateinfo']; 13 $facility_name = $data['facility_name']; 14 15 $reserveinfo = Reserve::SearchReserveDates($facility_name, $dateinfo); 16 17 \Debugbar::info(); 18 19 return json_encode($reserveinfo, JSON_PRETTY_PRINT); 20 21 } else { 22 echo 'FAIL TO AJAX REQUEST'; 23 } 24} 25 26 // 入力確認画面に最終的なフォームの値を渡す。Requestにはバリデーションの拡張クラスを渡す。 27 public function confirm(CreateReserveRequest $request) { 28 29 // 予約情報取得 30 31 $data = $request->all(); 32 33 if(isset($data['dateinfo']) && isset($data['facility_name'])) { 34 35 $dateinfo = $data['dateinfo']; 36 $facility_name = $data['facility_name']; 37 38 $reserveinfo = Reserve::SearchReserveDates($facility_name, $dateinfo); 39 } 40 41 42 // 取得したレコードを配列の形にする。 43 // start_timeとend_timeそれぞれの値で配列を作る。 44 45 $arr_start_time = array_column($reserveinfo, 'start_time'); 46 // var_dump($arr_start_time); 47 $arr_end_time = array_column($reserveinfo, 'end_time'); 48 // var_dump($arr_end_time); 49 50 // 選択された時間帯が予約時間と重複していないか検証 51 if(isset($data['start_time']) && isset($data['end_time'])) { 52 53 // datetime型に整形 54 $start_datetime =$data['dateinfo'] .' '. $data['start_time']; 55 $end_datetime =$data['dateinfo'] .' '. $data['end_time']; 56 57 // Carbonに整形 58 59 $st = new Carbon($start_datetime); 60 $start = $st->format('Y-m-d H:i:s'); 61 // var_dump($start); 62 $ed = new Carbon($end_datetime); 63 $end = $ed->format('Y-m-d H:i:s'); 64 // var_dump($end); 65 // テーブルから予約情報取得して配列に格納 66 // それぞれ$arr_start_time[]と$arr_end_times[]で呼び出せるようにする 67 // forかwhile文で$start_time[n]と$end_time[n]まで検証する 68 69 // $arr_start_time及び$arr_end_timeの配列の数を取得する。 70 $c1 = count($arr_start_time); 71 $c2 = count($arr_end_time); 72 73 74 // 時間帯比較の関数 75 function isTimeDuplication($start, $end, $start_time, $end_time) { 76 return ($start < $end_time && $start_time < $end); 77 } 78 // try-catchで例外が出たら入力フォームまでロールバックする。 79 80 try { 81 82 for ($i=0; $i < $c1 && $c2 ; $i++) { 83 84 $start_time = $arr_start_time[$i]; 85 $end_time = $arr_end_time[$i]; 86 87 $result = isTimeDuplication($start, $end, $start_time, $end_time); 88 89 if($result === TRUE) { 90 throw new ReserveDuplicationException; 91 } 92 } 93 94 // 入力確認ページのviewにdataを渡す 95 return view('reserve-confirm', [ 96 'data' => $data, 97 ]); 98 99 } catch(ReserveDuplicationException $e) { 100 throw $e; 101 } 102 103 104 } 105} 106 107 // 予約完了メールの発送 108 public function send(Request $request) { 109 110 //フォームから受け取ったactionの値を取得 111 $action = $request->input('action'); 112 $inputs = $request->except('action'); 113 114 //actionの値で分岐 115 if($action !== 'submit'){ 116 return redirect() 117 ->route('reserve.index') 118 ->withInput($inputs); 119 120 } else { 121 $user = Auth::user(); 122 $email = $user->email; 123 \Mail::to($email)->send(new ContactSendmail($inputs)); 124 125 // トークンを再発行して再送信防止 126 127 $request->session()->regenerateToken(); 128 129 } 130 131 } 132 133 134// 予約確定による、データベースへの情報の追加 135 136 public function store(Request $request) { 137 138 // バリデーション 139 140 $request->validate([ 141 'facility_name' => 'required', 142 'dateinfo' => 'required', 143 'start_time' => 'required', 144 'end_time' => 'required', 145 ]); 146 147 148 // 再度ダブルブッキングのチェックを行う。 149 $this->confirm($request); 150 151 // 以下、ダブルブッキングなしの場合の更新処理 152 153 //フォームから受け取ったactionを除いたinputの値を取得 154 $inputs = $request->except('action'); 155 156 // 検索用にfacility_nameをのみ別に変数に取り出しておく 157 158 $facility_name = $inputs['facility_name']; 159 160 // facility_idを抽出するメソッド 161 162 $facility_id = Facility::SearchFacility_id($facility_name); 163 164 165 // dateinfoとstart_time及びend_timeを組み合わせてdatetime型にする。 166 $start_time =$inputs['dateinfo'] .' '. $inputs['start_time']; 167 $end_time =$inputs['dateinfo'] .' '. $inputs['end_time']; 168 169 // 予約番号生成 170 171 $reserve_number = uniqid(bin2hex(random_bytes((1)))); 172 173 // ユーザー情報取得 174 $user = Auth::user(); 175 $user_id = $user->id; 176 177 // データベースに追加 178 $reserve = new Reserve(); 179 $reserve->user_id = $user_id; 180 $reserve->facility_id = $facility_id; 181 $reserve->start_time = $start_time; 182 $reserve->end_time = $end_time; 183 $reserve->reserve_number = $reserve_number; 184 $reserve->save(); 185 186 \Debugbar::info(); 187 188 $this->send($request); 189 190 // viewへ遷移 191 return view('reserve-complete' ,[ 192 'reserve_number' => $reserve_number, 193 ]); 194 } 195
質問に際してやったこと・作ったもの
追記
####Handler.php
php
1 2 public function prepareResponse($request, Exception $e) { 3 // 競合違反を条件分岐 4 if($e instanceof ConflictHttpException) { 5 return $this->invaildHttpRequest($request, $e); 6 } 7 // 予約時間が競合していた場合エラーハンドリング 8 if($e instanceof ReserveDuplicationException) { 9 return redirect()->back()->withInput()->withErrors('その時間帯はすでに予約が入っています'); 10 } 11 12 return parent::prepareResponse($request, $e); 13 } 14
回答1件
あなたの回答
tips
プレビュー