Copilot + Claude で Laravel の Web API を作る

一番手慣れた手段として、Laravel で作ってみます。

OpenAPI 仕様 mos-api.yaml に従って Controller を作って。

これまで OpenAPI の yaml/json から各プログラム言語のコードを作るときはジェネレーターを動かす必要があったのですが、Claude Sonnet を使うと一切必要ありません。

  • routes/api.php にルーティングの追加
  • app/Models/* にモデルクラスの追加
  • app/Http/Controllers/* にコントローラークラスの追加
  • database/migrations/* にマイグレーションコードの追加

この手のコンバートツールは個人で作りがちだし、開発プロジェクト内でも標準ツールにしがちなのですが、フレームワークのバージョンが上がったりすると乖離してしまうので自作はお薦めしません。できることならば OSS にある標準のものを使うか、以後は Claude Sonnet のような生成AIものにしておくか、というところです。

あと、windows 上の mysql を動かすと毎回出てくるエラーにも対処しておきます。

SQLSTATE[HY000]: General error: 1709 Index column size too large. The maximum column size is 767 bytes. (Connection: mysql, SQL: alter table `users` add unique `users_email_unique`(`email`)) 

の対処は?
class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        //
    }

    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        // Set default string length to 191 for MySQL compatibility
        Schema::defaultStringLength(191);
    }
}

これで php artisan migrate:fresh が正常に終了します。

標準のままだと、/api のプレフィックスが付くので、これを修正します。

api/mos/api/categories



mos/api/categories

にするには?

以下のコマンドを動かせばよいそうです。

php artisan route:list --path=mos

postman で動作確認

postman を使って web api にアクセスしてみます。これは成功

localhost:8000/mos/api/categories

OpenAPI SwaggerUI でアクセス

vscode の swaggerUI でアクセスしてみます。

どうやら CORS でエラーになっているっぽいです。

swaggerUI でアクセスをすると CORS のエラーがでます。

実は、この対処だけではエラーになるので、Claude にログも確認してもらいます。

どうやら、Laravel に最初に入っている CORS の機能と、Sanctumミドルウェア、CORS ミドルウェアの兼ね合いが良くなかったみたいですね。

このあたりの laravel の middleware の設定が良く分かっていないので、なんとも言えないのですが、ひとまずエラーが無くなります。

app.php

<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        apiPrefix: '',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware): void {
        // Add CORS middleware alias
        $middleware->alias([
            'cors' => \Illuminate\Http\Middleware\HandleCors::class,
        ]);

        // Apply CORS middleware globally to API routes
        $middleware->api(append: [
            \Illuminate\Http\Middleware\HandleCors::class,
        ]);
    })
    ->withExceptions(function (Exceptions $exceptions): void {
        //
    })->create();

cors.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['mos/api/*', 'api/*'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

Seeder を入れる

カテゴリ(categories)と商品(products)に初期値を入れたいので、これを Claude に作って貰います。

categories と puroducts の seed のサンプルを作成して。

ひな形ができたので、これをハンバーガー注文サイトのカテゴリと商品名に手作業で修正。

CategorySeeder.php

class CategorySeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run(): void
    {
        $categories = [
            [
                'id' => 1,
                'slug' => 'special1',
                'title' => '今月のお薦め',
                'description' => '今月のお薦め商品を紹介します。',
                'image' => 'special1.jpg',
                'sortid' => 1,
                'display' => 1,
            ],
            [
                'id' => 2,
                'slug' => 'special2',
                'title' => 'ネット注文特別価格メニュー',
                'description' => '',
                'image' => 'special2.jpg',
                'sortid' => 2,
                'display' => true,
            ],

ProductSeeder.php

class ProductSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */


    public function run(): void
    {
        // カテゴリの最大値からランダムidを取得
        function fake_category_id() {
            $maxId = Category::max('id');
            return rand(1, $maxId);
        }

        $products = [
            [
                'id' => 1,
                'slug' => 'burger1',
                'name' => 'モスバーガー',
                'description' => '',
                'image' => 'burger1.jpg',
                'price' => 440.00,
                'sortid' => 1,
                'display' => true,
                'category_id' => fake_category_id(),
            ],
            [
                'id' => 2,
                'slug' => 'burger2',
                'name' => 'モスチーズバーガー',
                'description' => '',
                'image' => 'burger2.jpg',
                'price' => 480.00,
                'sortid' => 2,
                'display' => true,
                'category_id' => fake_category_id(),
            ],

たぶん、テスト用に大量に作成する手段はあるのですが、ひとまず新人研修で作成したものをコピーします。このあたり、Copilot にテストデータを作って貰ってもいいと思います。

OpenAPI の戻り値を修正する

web api で  GET /products/{id} を呼び出したときに、category_id がそのまま返ってきているので、これを categories を検索するよう変更します。

mos-api.yaml の GET /products/{id} を呼び出した時に、
category_id と一緒に categories の内容も戻して。

ProductController.php と mos-api.yaml の両方を修正してくれます。

GET /categories の戻りが配列だけになっているので、以前のクライアントで受けられるように items の中に配列を作ります。正確には mos-api.yaml を直すのですが、直し方がわからないのでレスポンスの JSON 例を示して Copilot に修正してもらいましょう。

web api で GET /categories のレスポンスを次のように変更して。

JSON 形式の例

{
"items": [
{
"id": 1,
"slug": "special1",
"title": "今月のお薦め",
"description": "今月のお薦め商品を紹介します。",
"image": "",
"sortid": 1,
"display": true,
"created_at": "2025-06-05T11:57:01+09:00",
"updated_at": "2025-06-05T11:57:01+09:00",
"deleted_at": null
},
...
{
"id": 10,
"slug": "sidemenu3",
"title": "デザート",
"description": "",
"image": "",
"sortid": 22,
"display": true,
"created_at": "2024-06-19T02:49:19+09:00",
"updated_at": "2024-06-19T02:49:19+09:00",
"deleted_at": null
}
],
"total": 10
}

この部分はプロトタイプや社内ツールならば Claude の提案したテンプレートでもよいのですが、Web API の場合は既存の形式に合わせることも多いので妥協せずに少し突っ込みます。

指定したカテゴリ内の商品を取得する場合、/products?category_id=1 で実装済みではあるのですが、これも /products/slug/{category_slug} 形式に修正します。

カテゴリ内の商品一覧を取得する web api を
/products/slug/{category_slug}
のようにカテゴリの slug を使って。
POST /orders では、
次の JSON 形式の例に変更して。

{
total_price: 0,
total_quantity: 0,
items: [
{
id: 1,
price: 1000,
quantity: 2,
},
{
id: 2,
price: 2000,
quantity: 3,
},
],
}

これでほぼ完成です。後は

  • ログイン機能の追加
  • 管理画面で使うためのルーティングをログイン状態へ移動
  • CRFS, CROS 機能の復活

あたりでしょうか。

ファイル数が多くなると、指示によってあちこちのファイルの手を入れることになるので時間が掛かります。この現象自体はAIであっても人間であっても同じなのですが、MVC パターンの Web API の場合は、Model/Controller そしてルーティングの3か所に手をいれないといけないのが難点です。この現象はもともとの MVC パターンのアプリケーションからあるもので今に限ったものではないのですが、人が修正するときは3か所同時に手をいれないといけないので大変でした。

そういう意味では、Claude Sonnet の場合は多少時間がかかるものの複数のファイルに手を入れることに躊躇はしません。たまにコンパイルが出来ないコードを吐き出しますが、Claude 自らコンパイルエラーを読み解いたり、実行時のエラーを読み解くことでコードを修正していきます。その部分では開発者はナビゲーターとしての役割やレビュアやテスターの視点を持てるのが便利なところです。自分がやると「面倒くさい」が先に立ってしまって、オブジェクト指向的にもう少し依存関係が少なくならないか?と考えてしまいますからね。

参照

mos-ai-sample/src/webapi/laravel-webapi-sample at master · moonmile/mos-ai-sample https://github.com/moonmile/mos-ai-sample/tree/master/src/webapi/laravel-webapi-sample

カテゴリー: 開発 パーマリンク