PHP 控制器复用策略

wen PHP项目 1

本文目录导读:

PHP 控制器复用策略

  1. 基础继承(Base Controller)
  2. 特征复用(Traits)
  3. 服务注入(Service Injection)
  4. 中间件/装饰器模式
  5. 策略模式(Strategy Pattern)
  6. 组合模式(Composition)
  7. 实战建议

在 PHP 开发中,控制器复用是一个常见的架构设计问题,合理的复用可以避免代码重复,提高可维护性,但过度复用会导致耦合和混乱,以下是几种主流的控制器复用策略,从简单到复杂:


基础继承(Base Controller)

这是最基础的复用方式,将公共逻辑放在基类中。

<?php
// BaseController.php
abstract class BaseController {
    protected $request;
    protected $response;
    protected $logger;
    public function __construct(Request $request, Response $response) {
        $this->request = $request;
        $this->response = $response;
        $this->logger = new Logger();
    }
    protected function json($data, $status = 200) {
        $this->response->setStatusCode($status);
        $this->response->setContent(json_encode($data));
        $this->response->headers->set('Content-Type', 'application/json');
        return $this->response;
    }
    protected function validate($data, $rules) {
        $validator = new Validator();
        return $validator->validate($data, $rules);
    }
    protected function authorize($permission) {
        if (!$this->user->hasPermission($permission)) {
            throw new UnauthorizedException();
        }
    }
    protected function log($message, $level = 'info') {
        $this->logger->log($message, $level);
    }
}
// UserController extends BaseController
class UserController extends BaseController {
    public function show($id) {
        $user = User::find($id);
        if (!$user) {
            return $this->json(['error' => 'User not found'], 404);
        }
        return $this->json($user);
    }
}

优点: 简单、易于理解
缺点: 多人协作时容易产生“上帝基类”,继承层级过深时难以维护


特征复用(Traits)

PHP 5.4+ 支持 Traits,用于水平复用代码片段。

<?php
// JsonResponseTrait.php
trait JsonResponseTrait {
    protected function jsonResponse($data, $status = 200) {
        header('Content-Type: application/json');
        http_response_code($status);
        echo json_encode($data);
        exit;
    }
    protected function errorResponse($message, $status = 400) {
        return $this->jsonResponse([
            'success' => false,
            'error' => $message
        ], $status);
    }
}
// ValidationTrait.php
trait ValidationTrait {
    protected function validateRequest(array $rules) {
        $validator = Validator::make($this->request->all(), $rules);
        if ($validator->fails()) {
            $this->errorResponse($validator->errors()->first(), 422);
        }
        return $validator->validated();
    }
}
// AuthTrait.php
trait AuthTrait {
    protected function user() {
        return Auth::user();
    }
    protected function isAdmin() {
        return $this->user() && $this->user()->role === 'admin';
    }
    protected function authorizeAdmin() {
        if (!$this->isAdmin()) {
            $this->errorResponse('Unauthorized', 403);
        }
    }
}
// UserController.php
class UserController {
    use JsonResponseTrait, ValidationTrait, AuthTrait;
    public function update($id) {
        $this->authorizeAdmin();
        $data = $this->validateRequest([
            'name' => 'required|string|max:255',
            'email' => 'required|email'
        ]);
        $user = User::find($id);
        $user->update($data);
        return $this->jsonResponse(['success' => true, 'data' => $user]);
    }
}

优点: 灵活、粒度细分、不强制继承关系
缺点: Traits 之间可能有冲突,需要管理它们的依赖关系


服务注入(Service Injection)

将通用业务逻辑抽到服务类中,控制器只做轻量调用。

<?php
// FileStorageService.php
class FileStorageService {
    public function upload($file, $directory) {
        // 处理上传逻辑
        return $file->store($directory);
    }
    public function delete($path) {
        // 删除逻辑
        Storage::delete($path);
    }
}
// NotificationService.php
class NotificationService {
    public function send($user, $message) {
        // 发送通知
    }
    public function notifyAdmins($message) {
        foreach ($this->getAdmins() as $admin) {
            $this->send($admin, $message);
        }
    }
}
// UserController.php
class UserController extends BaseController {
    protected $fileStorage;
    protected $notification;
    public function __construct(
        FileStorageService $fileStorage, 
        NotificationService $notification
    ) {
        $this->fileStorage = $fileStorage;
        $this->notification = $notification;
    }
    public function store() {
        $user = new User();
        $user->fill($this->request->all());
        $user->avatar = $this->fileStorage->upload($this->request->file('avatar'), 'avatars');
        $user->save();
        $this->notification->send($user, 'Welcome to our platform!');
        return $this->jsonResponse(['success' => true, 'data' => $user], 201);
    }
}

优点: 控制器更轻量,业务逻辑可复用,便于单元测试
缺点: 需要依赖注入容器支持


中间件/装饰器模式

使用中间件处理跨控制器的横切关注点(认证、日志等)。

<?php
// TokenAuthMiddleware.php
class TokenAuthMiddleware {
    public function handle($request, $next) {
        $token = $request->bearerToken();
        if (!$token) {
            return response()->json(['error' => 'Unauthenticated'], 401);
        }
        $user = User::where('api_token', $token)->first();
        if (!$user) {
            return response()->json(['error' => 'Invalid token'], 401);
        }
        app()->instance('currentUser', $user);
        return $next($request);
    }
}
// LoggingMiddleware.php
class LoggingMiddleware {
    public function handle($request, $next) {
        $start = microtime(true);
        $response = $next($request);
        Log::info('Request completed', [
            'uri' => $request->getUri(),
            'duration' => microtime(true) - $start
        ]);
        return $response;
    }
}
// 配置路由中的中间件
Route::group(['middleware' => ['auth.token', 'logging']], function () {
    Route::post('/api/users', 'UserController@store');
    Route::put('/api/users/{id}', 'UserController@update');
});

