【jQuery】PHP/Ajax/MySQL を利用した非同期通信を行う

# 実装内容

- productsテーブルを作成する。（カラムはid, name, priceの3つ）
- 一覧に常にレコード全件が表示される
- 特定のidからnameとpriceを取り出して表示する
- 新しくレコードを追加して、一覧に追記する
- レコード追加前に確認メッセージを表示する

# AjaxとJSON
こちらの記事がわかりやすいです！

>[初心者目線でAjaxの説明](https://qiita.com/hisamura333/items/e3ea6ae549eb09b7efb9)

##Ajax
- Asynchronous JavaScript + XML
- JavaScript組み込みクラスであるXMLHttpRequestによる非同期（Asynchronous）通信のこと

##JSON
- JavaScript Object Notation
- データ形式の1つ
- XMLに替わり、Ajaxで使われることが多い

# ファイル構成
サーバはApache、データベースはMySQLを使用しました。

htdocsの下に`ajax_test`ディレクトリを用意し、その中に必要なファイルを作成します。

```bash
$ pwd
/usr/local/var/www/htdocs/ajax_test
$ ls
ajax_test.html		ajax_test_add.php	ajax_test_show_all.php
ajax_test.js		ajax_test_show.php
```

# テーブルの作成
既存のデータベースdb1にproductsテーブルを追加しました。

作成済みのテーブルは、`SHOW CREATE TABLEコマンド`で作成時のクエリを確認できるので便利です。

`Table`カラムにテーブル名、`Create Table`カラムに作成時のクエリが表示されます。（`ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8`は、テーブル作成時には記述していません。）

```sql
mysql> use db1;
SHOW CREATE TABLE products;
```

`Create Table`の値

```sql
CREATE TABLE `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `price` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8
```

適当にレコードを作成しておきます。

```sql
mysql> INSERT INTO products(name, price) VALUES('りんご', 100), ('みかん', 80);
```

ちなみに、MySQLのコマンドラインは`\c`で入力キャンセルができます。（前までは`\q`を実行していて、コマンドラインから退出してました；）

>[MySQLのコマンドラインで入力をキャンセルする](https://qiita.com/ysti/items/b4a84691c357167c89c2)

# HTML

```ajax_test.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <!-- jQueryの読み込み -->
    <title>Ajax、PHP、MySQLの連携</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
    <!-- 指定したidからレコード取得 -->
    <div class="show_products">
        IDを入力:<input id="id_number"><br>
        <!-- 取得したレコードを表示 -->
        <div id="result">
        </div>
        <!-- 送信ボタン -->
        <button id="ajax_show">詳細ボタン</button>
    </div>
    <!-- レコード追加 -->
    <div class="add_products">
        <!-- 入力フォーム -->
        商品名を入力:<input id="name"><br>
        価格を入力:<input id="price"><br>
        <!-- レコード追加後に入力内容を表示 -->
        <div id="add_result">
        </div>
        <!-- 送信ボタン -->
        <button id="ajax_add">追加ボタン</button>
    </div>
    <!-- レコード全件取得 -->
    <!-- 追加したレコードも即時反映される -->
    <table border="1" id="all_show_result">
        <tr>
            <th>id</th><th>商品名</th><th>価格</th>
        </tr>
    </table>
    <script src="ajax_test.js"></script>
</body>
</html>
```

# jQuery
勉強用なので、やたらとconsole.logが記述してあります。

```ajax_test.js
$(function(){
// レコードを全件表示する
// 試しに関数にしてみただけ
function getAllData(){
    $.ajax({
        // 通信先ファイル名
        url: "ajax_test_show_all.php",
        // 通信が成功した時
        success: function(data) {
            // 取得したレコードをeachで順次取り出す
            $.each(data, function(key, value){
                // #all_show_result内にappendで追記していく
                $('#all_show_result').append("<tr><td>" + value.id + "</td><td>" + value.name + "</td><td>" + value.price + "</td></tr>");
            });

            console.log("通信失敗");
            console.log(data);
        },

        // 通信が失敗した時
        error: function(){
            console.log("通信失敗");
            console.log(data);
        }
    });
}

// 関数を実行
getAllData();

// #ajax_showがクリックされた時の処理
// 指定したidのレコードを取得する
$('#ajax_show').on('click',function(){
    $.ajax({
        // リクエスト方法
        type: "GET",
        // 送信先ファイル名
        url: "ajax_test_show.php",
        // 受け取りデータの種類
        datatype: "json",
        // 送信データ
        data:{
            // #id_numberのvalueをセット
            "id" : $('#id_number').val()
        },
        // 通信が成功した時
        success: function(data) {
            // 取得件数が1件のため、取得した情報を#result内にそのまま追加する
            $('#result').html("<p>ID番号" + data[0].id + "は「" + data[0].name + "」です。<br>価格は「" + data[0].price+"円」です。</p>");

            console.log("通信成功");
            console.log(data);
        },

        // 通信が失敗した時
        error: function(data) {
            console.log("通信失敗");
            console.log(data);
        }
    });

    return false;
});

// #ajax_addがクリックされた時の処理
$('#ajax_add').on('click',function(){
    // 確認メッセージを表示
    // OKならtrue,キャンセルならfalseが代入される
    var confirmResult = window.confirm("登録してもよろしいですか？");

    if(confirmResult) {
        $.ajax({
            // 送信方法
            type: "POST",
            // 送信先ファイル名
            url: "ajax_test_add.php",
            // 受け取りデータの種類
            datatype: "json",
            // 送信データ
            data: {
                // #nameと#priceのvalueをセット
                "name" : $('#name').val(),
                "price" : $('#price').val()
            },
            // 通信が成功した時
            success: function(data) {
                $('#add_result').html("<p>" + data[0].name + "が" + data[0].price + "円のデータを登録しました。</p>");

                console.log("通信成功");
                console.log(data);

                // 一覧に追加したレコードを追記
                $.each(data, function(key, value){
                    $('#all_show_result').append("<tr><td>" + value.id + "</td><td>" + value.name + "</td><td>" + value.price + "</td></tr>");
                });
            },

            // 通信が失敗した時
            error: function(data) {
                console.log("通信失敗");
                console.log(data);
            }
        });
    }

    return false;
});
});
```

## こんな書き方も
### getメソッド
通信の成功時のみ、実行されます。

```ajax_test.js
function getAllData(){
    $.get("ajax_test_show_all.php",
        function(data){
            $.each(data, function(key, value){
                $('#all_show_result').append("<tr><td>" + value.id + "</td><td>" + value.name + "</td><td>" + value.price + "</td></tr>");
            });

            console.log("通信成功");
            console.log(data);
        }
    );
}
```

### doneメソッドとfailメソッド
メソッドチェーンを使います。

```ajax_test.js
$('#ajax_show').on('click',function(){
    $.ajax({
        // リクエスト方法
        type: "GET",
        // 送信先ファイル名
        url: "ajax_test_show.php",
        // 受け取りデータの種類
        datatype: "json",
        // 送信データ
        data:{
            // #id_numberのvalueをセット
            "id" : $('#id_number').val()
        }
    })

    // 通信が成功した時
    .done( function(data) {
        $('#result').html("<p>ID番号" + data[0].id + "は「" + data[0].name + "」です。<br>価格は「" + data[0].price+"円」です。</p>");

        console.log('通信成功');
        console.log(data);
    })

    // 通信が失敗した時
    .fail( function(data) {
        console.log('通信失敗');
        console.log(data);
    });

    return false;
});
```
# レコード表示処理（全件）
コードの重複が多い・・・

```ajax_test_show_all.php
<?php
// データベース接続
// $host = localhostで動かなければipアドレスを記載
$host = '127.0.0.1';
// データベース名
$dbname = 'db1';
// ユーザー名
$dbuser = 'user_name';
// パスワード
$dbpass = 'password';

// データベース接続クラスPDOのインスタンス$dbhを作成する
try {
    $dbh = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8mb4", $dbuser, $dbpass);
// PDOExceptionクラスのインスタンス$eからエラーメッセージを取得
} catch (PDOException $e) {
    // 接続できなかったらvar_dumpの後に処理を終了する
    var_dump($e->getMessage());
    exit;
}

// データ取得用SQL
// 値はバインドさせる
$sql = "SELECT id, name, price FROM products";
// SQLをセット
$stmt = $dbh->prepare($sql);
// SQLを実行
$stmt->execute();

// あらかじめ配列$productListを作成する
// 受け取ったデータを配列に代入する
// 最終的にhtmlへ渡される
$productList = array();

// fetchメソッドでSQLの結果を取得
// 定数をPDO::FETCH_ASSOC:に指定すると連想配列で結果を取得できる
// 取得したデータを$productListへ代入する
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
    $productList[] = array(
        'id'    => $row['id'],
        'name'  => $row['name'],
        'price' => $row['price']
    );
}

