
【Laravel】FullCalendarでスケジュールのDB登録・表示【実践向け】
https://chigusa-web.com/blog/laravel-fullcalendar
目次
スポンサーリンク
はじめに
今回はFullCalendarを利用して、イベントを登録しカレンダーに表示する簡単なWebアプリケーションを作成してみます。
(スケジュールアプリの作成)
Laravelプロジェクトで作成し、イベントはデータベースに保存します。
そうすることにより、ブラウザを再度表示した際に、イベントを再表示することが出来ます。
Laravel Sailでの開発環境の構築方法は以下をご覧ください。
Windows

Mac

使用するバージョン
- Windows 11 or macOS Monterey (M1)
- Laravel Framework 8 or 9 or 10
- FullCalendar 6.1.4
2022/10/02 WindowsとMacを使い、最新バージョンで確認しました。
Vite版の対応を追加しました。
2023/03/11 Laravel10で確認しました。
他にも私のブログで、JavaScriptについて解説している記事がありますのでご覧ください。
関連

【Laravel】Vue.js v3導入とComposition API実装【Vite】
LaravelプロジェクトにVue3を導入し、Composition APIの実装を行いました。【VSCode/Dev Containers】DockerでNode.jsの開発・デバッグVS CodeでDockerを使ったリモート開発を試してみました。Dockerを使いNode環境を用意し、VS Codeでリモートデバッグを行います。【JavaScript】Base64 エンコード・デコード(UTF-8/SJIS)【js-base64】JavaScriptを使って、Base64エンコード・デコードを行います。【Laravel】Vue.js v3導入とComposition API実装【Mix】LaravelプロジェクトにVue3を導入し、Composition APIの実装を行いました。
スポンサーリンク
【紹介】個人開発
私の個人開発ですがQuiphaというサービスを開発しました。(Laravel, Vue3など)
良かったら、会員登録して動作を試してみて下さい。

また、Laravel 9 実践入門という書籍を出版しました。
Kindle Unlimitedを契約している方であれば、読み放題で無料でご覧いただくことができます。

是非多くの方に読んでいただき、Laravelの開発に少しでもお役に立てたら幸いです。
余談ですが、Django向けに解説した記事もございますので参考にしてください。

FullCalendarとは
FullCalendarのインストール方法や、実装は以下の記事をご覧ください。
本記事は、以下の記事の続きになります。

VS Codeの用意
VS Codeのインストール方法は、以下の記事にまとめましたのでご覧ください。

設定方法は以下を参考にしてください。

