【Laravel】非同期処理をJavaScriptのFetch APIで実装する

# Fetch APIによる非同期処理とは？

Fetch APIはブラウザに搭載された非同期処理をするための関数です。
fetch関数を用いることで、ブラウザのページを全て更新することなく
一部分だけを更新する非同期処理が実行できます

## 非同期処理のFetch関数の使い方

Laravelでは`Model` `View` `Controller`の3つに分けてコードを管理するため
fetchメソッドもMVCの流れに沿う必要があります

fetch関数は第一引数に指定したURLにhttp通信（リクエスト）を送信することで
Laravelのルーティングを経由しコントローラに記述したアクションを実行します

コントローラ側でデータベースの操作および情報を取得した処理を実行した後、
fetch関数に対して実行結果（レスポンス）を返します。

JavaScriptにはブラウザのHTMLを書き換えたり追記したりする機能があるため
必要に応じて現在表示されているブラウザのHTMLにレスポンスの内容を反映させます
つまり非同期で部分的にHTMLを書き換えることができます

## 対応するブラウザは？


|ブラウザ |バージョン  |
|---|---|
|IE  |非対応  |
|Edge  |14〜  |
|Firefox  |39〜  |
|Chrome  |42〜  |
|Safari  |10.1〜  |

## 同じ非同期処理のajaxとの違いは？

- 非同期処理を実装する方法は複数存在し、fetchの他に挙げられるのが
ajaxという方法になります。

- ajaxを実行するためにはまず、jQueryのライブラリを読み込む必要があります。
fetchはブラウザが既にライブラリを搭載しているため
`ライブラリを読み込む必要がありません`

- fetchは最新のブラウザであれば機能しますが、古いブラウザの場合
機能しない場合があります。
jQueryでも一概には言えませんが、ajaxではブラウザの違いによる誤作動を
ある程度許容することができます



## 元となるHTMLコード

```html:index.braid.html

<table class="table">
    <tr id="result">
        <th>id</th>
        <th>商品名</th>
        <th>価格</th>
    </tr>
</table>

```


## LaravelでFetchを動かすための基本知識




#### JavaScriptをhtmlに直接記述する場合

```html:index.braid.html

<table class="table">
    <tr id="result">
        <th>id</th>
        <th>商品名</th>
        <th>価格</th>
    </tr>
</table>

<script type="text/javascript">

// ここに処理を記述します 

</script>

```
#### JavaScriptを呼び出す場合
- ファイルの保存場所はLaravelのプロジェクト内部に存在する次のフォルダに保存します
- `public¥js¥fetch.js`

- 呼び出す際のパスは`js/fetch.js`で呼び出せます

```html:index.braid.html

<table class="table">
    <tr id="result">
        <th>id</th>
        <th>商品名</th>
        <th>価格</th>
    </tr>
</table>

<script src="js/fetch.js"></script>
```

## データベースのテーブル情報を一覧表示する

- 非同期通信処理つまりfetchのコードはJavaScriptファイルに記述します
- javascriptのscriptタグはbodyタグの最後に書くことが推奨されます。
- javascriptの書き方は外部ファイルとして呼び出す場合と
scriptタグに直接記述する方法があります



### JavaScriptにfetch（非同期処理）を記述する
- テーブルの一覧を表示する処理を実行する関数を作成します
- 関数の名前は任意で命名しますが命名規則で先頭小文字のキャメルケースです。
- 各構成語の先頭を大文字にする方式で記述します
- 例：indexAll

```javascript:fetch.js
function indexAll(){
    // ここにfetch関数のコードを記述していきます
}

// 最後に関数を実行します

// functionを動かす条件として”ボタンを押した場合”などの条件がない場合は
// ブラウザが更新されるとコードが上から順に、つまりHTMLとjavascriptが読み込まれるため
// indexAllは自動で実行されることになります

indexAll();
```


### fetchの基本構文

*通常の書き方*

