グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



PHPのwebアプリケーションフレームワーク「Laravel」でAPIを作ってみる

2024年も始まり、早くも2月に突入してしまいましたが、みなさまはいかがお過ごしでしょうか?

私は、相変わらずバックエンドとフロントエンドを行き来する忙しい日々を送っています。

前回に引き続き、今回はLaravelを使ってAPIを作ってみたいと思います。

前提

  • WSL2
  • ubunts v20.04
  • PHP v8.3
  • Laravel v10.4
  • Postman(ローカルにインストール済み)

開発環境のセットアップとプロジェクトの作成

Laravel Sailを利用して、開発環境を構築し、新しいLaravelプロジェクトを作成しましょう。尚、開発環境は稼働させておきます。

$ curl -s https://laravel.build/myFirstApiInLaravel | bash
$ cd myFirstApiInLaravel
$ sail up -d
  • http://localhost/

モデルの生成と必要な開発要素のセットアップ

以下のコマンドでAPIの実装にとりあえずは必要なものを作れます。便利ですね~。

$ php artisan make:model Product -mfs --api

   INFO  Model [app/Models/Product.php] created successfully.

   INFO  Factory [database/factories/ProductFactory.php] created successfully.

   INFO  Migration [database/migrations/2024_01_11_010607_create_products_table.php] created successfully.

   INFO  Seeder [database/seeders/ProductSeeder.php] created successfully.

   INFO  Controller [app/Http/Controllers/ProductController.php] created successfully.

各オプションは、以下の通りです。

-m モデルに対応するマイグレーションファイルも同時に生成
-f モデルに関連するファクトリを生成
-s モデルに関連するシーダーを生成
–api API用のCRUD処理を持つコントローラーを生成

マイグレーションファイルの編集

必要なカラムを定義しましょう。

今回はシンプルに、textとdescriptionだけ追加します。

# database/migrations/2024_01_11_010607_create_products_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.
     */
    public function up(): void
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('text'); //追加
            $table->longText('description'); //追加
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('products');
    }
};

モデルの編集

追加したカラムにデータを挿入する時等に一括で扱えるよう$fillableを追加します。

# app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    //以下4行追加
    protected $fillable = [
        'title',
        'description',
    ];
}

ファクトリの編集

後ほど、シーダーでテスト用のデータを登録するのですが、1つずつ入れていくのは骨が折れるので、ファクトリを作って一気に登録できるようにしておきましょう。

また、テスト用のデータも全部一緒だと面白くないので、Fakerを使ってダミーデータを登録するようにします。

# database/factories/ProductFactory.php

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Product>
 */
class ProductFactory extends Factory
{
    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'title' => $this->faker->title(), //追加
            'description' => $this->faker->text(), //追加
        ];
    }
}

シーダーの編集

シーダーを編集していきます。

factoryで実際にテストデータを登録する処理をDatabaseSeederクラスのrun()メソッド内に直接記述するのではなく、ProductSeederクラスで記述しています。

DatabaseSeederクラスでは、call()メソッドを使って実行したいシーダーを指定できます。

#database/seeders/ProductSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Models\Product; //追加

class ProductSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        Product::factory()->count(3)->create(); //追加
    }
}
#database/seeders/DatabaseSeeder.php

<?php

namespace Database\Seeders;

// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        //以下3行追加
        $this->call([
            ProductSeeder::class,
        ]);
    }
}

マイグレートとシーディング

$ sail php artisan migrate
$ sail php artisan db:seed

tinkerで確認

tinkerでテストデータが入ったか確認しましょう。