イベントの登録機能の実装
Laravelの登録処理の実装
前回の記事では、カレンダーにイベントを登録することが出来ました。
ただし、ブラウザの再描画を行うと、イベントはクリアされてしまいます。
ここからは実践でも使えるように、イベントをサーバーサイドで受信し、データベースに登録する処理をLaravelプロジェクトで実装していきます。
app.phpの修正
日付を操作しますので、Laravelのタイムゾーンの設定を変更してください。
'timezone' => 'Asia/Tokyo',
デフォルトでUTCがセットされており、変更しないと9時間ずれます。
テーブルの用意
以下のコマンドでmigrationファイルを用意します。
イベントを格納するテーブルになります。
$ sail php artisan make:migration create_schedules_table
migrationファイルが作成されましたので、以下のように修正しました。(ファイル名は現在時刻)
database\migrations\2021_11_18_144113_create_schedules_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('schedules', function (Blueprint $table) {
$table->id();
$table->date('start_date')->comment('開始日');
$table->date('end_date')->comment('終了日');
$table->string('event_name')->comment('イベント名');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('schedules');
}
};
マイグレーションを実行し、テーブルを作成します。
$ sail php artisan migrate
モデルの作成
先ほど作成したテーブルに対するモデルクラスを作成します。コマンドを実行します。
$ sail php artisan make:model Schedule
クラスファイルが作成されますが、特に変更の必要はありません。
コントローラの作成
以下のコマンドでコントローラを作成します。
$ sail php artisan make:controller ScheduleController
web.phpを修正し、ルーティングを追加します。
use App\Http\Controllers\ScheduleController;
// イベント登録処理
Route::post('/schedule-add', [ScheduleController::class, 'scheduleAdd'])->name('schedule-add');
イベントを登録するための処理を追加します。
リクエストのバリデーションと、DBへの登録処理を追加しました。
イベント名は32文字の文字数制限としました。
App\Http\Controllers\ScheduleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Schedule;
class ScheduleController extends Controller
{
/**
* イベントを登録
*
* @param Request $request
*/
public function scheduleAdd(Request $request)
{
// バリデーション
$request->validate([
'start_date' => 'required|integer',
'end_date' => 'required|integer',
'event_name' => 'required|max:32',
]);
// 登録処理
$schedule = new Schedule;
// 日付に変換。JavaScriptのタイムスタンプはミリ秒なので秒に変換
$schedule->start_date = date('Y-m-d', $request->input('start_date') / 1000);
$schedule->end_date = date('Y-m-d', $request->input('end_date') / 1000);
$schedule->event_name = $request->input('event_name');
$schedule->save();
return;
}
}
今回は取り敢えず、コントローラに処理を全て実装しています。
入力チェックなども考慮しておりますので、十分実用的に使えると思いますが、お好みでフォームリクエストの作成や、ビジネスロジックの切り出しなどを行うと良いでしょう。
calendar.jsを開き、非同期通信を行うためのaxiosを追加します。
resources\js\calendar.js
import axios from 'axios';
さらにイベントを送信する処理を追加します。
resources\js\calendar.js
import { Calendar } from "@fullcalendar/core";
import interactionPlugin from "@fullcalendar/interaction";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import listPlugin from "@fullcalendar/list";
import axios from 'axios';
var calendarEl = document.getElementById("calendar");
let calendar = new Calendar(calendarEl, {
plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin],
initialView: "dayGridMonth",
headerToolbar: {
left: "prev,next today",
center: "title",
right: "dayGridMonth,timeGridWeek,listWeek",
},
locale: "ja",
// 日付をクリック、または範囲を選択したイベント
selectable: true,
select: function (info) {
//alert("selected " + info.startStr + " to " + info.endStr);
// 入力ダイアログ
const eventName = prompt("イベントを入力してください");
if (eventName) {
// Laravelの登録処理の呼び出し
axios
.post("/schedule-add", {
start_date: info.start.valueOf(),
end_date: info.end.valueOf(),
event_name: eventName,
})
.then(() => {
// イベントの追加
calendar.addEvent({
title: eventName,
start: info.start,
end: info.end,
allDay: true,
});
})
.catch(() => {
// バリデーションエラーなど
alert("登録に失敗しました");
});
}
},
});
calendar.render();
FullCalendarのプラグインの導入については、前回の記事を参考にしてください。
またソースの全容は、最後に掲載しています。
jsファイルをビルドします。開発時ですのでdevを指定しました。
(ViteでもMixでも同様)
$ sail npm run dev
Viteの場合で、本番用にJS/CSSを出力する場合は、以下のコマンドを実行します。
$ sail npm run build
Viteについては以下の記事にもまとめました。

これで登録処理の流れができました。実際に試してみましょう。
以下のURLにアクセスします。
http://localhost/calendar
プロンプトに任意のイベント名を入力します。

データベースに正しく登録されました。

エラー処理も確認してみましょう。
イベント名は32文字の文字数制限をかけましたので、32文字以上のイベント名を登録してみます。
失敗アラートが表示され、カレンダー上にイベントは表示されませんし、当然DBにも登録されません。

エラーメッセージは簡易的にしましたが、バリデーション結果を表示するのも良いでしょう。
今回は不正な値が登録されないように、バリデーション処理の実装を行いました。
スポンサーリンク
イベントの表示
イベントの登録が完了しました。今度はカレンダーの表示時に、データベースに登録されているイベントを表示します。
web.phpを修正し、イベントを取得するためのルーティングを追加します。
use App\Http\Controllers\ScheduleController;
// イベント登録処理
Route::post('/schedule-add', [ScheduleController::class, 'scheduleAdd'])->name('schedule-add');
// イベント取得処理
Route::post('/schedule-get', [ScheduleController::class, 'scheduleGet'])->name('schedule-get');
イベントを取得するための処理を追加します。
カレンダーが表示されている期間のみ、イベントを取得するようにします。
基本的にカレンダーは1ヶ月分表示されています。
表示されている期間のみのデータを返却するようにしましょう。
そうしないと、毎回全イベントを返却することになり、効率が悪いです。
App\Http\Controllers\ScheduleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Schedule;
class ScheduleController extends Controller
{
...省略
/**
* イベントを取得
*
* @param Request $request
*/
public function scheduleGet(Request $request)
{
// バリデーション
$request->validate([
'start_date' => 'required|integer',
'end_date' => 'required|integer'
]);
// カレンダー表示期間
$start_date = date('Y-m-d', $request->input('start_date') / 1000);
$end_date = date('Y-m-d', $request->input('end_date') / 1000);
// 登録処理
return Schedule::query()
->select(
// FullCalendarの形式に合わせる
'start_date as start',
'end_date as end',
'event_name as title'
)
// FullCalendarの表示範囲のみ表示
->where('end_date', '>', $start_date)
->where('start_date', '<', $end_date)
->get();
}
}
calendar.jsを開き、イベントを取得する処理を記述します。
「events」プロパティを追加します。
カレンダーが表示された時や、前の月・次の月のように表示が切り替わる度に呼ばれます。
resources\js\calendar.js
import { Calendar } from "@fullcalendar/core";
...省略
let calendar = new Calendar(calendarEl, {
plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin],
initialView: "dayGridMonth",
headerToolbar: {
left: "prev,next today",
center: "title",
right: "dayGridMonth,timeGridWeek,listWeek",
},
locale: "ja",
...省略
events: function (info, successCallback, failureCallback) {
// Laravelのイベント取得処理の呼び出し
axios
.post("/schedule-get", {
start_date: info.start.valueOf(),
end_date: info.end.valueOf(),
})
.then((response) => {
// 追加したイベントを削除
calendar.removeAllEvents();
// カレンダーに読み込み
successCallback(response.data);
})
.catch(() => {
// バリデーションエラーなど
alert("登録に失敗しました");
});
},
});
calendar.render();
これでプログラムは完成です。
イベントを登録してみましょう。DBにも格納されますので、ブラウザを閉じて再度カレンダーを表示してもイベントは表示されます。(永続化)

