PHP项目框架控制器如何规范编写

wen PHP项目 22

本文目录导读:

PHP项目框架控制器如何规范编写

  1. 核心设计原则
  2. 方法命名与结构规范
  3. 输入处理规范
  4. 业务逻辑分离
  5. 响应输出规范
  6. 中间件与权限控制
  7. 依赖注入
  8. 常见反模式(应避免)
  9. 实战示例(Laravel 完整资源控制器)
  10. 测试可测性

在 PHP 项目中,控制器的规范编写是保证代码可维护性、可测试性和团队协作效率的关键,以下是基于主流 MVC 框架(如 Laravel、ThinkPHP、Symfony、CodeIgniter 等)的通用规范化建议。

核心设计原则

  1. 单一职责:一个控制器方法只处理一类请求(如 createstoreshowupdatedestroy)。
  2. 瘦控制器(Skinny Controller):控制器不写业务逻辑,只负责调度(接收输入 -> 调用服务 -> 返回输出)。
  3. 避免重复代码:通过中间件、表单请求验证、资源控制器等机制减少模板代码。

方法命名与结构规范

RESTful 资源控制器(推荐)

使用 RESTful 风格的资源路由,标准方法:

动词 URI 方法名 用途
GET /users index 显示列表
GET /users/create create 显示创建表单
POST /users store 处理创建请求
GET /users/{id} show 显示单个资源
GET /users/{id}/edit edit 显示编辑表单
PUT/PATCH /users/{id} update 处理更新请求
DELETE /users/{id} destroy 删除资源

示例(Laravel):

class UserController extends Controller
{
    public function index(Request $request): View
    {
        $users = UserService::list($request->validated());
        return view('users.index', compact('users'));
    }
    public function store(UserStoreRequest $request): RedirectResponse
    {
        $user = UserService::create($request->validated());
        return redirect()->route('users.show', $user->id)
                         ->with('success', '用户创建成功');
    }
}

自定义方法命名规范

  • 动词 + 资源:exportUsers, batchDelete, toggleStatus
  • 避免使用 doSomething 等模糊命名

输入处理规范

使用表单请求验证

将验证逻辑从控制器分离到专门的验证类中。

// Laravel 示例
class UserStoreRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name'     => 'required|string|max:255',
            'email'    => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed',
        ];
    }
}
// 控制器中使用
public function store(UserStoreRequest $request): JsonResponse
{
    $data = $request->validated(); // 已验证过的数据
    // ...
}

避免直接操作 $_GET / $_POST

始终通过框架的请求对象获取输入,便于中间件处理、单元测试和全局过滤。

不要信任用户输入

即使验证通过,在业务层也需要进一步处理(如 SQL 注入防护、XSS 过滤)。


业务逻辑分离

Service 层(推荐)

将所有业务逻辑抽取到独立的服务类中。

// 不好的做法
public function store(Request $request)
{
    // 验证、数据库操作、邮件发送、日志记录全部写在这里
}
// 好的做法
public function store(UserStoreRequest $request)
{
    $this->userService->createUser($request->validated());
    // ...
}

Action 类(适用于复杂单一操作)

对于复杂的业务操作(如“订单结算”),可以创建单独的 Action 类。

class CheckoutAction
{
    public function execute(Order $order, User $user): void
    {
        // 复杂的结算流程
    }
}
// 控制器中调用
public function checkout(Request $request)
{
    (new CheckoutAction)->execute($order, auth()->user());
}

响应输出规范

统一返回格式

  • Web 视图:使用 view()redirect(),保持视图层与控制器分离。
  • API 接口:返回 JSON,包含状态码、消息、数据等。
    public function show(int $id): JsonResponse
    {
      $user = UserService::find($id);
      return response()->json([
          'code'    => 200,
          'message' => 'success',
          'data'    => $user,
      ]);
    }

错误处理

在控制器中使用 try-catch 或通过异常处理机制统一返回错误。

public function update(int $id, Request $request): JsonResponse
{
    try {
        $user = UserService::update($id, $request->validated());
        return $this->success($user);
    } catch (NotFoundException $e) {
        return $this->notFound($e->getMessage());
    } catch (ValidationException $e) {
        return $this->validationError($e->getErrors());
    }
}