// ヘッダーを指定することによりjsonの動作を安定させる
header('Content-type: application/json');
// htmlへ渡す配列$productListをjsonに変換する
echo json_encode($productList);
```

# レコード表示処理（id検索の1件のみ）

```ajax_test_show.php
<?php
// GETメソッドでリクエストした値を取得
$id = $_GET['id'];

// データベース接続
// $host = localhostで動かなければipアドレスを記載
$host = '127.0.0.1';
// データベース名
$dbname = 'db1';
// ユーザー名
$dbuser = 'user_name';
// パスワード
$dbpass = 'password';

// データベース接続クラスPDOのインスタンス$dbhを作成する
try {
    $dbh = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8mb4", $dbuser, $dbpass);
// PDOExceptionクラスのインスタンス$eからエラーメッセージを取得
} catch (PDOException $e) {
    // 接続できなかったらvar_dumpの後に処理を終了する
    var_dump($e->getMessage());
    exit;
}

// データ取得用SQL
// 値はバインドさせる
$sql = "SELECT id, name, price FROM products WHERE id = ?";
// SQLをセット
$stmt = $dbh->prepare($sql);
// SQLを実行
$stmt->execute(array($id));

// あらかじめ配列$productListを作成する
// 受け取ったデータを配列に代入する
// 最終的にhtmlへ渡される
$productList = array();

