LaravelでCRUDのAPIを実装する方法（Postmanで動作確認）

# 環境
- Laravel 10.4.1
- PHP 8.2.4

# 前提
動くLaravelアプリは完成されていて、DBとモデルは既に存在することを前提とする。
APIの動作はPostmanで確認する。

# 目的
既に動作しているLarvelのtodo管理のアプリに、
- GET
- POST
- PUT
- DELETE
のAPIでのエンドポイントを作って、postmanで動作確認する。

# 作業
## API用コントローラーを作成
php artisan make:controllerコマンドでコントローラーを作成。
今回はApiTodoControllerという名前にする。
```bash:bash
$ php artisan make:controller ApiTodoController.php
Controller created successfully.
```
作成できたら、通常の\Controllers直下にApiフォルダを作り、
\Controllers\Api\
に配置する。（わかりやすいようにApiフォルダに分けた）
→\Laravelapp\app\Http\Controllers\Api\ApiTodoController.php
となる。

## API用コントローラーを修正
ApiTodoController.php　を下記のように修正していく。

```PHP:ApiTodoController.php
<?php
namespace App\Http\Controllers\Api;

use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Todo;
use App\Http\Controllers\Controller;

class ApiTodoController extends Controller
{

    /**
     * todo作成.
     *
     * @param Request $request
     * @return void
     */
    public function create(Request $request) {

        if(!$request->user_id){
            return response()->json('ユーザーIDが未入力です' , 400);
        }
        if(!$request->title){
            return response()->json('タイトルが未入力です' , 400);
        }
        if(!$request->todo){
            return response()->json('TODOが未入力です' , 400);
        }
        $user = User::find($request->id);
        if(!$user){
            return response()->json('追加対象のユーザーが存在しません' , 400);
        }

        $todo = Todo::create([
            'user_id' => $request->user_id,
            'title' => $request->title,
            'todo' => $request->todo,
            'add_date' => NOW(),
        ]);
        return response()->json($todo);
    }
    /**
     * todo取得.
     *
     * @param Request $request
     * @return void
     */
    public function fetch(Request $request) {
        $todo = Todo::find($request->id);
        if(!$todo){
            return response()->json('取得対象のTODOが存在しません' , 400);
        }
        return response()->json($todo);
    }
    /**
     * todo更新.
     *
     * @param Request $request
     * @return void
     */
    public function update(Request $request) {
        if(!$request->title){
            return response()->json('タイトルが未入力です' , 400);
        }
        if(!$request->todo){
            return response()->json('TODOが未入力です' , 400);
        }

        $todo = Todo::find($request->id);
        if(!$todo){
            return response()->json('更新対象のTODOが存在しません' , 400);
        }
        $todo->update([
            'title' => $request->title,
            'todo' => $request->todo,
            'update_date' => NOW(),
        ]);

        return response()->json($todo);
    }
    /**
     * todo削除.
     *
     * @param Request $request
     * @return void
     */
    public function delete(Request $request) {
        $todo = Todo::find($request->id);
        if(!$todo){
            return response()->json('削除対象のTODOが存在しません' , 400);
        }
        $todo->delete();
        return response()->json();
    }

}
```
今回、
- GET
- POST
- PUT
- DELETE
のエンドポイントを作るので、それに合わせて、
- POST　－　createメソッド
- GET　－　fetchメソッド
- PUT　－　updateメソッド
- DELETE　－　deleteメソッド
を作成した。

## createメソッド
```PHP:ApiTodoController.php
$todo = Todo::create([
    'user_id' => $request->user_id,
    'title' => $request->title,
    'todo' => $request->todo,
    'add_date' => NOW(),
]);
return response()->json($todo);

```
createメソッドでは、送られてきたリクエストをtodoテーブルに保存する。
その前にバリデーションをかけて、エラーならエラーを返す処理になる。
保存した状態になったモデルが$todo変数に返って、それを
`return response()->json($todo);`
の形式で書くことで、リクエスト元にJASON形式でレスポンスを返す。

## fetchメソッド
```PHP:ApiTodoController.php
    $todo = Todo::find($request->id);
    if(!$todo){
        return response()->json('取得対象のTODOが存在しません' , 400);
    }
    return response()->json($todo);

```
送られてきたIDによりtodoテーブルの内容を取得、リクエスト元に対して、取得できた内容をJSON形式で返す。


## updateメソッド
```PHP:ApiTodoController.php
    $todo = Todo::find($request->id);
    if(!$todo){
        return response()->json('更新対象のTODOが存在しません' , 400);
    }
    $todo->update([
        'title' => $request->title,
        'todo' => $request->todo,
        'update_date' => NOW(),
    ]);
    
    return response()->json($todo);
```
findメソッドでtodoクラスを取得した後に、updateメソッドを使用。
同様に、リクエスト元に対して、更新した内容をJSON形式で返す。


## deleteメソッド
```PHP:ApiTodoController.php
    $todo = Todo::find($request->id);
    if(!$todo){
        return response()->json('削除対象のTODOが存在しません' , 400);
    }
    $todo->delete();
    return response()->json();
```
deleteメソッドも同様にfindメソッドでtodoモデルを取得後、deleteメソッドを呼ぶことで削除する。リクエスト元に対しては、レスポンスとしては何も返していない（エラーの時には返る）。


# APIのルーティングを設定する
通常のルーティングは`routes/web.php`に記載していたが、APIとしてアクセスする場合のルーティングは`routes/api.php`に記載する。

