Files
everything-claude-code/docs/zh-CN/examples/laravel-api-CLAUDE.md

312 lines
6.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<?php
declare(strict_types=1);
final class CreateOrderAction
{
public function __construct(private OrderRepository $orders) {}
public function handle(CreateOrderData $data): Order
{
return $this->orders->create($data);
}
}
final class OrderService
{
public function __construct(private CreateOrderAction $createOrder) {}
public function placeOrder(CreateOrderData $data): Order
{
return $this->createOrder->handle($data);
}
}
```
### 控制器模式
```php
<?php
declare(strict_types=1);
final class OrdersController extends Controller
{
public function __construct(private OrderService $service) {}
public function store(StoreOrderRequest $request): JsonResponse
{
$order = $this->service->placeOrder($request->toDto());
return response()->json([
'success' => true,
'data' => OrderResource::make($order),
'error' => null,
'meta' => null,
], 201);
}
}
```
### 策略模式
```php
<?php
declare(strict_types=1);
use App\Models\Order;
use App\Models\User;
final class OrderPolicy
{
public function view(User $user, Order $order): bool
{
return $order->user_id === $user->id;
}
}
```
### 表单请求 + DTO
```php
<?php
declare(strict_types=1);
final class StoreOrderRequest extends FormRequest
{
public function authorize(): bool
{
return (bool) $this->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
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
final class OrderResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'status' => $this->status,
'total' => $this->total,
'created_at' => $this->created_at?->toIso8601String(),
];
}
}
```
### 队列任务
```php
<?php
declare(strict_types=1);
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Repositories\OrderRepository;
use App\Services\OrderMailer;
final class SendOrderConfirmation implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(private int $orderId) {}
public function handle(OrderRepository $orders, OrderMailer $mailer): void
{
$order = $orders->findOrFail($this->orderId);
$mailer->sendOrderConfirmation($order);
}
}
```
### 测试模式 (Pest)
```php
<?php
declare(strict_types=1);
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\assertDatabaseHas;
use function Pest\Laravel\postJson;
uses(RefreshDatabase::class);
test('user can place order', function () {
$user = User::factory()->create();
actingAs($user);
$response = postJson('/api/orders', [
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
]);
$response->assertCreated();
assertDatabaseHas('orders', ['user_id' => $user->id]);
});
```
### 测试模式 (PHPUnit)
```php
<?php
declare(strict_types=1);
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
final class OrdersControllerTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_place_order(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->postJson('/api/orders', [
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
]);
$response->assertCreated();
$this->assertDatabaseHas('orders', ['user_id' => $user->id]);
}
}
```