環境
Laravel 6.5.0
Homestead
使っているライブラリ
→flatpickr(日付)、datetimepicker(時間)、moment.js、holiday_jp.js
やろうとしていること
画像のようなある施設の利用予約を申請するシステムについて、
決定ボタンが押されるとテーブルからその日の予約情報を取得して、それを参照しDatetimepickerの時間選択で予約が入っている時間は利用開始・利用終了時刻から選択できないようにしたい。
今回の場合は9時13時と15時17時で予約が入っていることを前提として、利用開始時刻は絞り込めている状態です。
また、実際は決定ボタンを押したあとに、時間の選択フォームとフォームの全体の情報をPOSTするSubmitボタンが出現するという仕様になります。
質問したいこと
以前に
Javascriptにおいて配列の中の時間の最大値や最小値、及び間の時刻を取得したい。
という質問をして、その中で回答者様のご教授に従い、ユーザーが利用したい施設と利用したい日付をフォームから送信すると、Ajaxで非同期通信を行い、既にテーブルにある予約情報を取得してそこから指定した施設・利用日において予約できる時間帯をユーザーに提示するにはどうすればよいのかということについて、以下のようなdatetimepickerのallowTimes(選択の候補に含める時間の一覧)を求めるのに
javascript
1jQuery('#datetimepicker5').datetimepicker({ 2 datepicker:false, 3 allowTimes:[ 4 '12:00', '13:00', '15:00', 5 '17:00', '17:05', '17:20', '19:00', '20:00' 6 ] 7});
javascript
1 2// momentのフォーマット文字列を定数にしておく 3 const FORMAT_DATE = "YYYY-MM-DD"; 4 const FORMAT_DATETIME = "YYYY-MM-DD HH:mm:ss"; 5 const FORMAT_TIME = "HH:mm:ss"; 6 7 // ジェネレータ(関数)を定義しておく 8 // 今回はある時間とある時間の間にある日時を開始は含み、終了は含まないように1時間ごとに取得する。 9 10 function* range({start, end}) { // 引数はstartとendプロパティを持つオブジェクトである。 11 let m = start; // 変数にstartプロパティを設定する。 12 while (m.isBefore(end)) { // 実行する関数を定義。momentのisBeforeメソッドを使って、mに設定したmomentプロパティをendと比較し続け、m<endである限り以下の処理を実行する。 13 yield m; // 一度関数の処理を止めたあとmをreturnする。 14 m = moment(m).add(1, 'hour'); // mに1時間加算する。whileまで戻る。 15 } 16 } 17 18 19 // Ajaxで受け取ったJsonデータをパースする 20 21 const reservedata = JSON.parse(data).map(e => ({ 22 start: moment(e.start_time, FORMAT_DATETIME), 23 end: moment(e.end_time, FORMAT_DATETIME) 24 })); 25 26 console.log(reservedata); 27 28 // 日付部分の文字列の取得 29 30 const dateStr = reservedata[0].start.format(FORMAT_DATE); // dataで得られた時間からその日の日付をだけ抜き出す。 31 32 // 営業時間の定義。本来は営業時間テーブルから引っ張ってくるが今回は直接momentの値を持つオブジェクトとして定義する。 33 34 const business_hours = { 35 start: moment(`${dateStr} 09:00:00`, FORMAT_DATETIME), 36 end: moment(`${dateStr} 19:00:00`, FORMAT_DATETIME) 37 }; 38 // filterを用いて、営業時間帯に含まれる時間の中で選択可能な時間に適用ものだけを取得する 39 40 const selecttablesStartMoments = [...range(business_hours)].filter(m => 41 // [...range()]はスプレット構文といい、iterableオブジェクトをこれで展開することができる。range関数はジェネレーターでジェネレーターはiterableであるのでこの表記で実行することができる。 42 reservedata.every(e => m.isBefore(e.start) || m.isSameOrAfter(e.end)) // everyメソッドに与えられた引数の関数が配列すべてに適応されるかチェック。 43 44 // つまり、filterで絞る条件はrangeで取得されていくmがdata.every()に適応されるかということになる。 45 ); 46 47 const selectStarttime = selecttablesStartMoments.map(m => m.format(FORMAT_TIME)); // mapで新たにfilterの結果を配列にする。m.format()を実行するようにすることでmomentの値の配列にできる。 48 49 console.log(selectStarttime); 50
このようなコードを書いて以下の通りのような結果を得て、利用開始時刻のallowTimesに関しては無事に絞り込めました。
javascript
1 2console.log(selectStarttime); // => ["13:00", "14:00", "17:00", "18:00"] 3 4
よって、今度は利用終了時刻の絞り込みとして利用開始時刻に対応して今回の場合だと
javascript
1console.log(selectEndtime); // => ["14:00:00","15:00:00", "18:00:00", "19:00:00"] 2
という結果を得たいと考えて利用開始時刻の考えを応用して以下のようなコードを書き、一応期待している結果にはなったのですが、それが正しいアルゴリズムなのか、穴があるのかアドバイスいただきたいと思って質問いたしました。
コードはこちらです。
javascript
1 2// $end_timeのallowTimesに設定する時間を取得する 3 4 function* range1({start, end}) { // 引数はstartとendプロパティを持つオブジェクトである。 5 let m = start; // 変数にstartプロパティを設定する。 6 while (m.isBefore(end)) { // 実行する関数を定義。momentのisBeforeメソッドを使って、mに設定したmomentプロパティをendと比較し続け、m<endである限り以下の処理を実行する。 7 yield m = moment(m).add(1, 'hour'); // rangeとは違い今度は開始を含みたくない。 8 } 9 } 10 const selecttablesEndMoments = [...range1(business_hours)].filter(m => 11 reservedata.every(e => m.isSameOrBefore(e.start)|| m.isAfter(e.end)) 12 ); 13 const selectEndtime = selecttablesEndMoments.map(m => m.format(FORMAT_TIME)); 14 15 console.log(selectEndtime); 16 17
出力結果は以下のように期待しているものが出ました。
javascript
1console.log(selectEndtime); // => ["14:00:00","15:00:00", "18:00:00", "19:00:00"] 2
試しに後述している予約情報に比べて予約情報を以下のように増やしてみて実行しても
{ "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" }
以下のようにやはり一応期待している結果は出ています。
javascript
1console.log(selectEndtime); // => ["15:00:00", "18:00:00", "19:00:00"] 2 3
なお、15時~17時で予約が入っているところで
このような選択がなされた場合についてはLaravelでバリデーションを行うことを考えています。
質問に際してやったこと・作ったもの
あなたの回答
tips
プレビュー