googleカレンダーっぽいカレンダーを作れるfullcalendar.js。
予約受付やスケジュール管理などの場面で大活躍しています。
いわゆる「かゆいところに手が届く」ってヤツで、いろいろカスタマイズできるので、愛用してます。
最近、セラピスト1名のプライベートサロン様から
ホームページで予約を受けたい
とご相談いただき、fullcalendar.jsを使って対応する中で、
ダブルブッキングを防ぐ対策をしたので、
その方法をご紹介します。
なお、fullcalendar.js の使い方については、たくさんサンプルが出ていますので、そちらを参考にしてください。
ダブルブッキングが発生する場所を考える
fullcalendar.js の機能でダブルブッキングが発生する場所は
- タイムスロットを選択してイベントアイテムを追加するとき
- イベントアイテムをドラッグして、日時を変更するとき
- イベントアイテムのサイズを変更するとき
の3箇所。
この3箇所をイベント名で表すと
select、eventDrop、eventResize
になる。
この3つのイベントで、ダブルブッキングを判定して、重複しているときにキャンセルしてやれば良い。
$('#calendar').fullCalendar({ // タイムスロットを選択したとき select: function (startDate, endDate, jsEvent, view) { if(ダブルブッキングだったら) { alert('ダブルブッキング!'); $('#calendar').fullCalendar('unselect'); } }, // イベントアイテムを移動したとき eventDrop: function (event, delta, revertFunc) { if(ダブルブッキングだったら) { alert('ダブルブッキング!'); revertFunc(); } }, // イベントアイテムのサイズを変更したとき eventResize: function (event, delta, revertFunc, jsEvent, ui, view) { if(ダブルブッキングだったら) { alert('ダブルブッキング!'); revertFunc(); } } })
select の場合、タイムスロットが選択されているだけで、イベントアイテムには影響をおよぼしていないため、選択を解除するだけでよい。(6行目)
eventResizeとeventDropはイベントアイテムが変更されているので変更を取り消す必要がある。(14行目、22行目)
ダブルブッキングのパターンを考える
ダブルブッキングのパターンは以下の3つと考える。
- 既存イベントの開始~終了の間に、新たに追加したイベントアイテムの終了時間があるとき
- 既存イベントの開始~終了の間に、新たに追加したイベントアイテムの開始時間があるとき
- 新たに追加したイベントアイテムの開始~終了の間に既存イベントの開始時間(または終了時間)があるとき
なお、パターン4については、パターン1またはパターン2でフォローできるので、考えないものとする。
既存イベントの取得
既存のイベントアイテムは
var events = $('#calendar').fullCalendar('clientEvents',function (event) { return 条件; });
で取得することができる。
この「条件」を設定することで条件にあった既存のイベントアイテムを取得することができます。
例えば、前記したパターン1に該当する既存のイベントアイテムを抽出するには
var events = $('#calendar').fullCalendar('clientEvents',function (event) { return event.start <= 新たに追加したイベントアイテムの終了時間 && event.end >= 新たに追加したイベントアイテムの終了時間; });
となる。
取得した既存のイベントアイテムは、events変数に配列として入ってくる。
あとは、パターン1~3に該当するデータを取得して、1件以上ならダブルブッキングとして判定すればよい。
これで完成!?
これまでのロジックをまとめ、出来上がったコードは以下の通り。
$('#calendar').fullCalendar({ // タイムスロットを選択したとき select: function (startDate, endDate, jsEvent, view) { if(is_double(startDate, endDate)) { alert('ダブルブッキング!'); $('#calendar').fullCalendar('unselect'); } }, // イベントアイテムを移動したとき eventDrop: function (event, delta, revertFunc) { if(is_double(event.start, event.end)) { alert('ダブルブッキング!'); revertFunc(); } }, // イベントアイテムのサイズを変更したとき eventResize: function (event, delta, revertFunc, jsEvent, ui, view) { if(is_double(event.start, event.end)) { alert('ダブルブッキング!'); revertFunc(); } } }) function is_double(new_start, new_end){ var events = $('#calendar').fullCalendar('clientEvents',function (event) { return (event.start <= new_end && event.end >= new_end) || (event.start <= new_start && event.end >= new_start) || (event.start >= new_start && event.start <= new_end); } if (events.length > 0) { return true; } else { return false; } }
1つ、重大な欠陥がある。
新たにイベントアイテムを追加する場合は良いのだが、ドラッグして日時を変更するとき(eventDrop)とサイズを変えて日時を変更するとき(eventResize)が問題だ。
この2つは、既存のイベントアイテムを変更するので、変更前のイベントアイテムと変更後のイベントアイテムがダブルブッキングと判断されてしまう。
そこで「自イベントアイテム以外」の条件を付け加える。
$('#calendar').fullCalendar({ // タイムスロットを選択したとき select: function (startDate, endDate, jsEvent, view) { if(is_double(startDate, endDate, '')) { alert('ダブルブッキング!'); $('#calendar').fullCalendar('unselect'); } }, // イベントアイテムを移動したとき eventDrop: function (event, delta, revertFunc) { if(is_double(event.start, event.end, event._id)) { alert('ダブルブッキング!'); revertFunc(); } }, // イベントアイテムのサイズを変更したとき eventResize: function (event, delta, revertFunc, jsEvent, ui, view) { if(is_double(event.start, event.end, event._id)) { alert('ダブルブッキング!'); revertFunc(); } } }) function is_double(new_start, new_end, new_id){ var events = $('#calendar').fullCalendar('clientEvents',function (event) { return ((event.start <= new_end && event.end >= new_end) || (event.start <= new_start && event.end >= new_start) || (event.start >= new_start && event.start <= new_end )) && event._id != new_id; } if (events.length > 0) { return true; } else { return false; } }
これで完成!!!
ダブルブッキングを禁止する以外にも、条件の設定次第で
同時に3名まで接客できるから「3つまではOK」とかできそうですね。