关于 laravel-controllers
面向 Laravel 开发者的控制器规范,强调控制器只处理 HTTP 事务,并将领域逻辑交给其他层。内容涵盖 Web 与 API 控制器、资源命名和查询对象。
主要功能
- 01控制器仅处理 HTTP 事务
- 02将领域逻辑交给 Action
- 03资源控制器使用复数命名
- 04区分 Web 与版本化 API 控制器
- 05用查询对象处理 API 查询
适用场景
- 01检查 Laravel 控制器结构
- 02设计资源及嵌套资源路由
- 03避免在 HTTP 控制器中编写业务规则
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leeovery/ claude-laravel --skill laravel-controllers安装后在 Claude Code 中输入/laravel-controllers确认已加载。
附带文件references/query-objects.md
详细介绍
Laravel 控制器
控制器应当极其精简,只处理 HTTP 相关事务,不包含任何领域逻辑。
相关指南:
- query-objects.md - 用于 API 筛选和排序的查询对象
- Actions - Action 中包含领域逻辑
- form-requests.md - 验证层
- DTOs - 用于数据传输的 DTO
- structure.md - Web 与 API 的组织方式
理念
控制器应当仅执行以下操作:
- 使用类型提示声明依赖项
- 进行验证(通过 Form Request)
- 调用 Action
- 返回响应(资源、重定向、视图)
控制器绝不能:
- 包含领域逻辑
- 直接执行数据库查询
- 执行计算
- 处理复杂业务规则
控制器命名约定
控制器应使用主要资源的复数形式命名:
标准资源控制器
// ✅ CORRECT - Plural resource names
CalendarsController // manages calendar resources
EventsController // manages event resources
OrdersController // manages order resources
UsersController // manages user resources
// ❌ INCORRECT - Singular form
CalendarController
EventController
嵌套资源控制器
对于嵌套资源,将两个资源名称(父级 + 子级)组合起来:
// Route: /calendars/{calendar}/events
CalendarEventsController // manages events within a calendar
// Route: /orders/{order}/items
OrderItemsController // manages items within an order
模式: {ParentSingular}{ChildPlural}Controller
路由:
// Standard resource routes
Route::resource('calendars', CalendarsController::class);
// Nested resource routes
Route::resource('calendars.events', CalendarEventsController::class);
仅使用 RESTful 方法
控制器必须仅使用 Laravel 标准的 RESTful 方法名称。
标准 RESTful 方法
适用于 Web 应用(带表单):
index- 显示资源列表create- 显示创建新资源的表单store- 存储新创建的资源show- 显示指定资源edit- 显示编辑资源的表单update- 更新指定资源destroy- 删除指定资源
适用于 API(无表单视图):
index、show、store、update、destroy- API 不得包含
create或edit方法(这些方法用于 HTML 表单)
禁止的方法名称
资源控制器中绝不能使用自定义方法名称:
// ❌ INCORRECT
class OrdersController extends Controller
{
public function all() { } // Use index
public function get() { } // Use show
public function add() { } // Use store
public function remove() { } // Use destroy
public function cancel() { } // Extract to CancelOrderController
}
非 RESTful 操作:提取为可调用控制器
如果需要的端点不符合标准 RESTful 方法,请将其提取为独立的可调用控制器:
// app/Http/Api/V1/Controllers/CancelOrderController.php
class CancelOrderController extends Controller
{
public function __invoke(
Order $order,
CancelOrderAction $action
): OrderResource {
$order = $action($order);
return OrderResource::make($order);
}
}
路由:
Route::apiResource('orders', OrdersController::class);
Route::post('/orders/{order:uuid}/cancel', CancelOrderController::class);
为什么非 RESTful 操作要使用可调用控制器?
- 符合单一职责原则
- 控制器名称能清晰表达意图
- 可独立测试
- 避免资源控制器变得臃肿
Web 层与公共 API
Web 层控制器
用途: 为应用的 Web 层提供服务(供独立前端使用的 API、Blade 视图或 Inertia)
位置: app/Http/Web/Controllers/
路由: routes/web.php
特点:
- 不进行版本控制
- 可自由更改
- 私有(仅由你的应用使用)
公共 API 控制器
用途: 供外部或第三方使用
位置: app/Http/Api/V1/Controllers/
路由: routes/api/v1.php
特点:
- 进行版本控制(
/api/v1、/api/v2) - 契约稳定
- 破坏性更改需要发布新版本
主要区别: 命名空间(Http\Web 与 Http\Api\V1)。控制器结构相同。
完整控制器示例
<?php
declare(strict_types=1);
namespace App\Http\Web\Controllers;
use App\Actions\Order\CreateOrderAction;
use App\Actions\Order\DeleteOrderAction;
use App\Actions\Order\UpdateOrderAction;
use App\Data\Transformers\Web\OrderDataTransformer;
use App\Http\Controllers\Controller;
use App\Http\Web\Queries\OrderIndexQuery;
use App\Http\Web\Requests\CreateOrderRequest;
use App\Http\Web\Requests\UpdateOrderRequest;
use App\Http\Web\Resources\OrderResource;
use App\Models\Order;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
use Illuminate\Http\Response;
class OrdersController extends Controller
{
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
public function show(Order $order): OrderResource
{
return OrderResource::make($order->load('items', 'customer'));
}
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$order = $action(
user(),
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
public function update(
UpdateOrderRequest $request,
Order $order,
UpdateOrderAction $action
): OrderResource {
$order = $action(
$order,
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
public function destroy(
Order $order,
DeleteOrderAction $action
): Response {
$action($order);
return response()->noContent();
}
}
对于 API 控制器: 结构相同,但命名空间不同(App\Http\Api\V1\Controllers)。
查询对象
对于 API 筛选、排序和包含关联数据,请使用 Spatie Query Builder 的查询对象:
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
授权
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$this->authorize('create', Order::class);
$order = $action(user(), OrderDataTransformer::fromRequest($request));
return OrderResource::make($order);
}
或者使用路由中间件:
Route::post('/orders', [OrdersController::class, 'store'])
->can('create', Order::class);
响应类型
JSON 资源
public function show(Order $order): OrderResource
{
return OrderResource::make($order);
}
集合资源
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
201 已创建
return OrderResource::make($order)->response()->setStatusCode(201);
204 无内容
return response()->noContent();
重定向
return redirect()->route('orders.show', $order);
路由模型绑定
使用路由模型绑定,让控制器更简洁:
Route::get('/orders/{order}', [OrdersController::class, 'show']);
Route::get('/orders/{order:uuid}', [OrdersController::class, 'show']); // Custom key
控制器会自动接收模型:
public function show(Order $order): OrderResource
{
return OrderResource::make($order->load('items', 'customer'));
}
控制器测试
控制器的功能测试:
use function Pest\Laravel\actingAs;
use function Pest\Laravel\postJson;
it('creates an order', function () {
$user = User::factory()->create();
$data = CreateOrderData::testFactory()->make();
actingAs($user)
->postJson('/orders', $data->toArray())
->assertCreated()
->assertJsonStructure(['data' => ['id', 'status']]);
});
it('requires authentication', function () {
postJson('/orders', [])->assertUnauthorized();
});
it('validates required fields', function () {
actingAs(User::factory()->create())
->postJson('/orders', [])
->assertUnprocessable()
->assertJsonValidationErrors(['customer_email', 'items']);
});
常见错误
❌ 在控制器中编写领域逻辑
// BAD
public function store(Request $request)
{
$order = Order::create($request->validated());
$order->items()->createMany($request->items);
$total = $order->items->sum('total');
$order->update(['total' => $total]);
}
✅ 委托给 Action
// GOOD
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
): OrderResource {
$order = $action(
user(),
OrderDataTransformer::fromRequest($request)
);
return OrderResource::make($order);
}
❌ 在控制器中执行数据库查询
// BAD
public function index()
{
$orders = Order::with('items')
->where('status', 'pending')
->latest()
->paginate();
}
✅ 使用查询对象
// GOOD
public function index(OrderIndexQuery $query): AnonymousResourceCollection
{
return OrderResource::collection($query->jsonPaginate());
}
总结
控制器是 HTTP 适配器:
- 接收 HTTP 请求
- 通过表单请求进行验证
- 调用 Action(必要时传入 DTO)
- 通过资源返回 HTTP 响应
所有领域逻辑都应放在 Action 中,而不是控制器中。