ソースの全容
resources\js\calendar.js
import { Calendar } from "@fullcalendar/core";
import interactionPlugin from "@fullcalendar/interaction";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import listPlugin from "@fullcalendar/list";
import axios from 'axios';
var calendarEl = document.getElementById("calendar");
let calendar = new Calendar(calendarEl, {
plugins: [interactionPlugin, dayGridPlugin, timeGridPlugin, listPlugin],
initialView: "dayGridMonth",
headerToolbar: {
left: "prev,next today",
center: "title",
right: "dayGridMonth,timeGridWeek,listWeek",
},
locale: "ja",
// 日付をクリック、または範囲を選択したイベント
selectable: true,
select: function (info) {
//alert("selected " + info.startStr + " to " + info.endStr);
// 入力ダイアログ
const eventName = prompt("イベントを入力してください");
if (eventName) {
// Laravelの登録処理の呼び出し
axios
.post("/schedule-add", {
start_date: info.start.valueOf(),
end_date: info.end.valueOf(),
event_name: eventName,
})
.then(() => {
// イベントの追加
calendar.addEvent({
title: eventName,
start: info.start,
end: info.end,
allDay: true,
});
})
.catch(() => {
// バリデーションエラーなど
alert("登録に失敗しました");
});
}
},
events: function (info, successCallback, failureCallback) {
// Laravelのイベント取得処理の呼び出し
axios
.post("/schedule-get", {
start_date: info.start.valueOf(),
end_date: info.end.valueOf(),
})
.then((response) => {
// 追加したイベントを削除
calendar.removeAllEvents();
// カレンダーに読み込み
successCallback(response.data);
})
.catch(() => {
// バリデーションエラーなど
alert("登録に失敗しました");
});
},
});
calendar.render();
App\Http\Controllers\ScheduleController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Schedule;
class ScheduleController extends Controller
{
/**
* イベントを登録
*
* @param Request $request
*/
public function scheduleAdd(Request $request)
{
// バリデーション
$request->validate([
'start_date' => 'required|integer',
'end_date' => 'required|integer',
'event_name' => 'required|max:32',
]);
// 登録処理
$schedule = new Schedule;
// 日付に変換。JavaScriptのタイムスタンプはミリ秒なので秒に変換
$schedule->start_date = date('Y-m-d', $request->input('start_date') / 1000);
$schedule->end_date = date('Y-m-d', $request->input('end_date') / 1000);
$schedule->event_name = $request->input('event_name');
$schedule->save();
return;
}
/**
* イベントを取得
*
* @param Request $request
*/
public function scheduleGet(Request $request)
{
// バリデーション
$request->validate([
'start_date' => 'required|integer',
'end_date' => 'required|integer'
]);
// カレンダー表示期間
$start_date = date('Y-m-d', $request->input('start_date') / 1000);
$end_date = date('Y-m-d', $request->input('end_date') / 1000);
// 登録処理
return Schedule::query()
->select(
// FullCalendarの形式に合わせる
'start_date as start',
'end_date as end',
'event_name as title'
)
// FullCalendarの表示範囲のみ表示
->where('end_date', '>', $start_date)
->where('start_date', '<', $end_date)
->get();
}
}
その他