$ sail tinker
> \App\Models\Product::all()
= Illuminate\Database\Eloquent\Collection {#5699
    all: [
      App\Models\Product {#5956
        id: 1,
        title: "Mr.",
        description: "Nulla sit facilis cumque et. Omnis non eligendi suscipit. Occaecati voluptatem quo porro occaecati. Qui sed est officiis ut nihil architecto.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
      App\Models\Product {#5957
        id: 2,
        title: "Dr.",
        description: "Explicabo omnis perferendis repudiandae repudiandae. Fuga pariatur saepe cupiditate dolor.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
      App\Models\Product {#5958
        id: 3,
        title: "Dr.",
        description: "Aut voluptate id officiis ea dolores. Cumque nihil totam maiores. Et odit placeat itaque ut est impedit. Nisi quo ab inventore nemo vel.",
        created_at: "2024-01-11 02:10:37",
        updated_at: "2024-01-11 02:10:37",
      },
    ],
  }

無事登録されていますね。

データの取得

まずはデータを取得してみましょう。

controllerの編集

登録されているデータ全てを返すようにしてみましょう。

response()->json()メソッドでレスポンスヘッダーのContent-Typeapplication/jsonにしつつ、引数として渡された配列をjson_encondeしたjsonを返すことができます。

#app/Http/Controllers/ProductController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Product; //追加

class ProductController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        //以下4行追加
        $products = Product::all();
        return response()->json([
            'products' => $products,
        ], 200);
    }

...省略...

routesの定義

ルーティングを定義しましょう。

Route::apiResource()はAPI開発に特化したCRUDのルーティングを作れる優れものです。

#routes/api.php

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ProductController; //追加

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Route::apiResource('products', ProductController::class); //追加

Postmanで挙動確認

postmanで挙動を確認します。

GETリクエストを送ってみましょう。

無事シーダーで登録したデータ3件が取得できてますね。

データの登録

次にデータを登録する流れを見てみましょう。

Form Requestを作成

Form Requestを作成することで、validationの実装をコントローラーから切り離すことができるみたいですね。

$ sail php artisan make:request StoreProductRequest
# app/Http/Requests/StoreProductRequest.php

<?php

namespace App\Http\Requests;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException; //追加

class StoreProductRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true; //修正
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'title' => ['required', 'string'], //追加
            'description' => ['required', 'string'], //追加
        ];
    }

    //以下追加
    protected function failedValidation(Validator $validator)
    {
        $res = response()->json([
            'message' => 'Failed create product',
            'errors' => $validator->errors(),
        ], 400);
        throw new HttpResponseException($res);
    }
}

controllerの修正

controllerのstore()メソッドを修正します。

送られてきたProductを登録して、成功メッセージと登録されたProductを返すようにします。

# app/Http/Controllers/ProductController.php

...省略...
    
use App\Http\Requests\StoreProductRequest; //追加

class ProductController extends Controller
{
    
...省略...
    
    public function store(StoreProductRequest $request) //修正
    {
        //以下5行追加
        $product = Product::create($request->all());
        return response()->json([
            'message' => 'Product created successfully!',
            'product' => $product,
        ], 200);
    }

...省略...

Postmanで挙動確認

postmanで挙動を確認します。

まず、JSON形式のデータをボディに含めないでPOSTリクエストを送ってみます。

バリデーションが失敗し、エラーメッセージとともに400BadRequestステータスが返ってきてますね。

次に、JSON形式のデータをボディに含めてPOSTリクエストしてみます。

無事にProductが登録され、登録されたProductの情報とともに、200OKステータスが返ってきてますね。

tinkerで確認してみます。

$ sail tinker
> \App\Models\Product::all()
= Illuminate\Database\Eloquent\Collection {#5960
    all: [
    
...省略...

      App\Models\Product {#5965
        id: 4,
        title: "input title",
        description: "input description",
        created_at: "2024-01-11 02:51:08",
        updated_at: "2024-01-11 02:51:08",
      },
    ],
  }

しっかりと登録されていますね。

終わりに

Laravelで初めてAPIを作ってみましたが、思ったより理解しやすかったです。 updateもdeleteも同じ感じで実装できそうですね。

ここ3回程バックエンドの内容だったので、次回はフロントエンドについて書いてみようかなーなんて思ってますが、どうでしょうかね。。。 本格的に寒くなってきたので、体調管理にはくれぐれも気を付けたいですね。

では、また。

この記事を書いた人

KJG
KJGソリューション事業部 システムエンジニア
大学4年時春に文系就職を辞め、エンジニアになることを決意し、独学でRuby、Ruby on Railsを学習。
約1年間の独学期間を経てアーティスへWebエンジニアとして入社。現在はWebエンジニアとして、主にシステムの開発・運用に従事している。
抽象的なもの、複雑なものを言語化して文章にするのが好きで得意。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします