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_tablemigrationファイルが作成されましたので、以下のように修正しました。(ファイル名は現在時刻)
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 ScheduleControllerweb.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 devViteの場合で、本番用にJS/CSSを出力する場合は、以下のコマンドを実行します。
$ sail npm run buildViteについては以下の記事にもまとめました。

これで登録処理の流れができました。実際に試してみましょう。
以下の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();
    }
}
 
                    
コメントを残す
コメントを投稿するにはログインしてください。