- fetchメソッドは通信が`成功`したか、`失敗`したかを結果として返します
- 結果を参照する場合はfetchメソッドの`promiseオブジェクト`を呼び出します
- phpから受け取った`promiseオブジェクト`をJavaScriptで利用するためにはJSONファイルに変換が必要です
- 後述の`メソッドチェーン`を利用するとfetchから取得した結果をそのまま別のメソッドに渡すことができます。
-
```javascript:fetch.js

function indexAll(){

        
        fetch('URLを記述する第一引数') //（1）
        .then((任意の変数名1) => {return 任意の変数名1.json()}) //(2)
        .then((任意の変数名2) => { //(3)
                    
                                //(4)　

                                }) 
        
        .catch(error => {
                                //(5)
                            alert("実行失敗");　
                            alert(error);
                        })
                    }

//(1) コントローラーへ繋がるURLを記述することで
//(1) コントローラーへ「データベースの情報が欲しい」と"リクエスト"を送信します
//(1) コントローラーのアクションには「データベースの情報を操作する処理」を記述しておき
//(1) アクションを実行することでデータベースのテーブル情報を取得し結果をfetchに返します。

//(1) "fetch"の返り値は"Promise"と呼ばれるオブジェクト
//(1) "Promise"の中身は”通信成功”もしくは”通信失敗”という結果データが入っています
//(1) さらに"Promise"には"response"が格納されており
//(1) "response"はデータベースから取得したテーブルデータなどが代入されています

//(2) ドット ”.” によるメソッドチェーンを使うことでfetchの結果である"Promise"をインスタンス化して参照することができます
//(2) thenメソッドは通信が成功した場合の処理を実行することができます
// ※  catchメソッドは通信が失敗した場合の処理を実行することができます
//(2) "Promise"を参照し内部の"response"を引数として渡すことで
//(2) "response"を"jsonファイル"として解釈した結果を返します

//(3) jsonに変換した"response”を”任意の変数”に代入することで実行する処理の中で変数を利用できるようにします
//(3) 処理の内容は取得してきたテーブルのレコードをHTMLのタグで追加する処理を実行します

//(4) コントローラーとの通信が成功した場合に実行するコードを記述する
//(4) HTMLにタグを追加するコードを記述する

//(5) コントローラーとの通信が失敗した場合に実行するコードを記述する
//(5) エラーコードやエラーメッセージなどを記述する

```
基本構文の省略形（意味は同じになります）
- 処理が1行だけの場合`{}`と`return`は省略できます
```javascript:fetch.js

fetch('URLを記述する第一引数') //（1）
  .then((response) => response.json()) //（2）
  .then((res) => {

                     // (3)　通信が成功した場合の実行したい処理を記述する

                  }) 

  .catch(error => {
                                //(5)
                            alert("実行失敗");　
                            alert(error);
                        })
                    }


```



#### thenメソッドとは

参考サイト：https://www.sejuku.net/blog/52314

fetch() で通信が成功した場合のレスポンスを操作できます
ここでは通信が成功したため受け取ったレスポンスをjsonファイルに変換し
受け取ったレスポンスに存在するテーブルの情報をHTMLに反映させます

#### catchメソッドとは

fetch() で通信が失敗した場合のレスポンスを操作できます
alertでfetchの結果を確認すると、エラー内容が確認できます




#### JSONファイルとは

テキスト形式のファイルのことでJSONとは`JavaScript Object Notation`の略で
`ジェイソン`と呼ばれています。
JSONはJavaScriptで定義されているオブジェクト表記法の1つで
テキスト形式で表記されるため簡単にデータ交換ができます。
その可読性からJavaScriptに限らず主要なプログラム言語から利用されています。

#### メソッドチェーンとは

参考サイト：https://www.sejuku.net/blog/24962

メソッドの実行結果を変数などを代入する手間を省略し、
直接他のメソッドの`引数`に代入後、次のメソッドを実行することができます。

JavaScriptではメソッドを「 .」（ドット)で連結するため
fetchメソッドやの後に`.then`を実行しています







### Fetch関数でテーブルの一覧を表示させる

#### fetch関数に利用するURLを設定する
まずはfetchの通信先を設定するためにルーティングを設定します。
これでfetchメソッドの第一引数に`/show_all`を渡すことで
コントローラーの`showAll`が実行されます

```web.php
Route::get('/', 'HomeController@index')->name('crud.index'); /* 初期ページ */
Route::get('/show_all', 'HomeController@showAll'); // 全表示アクション

```

### コントローラーのアクションにデータベースの操作処理を記述します