避免在控制器中直接输出 echovar_dump

控制器只负责返回响应对象或视图,输出应由框架处理。


中间件与权限控制

使用中间件处理横切关注点

  • 认证:auth
  • 授权:can:update,user
  • 限流:throttle:60,1
  • 日志:log.requests

配置示例(Laravel)

// 在控制器构造函数中
public function __construct()
{
    $this->middleware('auth');
    $this->middleware('can:manage-users')->only(['store', 'update', 'destroy']);
    $this->middleware('throttle:30,1')->only(['store']);
}

依赖注入

  1. 通过构造函数或方法参数注入依赖,避免在控制器内部硬编码。

  2. 存储库模式:如果需要直接操作数据库,通过依赖注入 Repository 类,而不是直接使用模型。

    class UserController extends Controller
    {
    private UserService $userService;
    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
    }
    public function index(): View
    {
        $users = $this->userService->getPaginatedList();
        return view('users.index', compact('users'));
    }
    }

常见反模式(应避免)

  1. 上帝控制器:一个控制器包含几十个方法,处理多类资源。
  2. 胖控制器:控制器中包含大量 SQL 查询、业务判断、视图渲染逻辑。
  3. 直接操作全局变量:如 $_SESSION$_FILES 等,应通过框架封装好的对象操作。
  4. 返回 HTML 片段:与前端框架配合时,应使用 API 返回 JSON,而非拼接 HTML。
  5. 硬编码配置:如数据库连接信息、缓存时间等,应通过配置文件或环境变量管理。

实战示例(Laravel 完整资源控制器)

<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\UserStoreRequest;
use App\Http\Requests\UserUpdateRequest;
use App\Services\UserService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class UserController extends Controller
{
    private UserService $userService;
    public function __construct(UserService $userService)
    {
        $this->userService = $userService;
        $this->middleware('auth');
        $this->middleware('can:manage-users')->except(['index', 'show']);
    }
    public function index(Request $request): View
    {
        $users = $this->userService->paginate($request->all());
        return view('admin.users.index', compact('users'));
    }
    public function store(UserStoreRequest $request): JsonResponse
    {
        $user = $this->userService->create($request->validated());
        return response()->json([
            'code'    => 201,
            'message' => '用户创建成功',
            'data'    => $user,
        ], 201);
    }
    public function show(int $id): JsonResponse
    {
        $user = $this->userService->findOrFail($id);
        return response()->json([
            'code'    => 200,
            'message' => 'success',
            'data'    => $user,
        ]);
    }
    public function update(int $id, UserUpdateRequest $request): JsonResponse
    {
        $user = $this->userService->update($id, $request->validated());
        return response()->json([
            'code'    => 200,
            'message' => '用户更新成功',
            'data'    => $user,
        ]);
    }
    public function destroy(int $id): JsonResponse
    {
        $this->userService->delete($id);
        return response()->json([
            'code'    => 200,
            'message' => '用户删除成功',
        ]);
    }
}

测试可测性

  • 控制器应只测试 HTTP 流程(请求、响应状态、重定向),不测试业务结果(那是 Service 层的责任)。
  • 使用依赖注入使得在测试中容易模拟业务服务。

示例测试:

public function test_store_creates_user_and_redirects()
{
    $this->mock(UserService::class, function ($mock) {
        $mock->shouldReceive('create')->once()->andReturn(new User(['id' => 1]));
    });
    $response = $this->post('/users', [
        'name'     => 'Test',
        'email'    => 'test@example.com',
        'password' => 'password',
    ]);
    $response->assertRedirect(route('users.show', 1));
}

规范项 推荐做法 反例
方法职责 单一 RESTful 方法 一个方法处理增删改查
业务逻辑 委托给 Service 或 Action 类 直接写在控制器中
输入验证 Form Request 类 在控制器中用 if 验证
响应输出 返回 ViewJsonResponse 对象 直接 echoexit
依赖管理 构造函数或方法注入 静态方法或 new 实例化
错误处理 异常捕获 + 统一返回格式 使用 die()var_dump()
权限控制 中间件 在控制器方法中手动判断

遵循这些规范,你的 PHP 控制器将更加简洁、可维护、可测试,也便于团队协作和框架升级。

抱歉,评论功能暂时关闭!