# Laravel API — 项目 CLAUDE.md > 使用 PostgreSQL、Redis 和队列的 Laravel API 真实案例。 > 复制此文件到你的项目根目录,并根据你的服务进行自定义。 ## 项目概述 **技术栈:** PHP 8.2+, Laravel 11.x, PostgreSQL, Redis, Horizon, PHPUnit/Pest, Docker Compose **架构:** 采用控制器 -> 服务 -> 操作的模块化 Laravel 应用,使用 Eloquent ORM、异步工作队列、表单请求进行验证,以及 API 资源确保一致的 JSON 响应。 ## 关键规则 ### PHP 约定 * 所有 PHP 文件中使用 `declare(strict_types=1)` * 处处使用类型属性和返回类型 * 服务和操作优先使用 `final` 类 * 提交的代码中不允许出现 `dd()` 或 `dump()` * 通过 Laravel Pint 进行格式化 (PSR-12) ### API 响应封装 所有 API 响应使用一致的封装格式: ```json { "success": true, "data": {"...": "..."}, "error": null, "meta": {"page": 1, "per_page": 25, "total": 120} } ``` ### 数据库 * 迁移文件提交到 git * 使用 Eloquent 或查询构造器(除非参数化,否则不使用原始 SQL) * 为 `where` 或 `orderBy` 中使用的任何列建立索引 * 避免在服务中修改模型实例;优先通过存储库或查询构造器进行创建/更新 ### 认证 * 通过 Sanctum 进行 API 认证 * 使用策略进行模型级授权 * 在控制器和服务中强制执行认证 ### 验证 * 使用表单请求进行验证 * 将输入转换为 DTO 以供业务逻辑使用 * 切勿信任请求负载中的派生字段 ### 错误处理 * 在服务中抛出领域异常 * 在 `bootstrap/app.php` 中通过 `withExceptions` 将异常映射到 HTTP 响应 * 绝不向客户端暴露内部错误 ### 代码风格 * 代码或注释中不使用表情符号 * 最大行长度:120 个字符 * 控制器保持精简;服务和操作承载业务逻辑 ## 文件结构 ``` app/ Actions/ Console/ Events/ Exceptions/ Http/ Controllers/ Middleware/ Requests/ Resources/ Jobs/ Models/ Policies/ Providers/ Services/ Support/ config/ database/ factories/ migrations/ seeders/ routes/ api.php web.php ``` ## 关键模式 ### 服务层 ```php orders->create($data); } } final class OrderService { public function __construct(private CreateOrderAction $createOrder) {} public function placeOrder(CreateOrderData $data): Order { return $this->createOrder->handle($data); } } ``` ### 控制器模式 ```php service->placeOrder($request->toDto()); return response()->json([ 'success' => true, 'data' => OrderResource::make($order), 'error' => null, 'meta' => null, ], 201); } } ``` ### 策略模式 ```php user_id === $user->id; } } ``` ### 表单请求 + DTO ```php user(); } public function rules(): array { return [ 'items' => ['required', 'array', 'min:1'], 'items.*.sku' => ['required', 'string'], 'items.*.quantity' => ['required', 'integer', 'min:1'], ]; } public function toDto(): CreateOrderData { return new CreateOrderData( userId: (int) $this->user()->id, items: $this->validated('items'), ); } } ``` ### API 资源 ```php $this->id, 'status' => $this->status, 'total' => $this->total, 'created_at' => $this->created_at?->toIso8601String(), ]; } } ``` ### 队列任务 ```php findOrFail($this->orderId); $mailer->sendOrderConfirmation($order); } } ``` ### 测试模式 (Pest) ```php create(); actingAs($user); $response = postJson('/api/orders', [ 'items' => [['sku' => 'sku-1', 'quantity' => 2]], ]); $response->assertCreated(); assertDatabaseHas('orders', ['user_id' => $user->id]); }); ``` ### 测试模式 (PHPUnit) ```php create(); $response = $this->actingAs($user)->postJson('/api/orders', [ 'items' => [['sku' => 'sku-1', 'quantity' => 2]], ]); $response->assertCreated(); $this->assertDatabaseHas('orders', ['user_id' => $user->id]); } } ```