优点: 关注点分离,可组合复用
缺点: 中间件处理请求生命周期,不适合业务逻辑复用


策略模式(Strategy Pattern)

根据不同的业务场景选择不同的处理策略。

<?php
// PaymentStrategy.php
interface PaymentStrategy {
    public function processPayment($amount);
    public function refund($transactionId);
}
// WeChatPayStrategy.php
class WeChatPayStrategy implements PaymentStrategy {
    public function processPayment($amount) {
        // 微信支付逻辑
        $result = $this->wechat->createPrepayOrder($amount);
        return ['type' => 'wechat', 'params' => $result];
    }
    public function refund($transactionId) {
        // 微信退款逻辑
    }
}
// AlipayStrategy.php
class AlipayStrategy implements PaymentStrategy {
    public function processPayment($amount) {
        // 支付宝支付逻辑
        $result = $this->alipay->createPaymentUrl($amount);
        return ['type' => 'alipay', 'url' => $result];
    }
    public function refund($transactionId) {
        // 支付宝退款逻辑
    }
}
// PaymentController.php
class PaymentController {
    protected $strategies;
    public function __construct(array $strategies) {
        $this->strategies = $strategies; // ['wechat' => WeChatPayStrategy, 'alipay' => AlipayStrategy]
    }
    public function pay($method, $amount) {
        if (!isset($this->strategies[$method])) {
            return response()->json(['error' => 'Invalid payment method'], 400);
        }
        $strategy = $this->strategies[$method];
        $result = $strategy->processPayment($amount);
        return response()->json(['data' => $result]);
    }
}

优点: 高扩展性,符合开闭原则
缺点: 需要良好的接口设计,适合业务分支较多的情况


组合模式(Composition)

通过对象组合而不是继承来复用功能。

<?php
// TokenAuthService.php
class TokenAuthService {
    public function authenticate(Request $request) {
        $token = $request->bearerToken();
        return User::where('api_token', $token)->first();
    }
}
// CRUDService.php
class CRUDService {
    public function create($model, array $data) {
        return $model::create($data);
    }
    public function update($model, $id, array $data) {
        $instance = $model::find($id);
        $instance->update($data);
        return $instance;
    }
    public function delete($model, $id) {
        return $model::destroy($id);
    }
}
// UserController.php
class UserController {
    private $crudService;
    public $authService;
    public function __construct(CRUDService $crudService, TokenAuthService $authService) {
        $this->crudService = $crudService;
        $this->authService = $authService;
    }
    public function store(Request $request) {
        $user = $this->crudService->create(User::class, $request->all());
        return response()->json(['data' => $user], 201);
    }
    public function show(Request $request) {
        $user = $this->authService->authenticate($request);
        // 业务逻辑...
    }
}

优点: 解耦彻底,组合灵活,易于测试
缺点: 需要更多代码量,类增多


实战建议

分层复用策略

在实际项目中,建议分层组合使用:

<?php
// 1. 基础层(BaseController + Traits)
class BaseController {
    use JsonResponseTrait, LoggingTrait, ValidationTrait;
    protected function getUser() {
        return app()->make(TokenAuthService::class)->authenticate(request());
    }
}
// 2. 业务层(Service层)
class UserService {
    public function register(array $data) {
        DB::beginTransaction();
        try {
            $user = User::create($data);
            $this->sendWelcomeNotification($user);
            DB::commit();
            return $user;
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
}
// 3. 控制器薄层(薄控制器)
class AuthController extends BaseController {
    protected $userService;
    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }
    public function register() {
        $data = $this->validate(request(), [
            'name' => 'required|string',
            'email' => 'required|email|unique:users'
        ]);
        try {
            $user = $this->userService->register($data);
            return $this->jsonResponse(['success' => true, 'data' => $user], 201);
        } catch (\Exception $e) {
            return $this->errorResponse($e->getMessage(), 500);
        }
    }
}
// 4. 接口层(中间件 + 路由)
Route::post('/register', 'AuthController@register')->middleware('throttle:10,1');

选择标准建议

场景 推荐策略 理由
小项目/快速开发 基础继承 简单直接
中大型项目 Traits + Service 平衡灵活性和维护性
横切关注点 中间件 认证、日志、限流等
业务分支多 策略模式 支付、导入导出等
高度解耦需求 组合模式 微服务和复杂业务

需要注意的坑

  1. 避免过度继承:继承层级不要超过 3 层
  2. Traits 管理:Traits 之间有依赖时,要用 insteadofas 解决冲突
  3. 控制器中不要写业务逻辑:只做参数解析和响应处理
  4. 依赖注入而非硬编码:使用容器管理服务实例
  5. 保持一致性:团队统一使用一种复用风格

推荐阅读

  • Laravel 官方文档的 Service Container / Service Providers
  • PHP 设计模式 - 组合 vs 继承
  • 领域驱动设计(DDD)中的 Application Service 层

控制器复用没有银弹,建议遵循 “薄控制器、厚服务” 的原则,结合使用:

  • BaseController:存放通用响应和工具方法
  • Traits:细粒度的横切逻辑
  • Service 层:核心业务逻辑
  • 中间件:请求级横切关注点
  • 策略模式:复杂业务分支

根据项目的规模、团队熟练度和需求变更频率来选择合适策略,并在实际开发中不断演进优化。

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