ajaxを使って、非同期にmysqlへデータを保存しよう

# ajaxを使って、非同期にmysqlへデータを保存しよう

こんにちは@R_chibannuです。

今回は業務のなかで、ajaxを使ってmysqlにデータを保存する機会があったので、そちらの方法について紹介していきたいと思います。

なお、本記事は[All About Group（株式会社オールアバウト） Advent Calendar 2022](https://qiita.com/advent-calendar/2022/allabout)
の11日目の投稿です。

## ajaxとは

- Ajax(asynchronous  javascript xml)の略
- 通信の方法の一つであり、その方法が非同期にHTTPリクエストを行うというものである
- phpなどは同期通信でHTTPリクエストをしたときに、ページ全体のリロードが発生する
- 逆に、非同期通信を用いることで、HTTPリクエストしながら、ページのリロードを挟まない
- なので、ajaxの非同期通信を利用して、ページのリロードを挟まずに、シームレスな画面描画が可能になる

例えば、フォームに入力したデータを同期的にdbに保存する場合には、フォームの投稿ボタンを押したときに、画面のリロードが挟まれてしまいます。

これは、同期的にサーバーにHTTPリクエストを行い、レスポンスが返ってくるまで、画面が読み込まれないためです。

逆にajaxを使用すれば、非同期にリロードを挟まずに画面の表示や、dbへのデータの保存が可能になります。

## 実行環境

- macOS Monterey ver12.6
- Jquery 3.6.1
- Bootstrap 4.6.2
- laravel9
- mysql8

## ajaxでメッセージデータを、mysqlに保存してみよう

今回は以下のようなフォームでメッセージが入力でき、投稿ボタンを押すとmysqlにメッセージデータが非同期で保存される、簡単な例を作成していきましょう！
![Screen Shot 2022-12-06 at 10.41.44 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612164/24712163-5f88-9135-3466-9175a69d6d3c.png)

### ■migrationファイルの作成

まず初めにメッセージデータを保存するためのmigrationファイルを作成します。

以下のコマンドを実行します

```bash
php artisan make:migration create_messages_table
```

migrationファイルが作成されるので、up()の中身を修正します。

```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('messages', function (Blueprint $table) {
			// 追記
            $table->bigIncrements('id');
            $table->text('content');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('messages');
    }
};
```

migrationファイルを実行し、DBに反映させます

```bash
php artisan migrate
```

データベースを確認して、DBが反映されていることを確認します

![Screen Shot 2022-12-06 at 11.03.08 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612164/ad1f3b4f-e531-2c2d-8d53-af2ff95a2dcd.png)


migrationファイルに指定したカラムがDBに反映されていることが確認できましたね！

### ■ルーティングの作成

routes/web.phpに以下の記述をします。

```php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\MessageController;

Route::controller(MessageController::class)->group(function () {
    // メッセージ保存するためのApi
    Route::post('api/message/store', 'store');
    // メッセージ投稿フォーム用のroute
    Route::get('message/create', 'create');
});
```

### ■bladeの作成

メッセージ投稿用のフォームを作成します。

`resources/views/message/create.blade.php` を作成します.

今回はcdnを用いてbootstrapと、ajaxを使用するためのjqueryを使います。

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  {{-- csrf対策 --}
  <meta name="csrf-token" content="{{ csrf_token() }}">
  {{-- jquery --}}
  <script src="https://code.jquery.com/jquery-3.6.1.min.js" 
		integrity="sha256-o88AwQnZB+VDvE9tvIXrMQaPlFFSUTR+nldQm1LuPXQ=" crossorigin="anonymous"></script>
  {{-- bootstrap --}}
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" 
		integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.min.js" 
		integrity="sha384-+sLIOodYLS7CIrQpBjl+C7nPvqq+FbNUBDunl/OZv93DB7Ln/533i8e/mZXLi/P+" crossorigin="anonymous"></script>
  <title>Document</title>
</head>
<body>
  <div class="container">
    <div class="message-form">
      <form class="mt-5">
        <div class="message-area">
          <p id="messageContent"></p>
        </div>
        <div class="form-group row">
          <label for="messageForm" class="col-2 offset-3 text-center">メッセージ</label>
          <input type="text" class="form-control col-4" id="messageForm" placeholder="Hi, there!">
          <a href="javascript:void(0)" id="submitButton" class="btn btn-primary ml-1">投稿</a>
        </div>
      </form>
    </div>
  </div>
  <script src="/js/message.js"></script>
</body>
</html>
```

フォーム投稿用の画面ができます

![Screen Shot 2022-12-06 at 10.41.44 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612164/f32a2dda-399f-92fe-13cb-a29922c118ea.png)


### ■jsファイルの作成

`/public/js/message.js`を作成します

```jsx
$(document).ready(function() {
	// 投稿ボタンのdom取得
  let $submit_button = $('#submitButton');

  // 投稿ボタンを押した時の処理
  $submit_button.click(function() {
    let $message_text = $('#messageForm').val();
    let request_url = '/api/message/store';
    let message_data = {
      message: $message_text
    };

    $.ajax({
      url: request_url,
      type: 'POST',
      data: message_data,
      dataType: 'json',
      headers: {
          'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
      }
    });
  });
})
```

■解説

- $(document).ready
    - HTMLのDOM要素を読み込んでから、javascriptの処理を始めるためのもの
- ajax
    - url: リクエスト先のurlを指定
    - type: リクエストタイプを指定
    - data: 指定したデータがurlのクエリパラメータとして設定
        - 今回の場合、`localhost/api/message/store?message='投稿されたメッセージの値'`
    - dataType: apiによって返されるデータタイプを指定
    - headers
        - laravelの場合、post送信をする場合csrfトークンというものを使用しないと419エラーになる
        - `message/create.blade.php`の、metaタグに設定したcsrfトークンを取得して、リクエストヘッダーに設定している

### ■Controllerの作成

`app/Http/Controllers/MessageController.php` を作成します

```php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use App\Services\MessageService;

class MessageController extends Controller {

    public function __construct(
        MessageService $message_service
    ) {
        $this->message_service = $message_service;
    }

    public function index()
    {
        return view('message.index');
    }

    public function store(Request $request): JsonResponse
    {
        try {
            $attribute = [
                'content' => $request->message, // クエリパラメータからデータを取得
                'created_at' => now(),
                'updated_at' => now()
            ];
            $this->message_service->create($attribute);
            $this->sendResponse();
        } catch (\Exception $e) {
            $error_message = $e->getMessage();
            $this->sendErrorResponse($error_message);
        }
    }

    private function sendResponse()
    {
        return response()->json([
            'success' => true,
            'data' => null,
            'message' => 'ok'
        ], 200);
    }

    private function sendErrorResponse($error_message)
    {
        return response()->json([
            'success' => false,
            'message' => $error_message
        ], 400);
    }
}
```

■解説

- `$request→message`
    - 指定したキーの、urlのクエリパラメータの値を取得する
    - 今回の場合、`localhost/api/message/store?message='投稿されたメッセージの値'` の’投稿されたメッセージ’が値になる
- `$this->message_service->create($attribute);`
    - message_serviceクラス(DB操作を行うクラス)で、messageデータをmysqlに保存している

### ■ Serviceの作成

`app/Services/MessageService.php` を作成します。

```php
<?php

namespace App\Services;

use DB;

class MessageService {

    public function create($attribute)
    {
        DB::table('messages')->insert($attribute);
    }
}
```

■解説

- DBにmessageデータをinsertで保存

## 動作確認

準備が整いました！フォームでメッセージを入力して、投稿ボタンを押してみましょう！

![Screen Shot 2022-12-06 at 10.59.06 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2612164/c95b8d72-9485-0334-348f-a2641f427bb0.png)

Mysqlのmessagesテーブルのcontentカラムに、メッセージデータが保存されているのが確認できました！

## まとめ

今回はajaxを使って、非同期にmysqlにデータを保存する方法を紹介させていただきました。

私はこのajaxという言葉を初めて聞いたときは、なんだかすごい高度なことをやってそうと思ったのが記憶にありますが、実際に行なっているのは

どこかのサーバーにリクエストを送り、サーバーで何かしらの処理を行い、レスポンスが返ってくるだけのもの。そう思ってからは少し身近に感じられて、ajaxを使ったデータのやり取りが、自分が魔法を使っているかのような感覚でとても楽しくなりました。

これは弊社のメンターの方からよく言われていることなのですが、アプリケーションはデータの流れを意識することが大事であって、それが意識できるようになればあとはデータを取得・加工・表示する、というシンプルな見方ができるようになると知りました。私もまだまだですがこのあたりが身についてくると、どんどんと開発が楽しくなっていくのではないかと思います。