*テーブル名：Companies*
![スクリーンショット 2022-12-30 9.20.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2765288/96d90316-9ee9-412c-8105-20ab5b364bb2.png)


#### コントローラーのデータベース操作処理
1. 全てのcompaniesテーブルの全てのレコードを取得する
1. 値は`id` `name` `price`をまとめた連想配列に格納する
1. 完成した配列はjavascriptで扱えるようにjsonデータに変換してfetchに送信する

``` javascript:****Controller.php

public function showAll()
{
    /* $companies = \DB::table('companies')->get(); */
    $companies = Companies::get();

    foreach($companies as $companie){
        $companieList[] = array(
          'id'    => $companie->id,
          'name'  => $companie->name,
          'price' => $companie->price
        );
    }

    // echoで配列をjsonに変換しつつfetchへサーバー情報を送信する
    echo json_encode($productList);
}

```

#### Eloquentとクエリビルダについて

参考先：https://qiita.com/Laravel-student/items/83475abb6d6acb3a177f

データベースを操作する方法にはEloquent（エロクエント）と
クエリビルダという手法がありますが、どちらでデータを取得しても
問題ありません

```php:Eloquentの場合

// Companiesモデルを利用しデータを取得する方法です
 $products = Companies::get();
```

```php:クエリビルダの場合

//DBファザードを利用しデータを取得する方法です
$companies = \DB::table('companies')->get();

```

### fetchで実行する一覧表示の非同期処理

fetchでサーバーにリクエストを送信しましたので
サーバーから届いたレスポンスを使ってJavaScriptのコードを実行します

- 上述のコントローラー側の処理で実行したechoメソッドが
fetchに対して実行結果をレスポンスしています

1. fetch関数では`通信が成功した場合と失敗した場合の処理`を
それぞれ記述することができます

1. ここでは通信が成功した場合にブラウザへ表示されているHTMLに
HTMLタグを追加する処理を実行します


```javascript:fetch.js

    .then(response => response.json()) 
    .then(res => {

    /* 通信成功が成功した場合の処理 */

        // forEach　はphpの繰り返し処理です "res"に存在する配列の数だけ繰り返すことができます
        //　メソッドチェーンにより、"res"の値は"elm"に代入され、次からの処理はelmという連想配列を使うことができます

        // resと区別するため繰り返し処理中では連想配列の値は"elm"という配列名に代入して利用していきます
        // "elm"は"element"の略称です　エレメント（要素）とは「HTMLタグ」で囲んだ情報の単位を示します
        
    res.forEach(elm =>{ 
                
                //　ここにHTMLを追加する処理を記述します
                
                //目的として、データベースから取得した情報（レスポンス）を使って、<tr>、<td>タグによるレコードを挿入します
                //手段としては上述されている"index.braid.html"に存在する"result"という名前に設定したID属性の場所を取得し
                //その下層にHTMLタグを挿入します
            
        })
        alert("実行成功");
      
    })

    .catch(error => {
         // "error" は任意の変数名です　可読性を高めるためにエラーに関する変数名は"error"で統一します　
        alert("実行失敗");

        alert(error);
    })
}

// 関数を実行します
indexAll();
```

```javascript:fetch.js

        // 取得したレコードをeachで順次取り出す
        res.forEach(elm =>{ 
                // forEach　は　phpの繰り返し処理 resに存在する配列の数だけ繰り返します
                // resと区別するため繰り返し処理中では連想配列の値は"elm"という配列名に代入して利用していきます
                // "elm"は"element"の略称です　エレメント（要素）とは「HTMLタグ」で囲んだ情報の単位を示します
            var insertHTML = "<tr class=\"target\"><td>" + elm['id'] + "</td><td>" + elm['name'] + "</td><td>" + elm['price'] + "</td></tr>"
            var result = document.getElementById("result");
            result.insertAdjacentHTML('afterend', insertHTML);

            //メソッドチェーンで"result"に含まれる結果を参照します
            //insertAdjacentHTMLは指定したテキストを指定した場所に挿入することができます
            //第一引数は場所,第二引数はテキストになります（'afterend'＝ 要素自身の後ろ)

        })
        alert("実行成功");
      
    })

    .catch(error => {
         // 取得したレコードをeachで順次取り出す
        alert("実行失敗");

        alert(error);
    })
}

// 関数を実行します
indexAll();
```