【Laravel】DBのデータを取得するAPIを自作し、フロント側から叩いてコンソールに表示させてみる

バックエンドとフロントエンドを切り分けた環境で、バックエンドのデータをフロントエンドに表示させる部分を学習中です。
今回は、データベースのデータを取得するAPIを作って、fetchとasync/awaitでフロント側に表示させることに成功したので、
流れを備忘録として残しておきます。

# 前提
- フロントエンドとバックエンドを切り分けている
- 自作APIを用いる
- データベースを使用する
    - PostgreSQL

# 環境
- PHP 8.1.7
- Laravel Framework 9.18.0
- psql (PostgreSQL) 14.4
- MacOS Monterey 12.2.1

# 開発手順

## 1. バックエンド、フロントエンドでそれぞれディレクトリを用意し、別々にターミナルを開いて移動しておく
```php:ターミナル1
mkdir todo-api
cd todo-api
```
```php:ターミナル2
mkdir todo-front
cd todo-front
```

## 2. フロントエンドのファイルを構成する

- todo-front
    - index.html

## 3. バックエンドのファイルを構成する（Laravelを使う）

```php:ターミナル
composer create-project laravel/laravel --prefer-dist todo-api
```

このコマンドで、todo-apiというディレクトリ配下にLaravelがよしなにファイルを構成してくれます。
ただ、今回LaravelではAPIを作るだけであり、MVCのVの部分はフロント側で作成するので、blade系は必要のないファイルになります。
とりあえず気にせず続行します。

## 4. データベース接続

今回はPostgreSQLを使用します。

[過去に自分がまとめた記事](https://qiita.com/asami___t/items/ac159c67effd34d44ab9)を見ながら、データベースを作成します。

今回は、以下のようなデータベースを用意しました。なお、テーブルやカラムは後でMigrationにより入れていきます。

```
データベース名:ToDoApp
```

## 5. Laravel側から接続する

.envファイルのDB部分を以下のように変更する。

```
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=データベース名
DB_USERNAME=ユーザ名
DB_PASSWORD=パスワード
```

## 6 モデルの作成

```php:ターミナル
php artisan make:model Todo -m
```

これで以下ファイルが自動的に作成されます。
- database/migrations/にxxxx_create_todos_table.php
- app/Models/Todo.php

以下のように、Migrationファイルにデータベースの設計書を記述していきます。
```php:xxxx_create_todos_table.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('todos', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->text('memo')->nullable();
            $table->smallInteger('status')->default(0);
            $table->timestamps();
        });
    }

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

これを「php artisan migrate」することで、先ほど作成したデータベースの中にテーブルとカラムが一括で追加されます。
では実行します。

```php:ターミナル
php artisan migrate
```
以下のようにテーブル名を確認すると、ちゃんとカラムが作られていますね！

![スクリーンショット 2022-06-29 14.47.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/7328877b-afd1-3e2a-6672-9fc93d42ebbb.png)

ただしデータが空っぽなので、試しに1件入れてみます。

```
データ挿入
insert into todos values (1, '洗濯', 0);
```

```
中身チェック
SELECT * FROM todos
```
![スクリーンショット 2022-06-29 14.50.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/04974a68-ed38-f7e4-c9c1-b1a662ad9a49.png)

今度は中身も入りました！

## 7. コントローラの作成

次のコマンドで、APIモードでコントローラを作成できます。

```php:ターミナル
php artisan make:controller TodoController --api
```

とりあえず簡単なAPIを作ってみます。
データベースから全てのデータを取得するAPIです。

```php:app/Http/Controllers/TodoController.php
<?php

namespace App\Http\Controllers;

use App\Models\Todo;

class TodoController extends Controller
{
    public function index()
    {
        $todoIndex = Todo::all();
        return $todoIndex;
    }
}
```

## 8. ルーティング

ルーティングは、APIの場合はroutes/api.phpに記述するのが理想ですが、今回は一番基本のroutes/web.phpに記述してみます。

```php:routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\TodoController;
use Illuminate\Http\Request;

Route::get('/', function () {
    return view('welcome');
});
// 追記
Route::controller(TodoController::class)->group(function () {
    Route::get('api/todo/index', [TodoController::class, 'index']);
});
```

これでURIの設定ができたはずなので、下記コマンドでチェックしてみます。

```php:ターミナル
php artisan route:list
```

![スクリーンショット 2022-06-29 15.04.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/88d73b9d-58bf-68cb-b706-bba21c8e3d55.png)

上記画像の通り、api/todo/indexにアクセスしたらTodoControllerのindexアクションにアクセスされる、というルーティングが作られているのが確認できました！

## 9. ローカルサーバ立ち上げ
ではブラウザでも確認してみます。
Laravelが用意してくれているコマンドを使ってビルトインサーバ（デフォルトの8000番ポート）を立ち上げ、ブラウザで下記URLにアクセスしてみましょう。

```:ターミナル
php artisan serve
```

```:ブラウザ
http://localhost:8000/api/todo/index
```

![スクリーンショット 2022-06-29 15.14.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/aacc6a96-e2b2-542a-81a5-02339120539f.png)

データベースのデータを取得できているのが分かります！
しかし日本語が文字化けしてしまっているので、コントローラを下記のように修正します。

```php:app/Http/Controllers/TodoController.php
<?php

namespace App\Http\Controllers;

use App\Models\Todo;

class TodoController extends Controller
{
    public function index()
    {
        $todoIndex = Todo::all();
        /*******************************************
        変数の配列に日本語が含まれる場合には
        JSON_UNESCAPED_UNICODEオプションを指定することで
        正常に日本語をJSON形式に変換することができます。
        *******************************************/
        $encode_array = json_encode($todoIndex, JSON_UNESCAPED_UNICODE);
        return $encode_array;
    }
}
```

そしてブラウザを更新すると・・・
![スクリーンショット 2022-06-29 15.12.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/f5ce6e96-f031-ebff-5ce8-67a4c026f366.png)

日本語がちゃんと変換されて表示されていますね。


## 10. フロント側からAPIを叩く

最後に、フロント側からAPIを叩いて表示させてみます。
手順１でバックエンド、フロントエンドで別々にターミナルを開いておいたと思いますので、フロントエンド用のターミナルに移っておきます。
todo.htmlを下記のように編集して、APIと連携させます。
今回はfetchとasync/awaitを使用しています。
なお、fetchの引数はAPIのURLを書きますが、これはroutes/web.phpで指定しておいたルーティングを書きます。

```html:todo.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
</head>

<body>
<script>
/************************************************++*******
APIを呼び出してデータをコンソールに表示させる
*********************************************************/
async function callApi() {
  const res = await fetch("http://localhost:8000/api/todo/index");
  const index = await res.json();
  console.log(index);
}

callApi();

</script>
</body>
</html>
```

それでは、バックとフロントが連携できたか確認するため、ブラウザのコンソールを開いて見てみましょう。
フロント側はLaravelとは切り離して構成しているため、Laravelの便利コマンド（php artisan〜）は使用できません。
そのため下記コマンドでビルトインサーバを立ち上げます。
なお、8000番ポートはバックエンド側で使用中のため、フロントエンド側では3000番ポートとしておきたいと思います。

```:ターミナル
php -S localhost:3000
```

```:ブラウザ
http://localhost:3000/todo.html
```

コンソールを確認すると、ちゃんとデータが返ってきています！

![スクリーンショット 2022-06-29 15.36.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985164/663ade08-a6e4-99b6-45d4-72a0cac84448.png)

無事に連携ができたので、あとはもっと内容を充実させていくのみです！