关于 laravel-controllers
面向 Laravel 开发者的控制器设计指南,规定控制器仅处理 HTTP 职责,并将业务逻辑交给 Action。还涵盖 REST 命名、Web 与公共 API 组织、查询对象和路由模型绑定。
主要功能
- 01控制器保持精简
- 02采用标准 REST 方法
- 03非 REST 操作拆分为单动作控制器
- 04区分 Web 与版本化 API
- 05使用查询对象处理 API 查询
- 06提供授权与模型绑定模式
适用场景
- 01规划资源控制器的命名与方法
- 02将业务规则从控制器移至 Action
- 03组织 Web 层和公共 API 控制器
- 04为 API 筛选、排序及关联数据设计查询
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ leeovery/ agentic-skills --skill laravel-controllers安装后在 Claude Code 中输入/laravel-controllers确认已加载。
附带文件references/query-objects.md
详细介绍
Laravel 控制器
控制器应当极为精简。它们只处理 HTTP 相关工作,不包含任何领域逻辑。
相关指南:
- query-objects.md - 用于 API 筛选和排序的查询对象
- Actions - Actions 包含领域逻辑
- form-requests.md - 验证层
- DTOs - 用于数据传输的 DTO
- structure.md - Web 与 API 的组织方式
理念
控制器只能:
- 声明依赖类型
- 验证(通过 Form Requests)
- 调用 Actions
- 返回响应(资源、重定向、视图)
控制器绝不能:
- 包含领域逻辑
- 直接执行数据库查询
- 执行计算
- 处理复杂业务规则
控制器命名约定
控制器应使用主要资源的复数形式命名:
标准资源控制器
// ✅ 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);
路由模型绑定
使用路由模型绑定,让控制器更简洁:
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());
}