Laravel+jQuery+fullCalendarで予定を非同期で登録する

先日投稿した記事と被っている箇所がありますがあしからず。

【技術スタック】
- Laravel 6.0
- AdminLTE 3 (ComposerでLaravelにインストール)
- jQuery 3.3.1+ (AdminLTEにデフォルトで入っている)
- FullCalendar 5.3.2 (AdminLTEでデフォルトで入ってるのは古いので別途新しいバージョンをCDNで導入)

## FullCalendar概要
オープンソースのカスタマイズ可能なjQueryのカレンダーライブラリ。

公式ドキュメント：https://fullcalendar.io/docs

※FullCalenderのバージョンによってFullCalendarのコードの書き方がかなり変わるようなので、注意が必要です。
ver.3まではjQueryの記述方法でカレンダーを描画していましたが、ver.4以降は生のjavascriptでの記述方法に変わりました。
> - DOMの取得方法がネイティブのgetElementByIdになった
> - カレンダーの生成方法が new FullCalendar.Calendar() になった

[JavaScript カレンダープラグイン FullCalendar ～その１ スケジュール表を作成する～ | 打ち聞かせ](https://kazunori-toybox.com/blog/js/fullcalendar-at-jquery-unused-1/) 様より引用

## 全体の大まかな流れ

1. モデル、テーブル作成しておく(FullCalendarのイベントに対応するカラム名にする)
2. フォームに情報を入力してボタンをクリックすると、jQuery側でクリックを検知&フォームの情報を取得
3. Ajaxを使い、2で取得した情報をPOSTでコントローラー側に送信
4. コントローラー側で送信された情報をsave

基本の書き方が下記。

```js

var calendar = new Calendar(calendarEl, {
  timeZone: 'UTC',
  events: [
    {
      id: 'a',
      title: 'testEvent',
      start: '2020-11-01'
    }
  ]
})
```
https://fullcalendar.io/docs/event-parsing に記載のイベントオブジェクト一覧で、全てのイベントに関連するプロパティを見ることができます。
下記一部抜粋です。

| プロパティ名 | 概要                                                                                         | 
| ------------ | -------------------------------------------------------------------------------------------- | 
| id           | 文字列または整数。イベントを一意に識別します。getEventByIdに便利です。<br>                   | 
| start	<br>   | イベントの開始時刻。イベントが明示的に allDay である場合、時、分、秒、ミリ秒は無視されます。 | 
| title	<br>   | イベントに表示されるテキスト。                                                               | 
| color	<br>   | backgroundColorとborderColorを同時に指定するためのエイリアス。<br>                           | 

上記の記述方法だと、 「testEventというイベント名が2020/11/1の日付に表示」されます。
今回は、 events の中身を、DBの値に置き換えていきたいと思います。

## 1. モデル、テーブル作成しておく(FullCalendarのeventプロパティに対応するカラム名にする)

```
$ php artisan make:model Event -m
```
で、モデル作成&マイグレーションファイルを作成します。
マイグレーションファイルを下記のように編集

```php

// create_events_table.php
    public function up()
    {
        Schema::create('events', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->date('start');
            $table->date('end')->nullable();
            $table->string('title');
            $table->string('color')->nullable();
        });
    }
```

```
$ php artisan migrate
```
でマイグレーションをかけてeventsテーブルを作成します。

## 2. フォームに情報を入力してボタンをクリックすると、jQuery側でクリックを検知&フォームの情報を取得

```php
// test.blade.php
<div style="margin-top:30px; border:1px solid gray;">
  <form id="form" action="">
    <p><input type="text" name="title" placeholder="タイトル入力"></p>
    <p><input type="date" name="start" placeholder="日付入力"></p>
    <p><input type="color" name="color" ></p>
  </form>

  <button id="bt2">
    Ajax->DBにPostテスト
  </button>

```
CSSはガン無視です(笑)
inputタグのname属性に、送信したい値のキーを書いてあげるのがポイントです。

```js
// scriptタグの中
$("#bt2").click(function () { // bt2のidの要素をクリックしたときに発動
  $.ajaxSetup({
    headers: {
      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
    },
  });
  var formData = $("#form").serialize(); 

  // ajax部分は後に記述 //
});
```

```js

  $.ajaxSetup({
    headers: {
      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
    },
  });
```
はおまじないです。これを設定することによって、セキュアなAjax通信を行うことができます。

`  var formData = $("#form").serialize(); `では、フォーム要素を取得して、**name属性とvalue属性をキーとvalueの形式に変換**します。
※ [jQuery serializeでキーバリューにするサンプル | ITSakura](https://itsakura.com/jquery-serialize) 様を参照

## 3. Ajaxを使い、2で取得した情報をPOSTでコントローラー側に送信

```js

$("#bt2").click(function () {
  $.ajaxSetup({
    headers: {
      "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
    },
  });
  var formData = $("#form").serialize();
  console.log(formData);

  $.ajax({
    //POST通信
    type: "post", //HTTP通信のメソッドをPOSTで指定
    //ここでデータの送信先URLを指定します。
    url: "/postevent", //通信先のURL
    dataType: "json", // データタイプをjsonで指定
    data: formData, // serializeしたデータを指定
  })
    //通信が成功したとき
    .then((res) => {
      console.log(res);
      // カレンダーの再描画
      var calendarEl = document.getElementById("calendar");
      var calendar = new FullCalendar.Calendar(calendarEl, {
        headerToolbar: {
          left: "prev,next today",
          center: "title",
          right: "dayGridMonth,timeGridWeek,timeGridDay,listWeek",
        },
        locale: "ja",
        editable: true,
        googleCalendarApiKey: "GoogleのAPIKEY",
        eventSources: [
          {
            googleCalendarId: "japanese__ja@holiday.calendar.google.com", //祝日の予定を取得
            rendering: "background",
            color: "#FF6666",
          },
        ],
        events: "/getevents",
        selectable: true,
      });
      calendar.render(); //カレンダーを再描画
    })
    //通信が失敗したとき
    .fail((error) => {
      console.log(error.statusText);
    });
});
```

## 4. コントローラー側で送信された情報をsave


```php
//web.php
Route::post('/postevent', '****Controller@postEvent'); //任意のコントローラー名を記述
```

```php
// コントローラー側
  public function postEvent(Request $request)
  {
    $event = new Event();
    $event->start = $request->start; 
    $event->title = $request->title;
    $event->color = $request->color;


    $event->save();
    return $event;
  }
```

これで下記のような形になりました。
Postテストを押すと・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499375/098e8143-d45e-9ba6-4844-6826a6181a54.png)


ページリロード無しでカレンダーだけが再描画されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499375/d64aa1ee-3cb2-bb36-3297-8ac0a610366e.png)

DBにもリアルタイムに画像が保存されていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/499375/e218179d-45c3-3fa0-6f26-dddc5559ffc7.png)