msksgm’s blog

msksgm’s blog

Webエンジニアです.日々の勉強,読書,映画観賞,美術観賞の記録を載せます.

Laravel 6 系でCRUDができるAPIサーバーを作成

概要

Laravel 6 系(PHP 7.3 系)で WebAPI サーバーを作成します。

phpenv で php をインストール済みと docker compose が使える環境であることを仮定します。 php(7.3)のインストール方法は以下の記事を参照。

msksgm.hatenablog.com

今回作成したソースコードは以下。

github.com

実装

project の作成

composer create-project laravel/laravel laravel6-api-sample --prefer-dist "6.*"
cd laravel6-api-sample

DB の準備

プロジェクト直下に docker-composer.yml を作成します。

version: "3.5"
services:
  db:
    image: mysql:5.7.34
    container_name: db-container
    environment:
      MYSQL_DATABASE: "laravel"
      MYSQL_ROOT_PASSWORD: "password"
      TZ: "Asia/Tokyo"
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    expose:
      - "3306"
    ports:
      - "3306:3306"
    volumes:
      - ./initdb.d:/docker-entrypoint-initdb.d
    restart: "always"

.env ファイルをDB_PASSWORDの部分を書き換えます。

DB_PASSWORD=password

DB サーバーのコンテナを立ち上げます

docker composer up -d

少し待って、以下のコマンドが問題なく終了すれば準備完了です。

mysql -u root -ppassword -D laravel -h 127.0.0.1 --port 3306 -e 'exit'

モデルの作成

以下のコマンドで、今回使用するモデルを作成します。

php artisan make:model Article -m

以下の2つのファイルが生成されます。(マイグレーションファイルは実行時刻によって名前が変わります。)

app/Article.php
database/migrations/2021_09_26_013743_create_articles_table.php

また、database/migrations/2021_09_26_013743_create_articles_table.phpを編集することで、articles のカラムに title と body を追加します。

// database/migrations/2021_09_26_013743_create_articles_table.php
// 中略
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title'); // 追加
            $table->text('body'); // 追加
            $table->timestamps();
        });
    }
// 略

編集後、php artisan migrateによって、更新をします。

$ php artisan migrate
Migration table created successfully.

API のコントローラーを作成

API 用のコントローラーを作成します。 --apiによって、API 用のコントローラー(ビュー用でない)に不要な処理をあらかじめ削除して生成してくれます。

php artisan make:controller Api/ArticleController --api

以下のファイルが生成されます。

app/Http/Controllers/Api/ArticleController.php

ダミーデータを作成

Article のダミーデータを作成します。

$ php artisan make:seeder ArticlesTableSeeder
Seeder created successfully.

database/seeds/ArticlesTableSeeder.phpという名前のダミーデータ用のファイルが作成されるので、編集します。

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB; // 追記

    // 中略
    public function run()
    {
        // 以下追記
        DB::table('articles')->insert([
            [
                'title' => 'タイトル1',
                'body' => '内容1'
            ],
            [
                'title' => 'タイトル2',
                'body' => '内容2'
            ],
            [
                'title' => 'タイトル3',
                'body' => '内容3'
            ],
        ]);
    }
    // 略

また、database/seeds/DatabaseSeeder.phpを編集します。

// database/seeds/DatabaseSeeder.php
// 略

    public function run()
    {
        // $this->call(UsersTableSeeder::class);
        $this->call(ArticlesTableSeeder::class); // 追記
    }
// 略

編集が完了したら、以下のコマンドで挿入をおこないます。

$ php artisan db:seed
Seeding: ArticlesTableSeeder
Seeded:  ArticlesTableSeeder (0.11 seconds)
Database seeding completed successfully.

実際に挿入されたか確認をおこないます。

$ mysql -u root -ppassword -D laravel -h 127.0.0.1 --port 3306 -e 'SELECT * FROM articles;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+----+---------------+---------+------------+------------+
| id | title         | body    | created_at | updated_at |
+----+---------------+---------+------------+------------+
|  1 | タイトル1     | 内容1   | NULL       | NULL       |
|  2 | タイトル2     | 内容2   | NULL       | NULL       |
|  3 | タイトル3     | 内容3   | NULL       | NULL       |
+----+---------------+---------+------------+------------+

CRUD を作成