// fetchメソッドでSQLの結果を取得
// 定数をPDO::FETCH_ASSOC:に指定すると連想配列で結果を取得できる
// 取得したデータを$productListへ代入する
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
    $productList[] = array(
        'id'    => $row['id'],
        'name'  => $row['name'],
        'price' => $row['price']
    );
}

// ヘッダーを指定することによりjsonの動作を安定させる
header('Content-type: application/json');
// htmlへ渡す配列$productListをjsonに変換する
echo json_encode($productList);
```

リクエストメソッドは`GET`を使用しているので、`http://localhost/ajax_test/ajax_test_show.php/?id=41/`でアクセスすると、`[{"id":41,"name":"\u308a\u3093\u3054","price":100}]`のようにレスポンスが返ってきます。

データ形式はもちろんJSONです。

# レコード追加処理

```ajax_test_add.php
<?php
// POSTメソッドでリクエストした値を取得
$name = $_POST['name'];
$price = $_POST['price'];

// データベース接続
// $host = localhostで動かなければipアドレスを記載
$host = '127.0.0.1';
// データベース名
$dbname = 'db1';
// ユーザー名
$dbuser = 'user_name';
// パスワード
$dbpass = 'password';

// データベース接続クラスPDOのインスタンス$dbhを作成する
try {
    $dbh = new PDO("mysql:host={$host};dbname={$dbname};charset=utf8mb4", $dbuser, $dbpass);
// PDOExceptionクラスのインスタンス$eからエラーメッセージを取得
} catch (PDOException $e) {
    // 接続できなかったらvar_dumpの後に処理を終了する
    var_dump($e->getMessage());
    exit;
}

// データ追加用SQL
// 値はバインドさせる
$sql = "INSERT INTO products(name, price) VALUES(?, ?)";
// SQLをセット
$stmt = $dbh->prepare($sql);
// SQLを実行
$stmt->execute(array($name, $price));

// 先ほど追加したデータを取得
// idはlastInsertId()で取得できる
$last_id = $dbh->lastInsertId();
// データ追加用SQL
// 値はバインドさせる
$sql = "SELECT id, name, price FROM products WHERE id = ?";
// SQLをセット
$stmt = ($dbh->prepare($sql));
// SQLを実行
$stmt->execute(array($last_id));

// あらかじめ配列$productListを作成する
// 受け取ったデータを配列に代入する
// 最終的にhtmlへ渡される
$productList = array();

// fetchメソッドでSQLの結果を取得
// 定数をPDO::FETCH_ASSOC:に指定すると連想配列で結果を取得できる
// 取得したデータを$productListへ代入する
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
    $productList[] = array(
        'id'    => $row['id'],
        'name'  => $row['name'],
        'price' => $row['price']
    );
}

// ヘッダーを指定することによりjsonの動作を安定させる
header('Content-type: application/json');
// htmlへ渡す配列$productListをjsonに変換する
echo json_encode($productList);
```

追加したばかりのレコードのidがどのように渡されているか、`var_dump(array($last_id));`を追記して確認できます。（内容はデベロッパーツールで確認できます。）

こんな感じで返ってきます。

```ajax_test_add.php
array(1) {
  [0]=>
  string(2) "61"
}
```

# 参考記事

>[Ajaxを使ってデータベースのデータを非同期で取得する【Ajax+MySQL+PHP】](https://rimane-novels.net/ajax-mysql-1021/)