Laravelでajaxを使ってページ遷移しないでデータベースを更新する

# Laravelでajax通信をする
写真に添えられたチェックボックスをクリックすることでタグ付けができるアプリを作りました。
元々はLaravelでformをPOSTしていましたが、ajax通信にすることでページ遷移せずにformをPOSTできるようにしたかった。その作業メモです。

#### 1.まずはLaravelにjQueryをインストールする。
##### 1-1. jQueryをインストールする。Laravelのプロジェクトディレクトリにて下記実行。
```
$ npm install jquery --save
```
##### 1-2.resources/js/ディレクトリに、適当な.jsファイルを作って中に記述する。
※今回はajaxPost.jsを作った
![スクリーンショット 2022-10-04 23.08.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394908/fc6facf7-a2cc-1137-69a2-5040376542a1.png)
とりあえずちゃんと動作するか確認するために下記のコードを記述
```javascript
$(function(){
    console.log('run jQuery')
});
```
##### 1-3.webpack.mix.jsを編集する
```javascript
const mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/js/app.js', 'public/js')
    .js('resources/js/ajaxPost.js', 'public/js')  //追加。作成したajaxPost.jsファイルを、public/jsディレクトリにつなげる。
    .autoload( {"jquery": [ '$', 'window.jQuery' ],} ) //追加。jQueryを使えるようにするため
    .vue()
    .sass('resources/sass/app.scss', 'public/css');
```
##### 1-4.ビルドする
```
$ npm run dev
```
public/jsディレクトリにajaxPost.jsが表示される
![スクリーンショット 2022-10-04 23.15.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394908/324a7a69-bbfc-3ab8-d4e5-659fe9789779.png)
##### 1-5.ちゃんと動くかテストする
作ったjsファイルを読み込みたいBladeファイルにソースを貼る
```html
<script src="{{mix('js/ajaxPost.js')}}"></script>
```
このBladeファイルを開いたときにコンソールに`'run jQuery'`と表示されていればちゃんと読み込みできてます。
ここまで出来てれば、LaravelでjQueryが使えるようになったぜ。

#### 2.ajax通信
##### 元々のコード
マイグレーションファイル
```php:create_photos_table.php
    public function up()
    {
        Schema::create('photos', function (Blueprint $table) {
            $table->id();
            $table->string('path');//追記！！！
            $table->dateTime('date')->nullable();//追記
            $table->timestamps();
        });
    }
```
```php:create_players_table.php
    public function up()
    {
        Schema::create('players', function (Blueprint $table) {
            $table->id();
            $table->string('name_kanji');//追記
            $table->string('name_kana');//追記
            $table->string('nickname');//追記
            $table->integer('number');//追記
            $table->timestamps();
        });
    }
```
中間テーブル
```php:create_photo_player_table.php
    public function up()
    {
        Schema::create('photo_player', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('photo_id');
            $table->unsignedBigInteger('player_id');
            $table->foreign('photo_id')->references('id')->on('photos')->onDelete('cascade');
            $table->foreign('player_id')->references('id')->on('players')->onDelete('cascade');
            $table->timestamps();
        });
    }
```
モデル
```php:Photo.php
    public function Players()
    {
        return $this->belongsToMany('App\Models\Player')->withTimestamps();
    }
```
```php:Player.php
    public function Photos()
    {
        return $this->belongsToMany('App\Models\Photo')->withTimestamps();
    }
```
ビューファイル
```html:taggingAll.blade.php
                <form class=form id="form" action="{{ route('photo.update',$photo->id)}}" method="POST"> 
                        @csrf
                        @method('PUT')
                    <p>
                        @foreach ($players->sortBy('number')  as $player)
                            <label class="checkbox">
                                <input class="PlayerCheckBox" type="checkbox" name="players[]" value="{{$player->id}}"
                                @if(in_array($player->id , $photo->players->pluck('id')->toArray())) checked @endif> 
                                    <!-- player_idとphotoが持っているplayer_idが一致したらチェック -->
                                {{ $player->number }}{{ $player->nickname }}
                            </label>
                        @endforeach
                    </p>
                </form>
```
コントローラー
```php:PhotoController.php
    public function update(Request $request, $id)
    {
        $photo = Photo::find($id);
        $photo->players()->sync(request()->players);
        return back()->with('success', '編集完了しました');
    }
```
#### jsファイルの記述（ajax通信）
```javascript:ajaxPost.js
$( function (){

    $('.PlayerCheckBox').on('change', function(){  //checkboxをクリックした時に発動

            // let form = $('#form')  //これだと常に最初のformを指定してしまうのでだめ
            let form = $(this.form);  //クリックしたcheckboxを含むformをjqueryで取得
        
        $.ajax({
            headers: {
            "X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr("content"),
            },

            url     :   form.attr('action'),  //formのaction要素を参照
            type    :   form.attr('method'),  //formのmethod要素を参照
            data    :   form.serialize(),     //formで送信している内容を送る
        })

        //通信が成功した時
        .done((res)=>{
            //何か処理
        })
        //通信が失敗したとき
        .fail((error)=>{
            //何か処理
        })
    })
});
```
##### ↑のコードを書いた上でビルドする
```
$ npm run dev
```
public/js/ajaxPost.jsの内容が更新されていればビルドできている証拠

#### ビューファイルでの読み込み
```html:taggingAll.blade.php
<script src="{{asset('js/ajaxPost.js')}}"></script>
もしくは
<script src="{{mix('js/ajaxPost.js')}}"></script>
```
これを忘れているとせっかく書いたajax通信の記述が使われないのでお忘れなく。