CURD を実装するには、app/Http/Controllers/Api/ArticleController.phpを編集します。
今回は、トランザクション処理やサービスロジックなどの実装を無視しています。

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Article;

class ArticleController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        // READ 全取得
        $articles = Article::all();
        return response($articles, 200);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        // UPDATE
        $article = new Article;

        $article->title = $request->input('title');
        $article->body = $request->input('body');

        $article->save();
        return response($article, 201);
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        // READ 単一取得
        $article = Article::find($id);
        return response($article, 200);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        // UPDATE
        $article = Article::find($id);

        $article->title = $request->input('title');
        $article->body = $request->input('body');

        $article->save();
        return response($article, 200);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        // DELETE
        Article::destroy($id);
        return response('ok', 200);
    }
}

ルーティングを定義

routes/api.phpの最後を以下ように修正しますします。

// routes/api.php
// 中略

Route::group(['middleware' => ['api']], function () {
    Route::get('articles', 'Api\ArticleController@index');
    Route::get('articles/{id}', 'Api\ArticleController@show');
    Route::post('articles', 'Api\ArticleController@store');
    Route::patch('articles/{id}', 'Api\ArticleController@update');
    Route::delete('articles/{id}', 'Api\ArticleController@destroy');
});

ルーティングが追記されたか、以下のコマンドで確認します。

$ php artisan route:list
+--------+----------+-------------------+------+----------------------------------------------------+--------------+
| Domain | Method   | URI               | Name | Action                                             | Middleware   |
+--------+----------+-------------------+------+----------------------------------------------------+--------------+
|        | GET|HEAD | /                 |      | Closure                                            | web          |
|        | GET|HEAD | api/articles      |      | App\Http\Controllers\Api\ArticleController@index   | api          |
|        | POST     | api/articles      |      | App\Http\Controllers\Api\ArticleController@store   | api          |
|        | GET|HEAD | api/articles/{id} |      | App\Http\Controllers\Api\ArticleController@show    | api          |
|        | PATCH    | api/articles/{id} |      | App\Http\Controllers\Api\ArticleController@update  | api          |
|        | DELETE   | api/articles/{id} |      | App\Http\Controllers\Api\ArticleController@destroy | api          |
|        | GET|HEAD | api/user          |      | Closure                                            | api,auth:api |
+--------+----------+-------------------+------+----------------------------------------------------+--------------+

動作確認

curl コマンドによって、処理ができるか確認していきます。

READ 全取得

コマンド

curl --location --request GET 'http://127.0.0.1:8000/api/articles'

レスポンス

[
  {
    "id": 1,
    "title": "タイトル1",
    "body": "内容1",
    "created_at": null,
    "updated_at": null
  },
  {
    "id": 2,
    "title": "タイトル2",
    "body": "内容2",
    "created_at": null,
    "updated_at": null
  },
  {
    "id": 3,
    "title": "タイトル3",
    "body": "内容3",
    "created_at": null,
    "updated_at": null
  }
]

READ 一件取得

コマンド

curl --location --request GET 'http://127.0.0.1:8000/api/articles/1'

レスポンス

{
  "id": 1,
  "title": "タイトル1",
  "body": "内容1",
  "created_at": null,
  "updated_at": null
}

CREATE

コマンド

curl --location --request POST 'http://127.0.0.1:8000/api/articles' \
--header 'Content-Type: application/json' \
--data-raw '{
    "title": "test title",
    "body": "test body"
}'

レスポンス

{
  "title": "test title",
  "body": "test body",
  "updated_at": "2021-09-26 10:53:01",
  "created_at": "2021-09-26 10:53:01",
  "id": 4
}

UPDATE

コマンド

curl --location --request PATCH 'http://127.0.0.1:8000/api/articles/4' \
--header 'Content-Type: application/json' \
--data-raw '{
    "title": "test title update",
    "body": "test body update"
}'

レスポンス

{
  "id": 4,
  "title": "test title update",
  "body": "test body update",
  "created_at": "2021-09-26 10:53:01",
  "updated_at": "2021-09-26 10:54:26"
}

DELETE

コマンド

curl --location --request DELETE 'http://127.0.0.1:8000/api/articles/4' \
--header 'Content-Type: application/json' \
--data-raw ''

レスポンス

ok