```PHP:api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\ApiTodoController;

// todoのCRUD(API呼び出し)
Route::post('todo', [ApiTodoController::class, 'create']);
Route::get('todo', [ApiTodoController::class, 'fetch']);
Route::put('todo', [ApiTodoController::class, 'update']);
Route::delete('todo', [ApiTodoController::class, 'delete']);
```
1行目の場合だと、
→APIから、`http://*******/api/todo` に`POST形式`でアクセスすると、`ApiTodoController`の`create`メソッドが呼び出される、
ということになる。
(routes/api.phpで作成したルーティングにはURLでデフォルトで/apiがつくため、`todo`のルーティングでは、`/api/todo`と呼び出すことになる。）

以下同様に、
- /api/todo　にgetでアクセスすると、fetchメソッドが呼ばれる。
- /api/todo　にputでアクセスすると、updateメソッドが呼ばれる。
- /api/todo　にdeleteでアクセスすると、deleteメソッドが呼ばれる。
となる。

# 実際にAPIから呼んでみる
Postmanというツールを使用する。
APIのテストをおこなうためのツールで、手早くHTTPリクエストを送信してエンドポイントが正しく機能しているか確認することができる。
参考：[RESTサービスを触る際の必須ツールPostmanを使ってみました](https://www.xlsoft.com/jp/blog/blog/2017/06/23/post-1638-post-1638/)

今回は、Laravel環境に`http://localhost:8000`でアクセスできるので、API呼び出しには`http://localhost:8000/api/todo`を使う。

## createメソッド
まずは、最初のcreateメソッドの呼び出しを行ってみる（データの追加）。
ルーティングに設定した通り、`post`で呼び出す。
（画面中央右寄りの「JASON」となっている部分は、「JASON」を選ばないと正しく動作しない。）
結果としてpostmanの画面では、下記のように返ってくる。
上部がAPIからの呼び出し値、下部がレスポンス値。
![230503_000000.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/2549f35b-7cac-e650-6d56-be55d574f153.jpeg)


```json:呼び出し値
{
    "user_id":9,
    "title":"APIからのテスト",
    "todo":"230503"
}
```
```json:返り値
{
    "user_id": 9,
    "title": "APIからのテスト",
    "todo": "230503",
    "add_date": "2023-05-03T05:59:36.869734Z",
    "id": 106
}
```

テーブルをphpmyadminから確認しても、データが追加されていることが確認できた。
![230503_000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/5539b3af-a3a5-c810-b8ae-ee3692074b77.jpeg)

ちなみに、user_idを100、という存在しないIDで呼び出すと、下記のようにエラーが返るように、簡単なバリデーションを設定した(他のメソッドも同様)。
![230503_000007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/3601e866-c9e4-5253-3980-5f5e79b25940.jpeg)
```json:呼び出し値
{
    "user_id":100,
    "title":"APIからのテスト",
    "todo":"230503"
}
```
```json:返り値
"追加対象のユーザーが存在しません"
```



## fetchメソッド
`GET`で呼び出して、IDは98とする。
![230503_000002.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/c9f8c407-d633-c331-98b0-6dadf8ee647f.jpeg)
このように、取得したデータが返ってきました。
```json:呼び出し値
{
    "id":98
}
```
```json:返り値
{
    "id": 98,
    "user_id": 9,
    "title": "22222222",
    "todo": "22222222",
    "add_date": "2023-04-22 16:40:01",
    "update_date": "2023-04-22 16:40:01"
}
```


## updateメソッド
`PUT`で呼び出して、IDは98とする。
このように、更新したデータが返ってきました。
![230503_000003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/31bb1cfa-f137-abbb-49aa-9306c6f14226.jpeg)

```json:呼び出し値
{
    "id": 101,
    "title": "APIからのテスト update",
    "todo": "23050 update"
}
```
```json:返り値
{
    "id": 101,
    "user_id": 9,
    "title": "APIからのテスト update",
    "todo": "23050 update",
    "add_date": "2023-05-02 15:09:22",
    "update_date": "2023-05-03T06:12:24.605165Z"
}
```
テーブルを確認して、データが更新されていることが確認できた。
![230503_000004.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/359a573a-db6a-2f53-32b9-a750e6ede8dd.jpeg)


## deleteメソッド
`DELETE`で呼び出して、IDは103とする。
deleteなのでデータは返ってきません。
![230503_000005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/452ab5b5-086d-bd21-949f-2233acf4672b.jpeg)

```json:呼び出し値
{
    "id": 103
}
```
```json:返り値
[]
```
テーブルを確認して、ID=103のデータが削除されていることが確認できた。
![230503_000006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3331757/bf5dc47c-ee9e-40c8-c951-6722ef46ae73.jpeg)


# まとめ
今回は、CRUD処理（参照、追加、削除、更新）をエンドポイントとしたAPIの実装を行ってみましたが、意外に簡単にできた。
バリデーションは簡易なものだけにしていますが、実際には認証処理なども含めて考える必要があると思う。


# 参考
[RESTサービスを触る際の必須ツールPostmanを使ってみました](https://www.xlsoft.com/jp/blog/blog/2017/06/23/post-1638-post-1638/)
[LaravelでAPIを作成する方法！](https://codelikes.com/laravel-api/)
[【Laravel】APIを実装するやり方を解説します](https://www.tairaengineer-note.com/laravel-api-implement/#toc3)