PHP 业务逻辑写在模型还是控制器

wen PHP项目 1

本文目录导读:

PHP 业务逻辑写在模型还是控制器

  1. 方案一:写在模型(Model)—— 推荐(胖模型,瘦控制器)
  2. 方案二:写在控制器(Controller)—— 不推荐(瘦模型,胖控制器)
  3. 方案三:写在服务层(Service Layer)—— 适合大型复杂项目(推荐升级)
  4. 总结:到底放哪?
  5. 行业通用准则(经验总结)

在 PHP 开发中,业务逻辑应该放在哪里是一个经典问题,但没有绝对的答案,取决于你的架构模式、项目规模和团队规范。

以下是最主流的三种方案,以及它们的适用场景和优缺点:


写在模型(Model)—— 推荐(胖模型,瘦控制器)

这是 MVC 的经典思想,也是目前 Laravel、Symfony 等主流框架社区大力推崇的做法。

核心原则: 控制器只负责“接收请求”和“返回响应”,所有的业务规则、计算、校验都放在模型类中。

  • 代码示例:

    // 控制器
    class OrderController extends Controller {
        public function store(Request $request) {
            // 1. 只做参数接收(或表单请求校验)
            // 2. 调用模型的方法处理业务
            $order = Order::createOrder($request->user(), $request->all());
            return response()->json($order);
        }
    }
    // 模型或服务类
    class Order extends Model {
        public static function createOrder(User $user, array $data) {
            // 业务逻辑:检查库存、计算价格、生成订单号等
            if ($data['quantity'] > $product->stock) {
                throw new \Exception('库存不足');
            }
            // ... 复杂的业务计算
            $order = self::create([...]);
            // 记录日志、发送通知等
            return $order;
        }
    }
  • 优点: 控制器非常薄,易于测试;业务逻辑复用性强(API 和 Web 同时调用同一模型方法)。

  • 缺点: 模型可能会变得非常庞大(上帝类),如果业务复杂,需要进一步拆分服务层。


写在控制器(Controller)—— 不推荐(瘦模型,胖控制器)

通常出现在初学者代码或快速原型中。

核心原则: 所有逻辑都写在 store()update() 等方法里。

  • 缺点: 控制器充满了 if...else 和 SQL 片段,极难测试和维护;业务逻辑无法复用;违反单一职责原则。

写在服务层(Service Layer)—— 适合大型复杂项目(推荐升级)

当业务逻辑非常复杂(涉及多模型交互、外部 API 调用、消息队列)时,把逻辑放在模型里会让模型过于臃肿,此时会引入 Service(服务)层

核心原则: 模型只负责数据表映射(ORM),控制器调用 服务类,服务类专门承载业务规则。

  • 代码示例:

    // 1. 服务类
    class OrderService {
        public function checkout(User $user, Cart $cart) {
            // 聚合操作多个模型
            $total = $this->calculatePrice($cart);
            $order = Order::create([...]); // 这里只写数据操作,不写复杂的业务判断
            $this->paymentGateway->charge($user, $total);
            return $order;
        }
    }
    // 2. 控制器注入服务
    class OrderController extends Controller {
        public function __construct(private OrderService $orderService) {}
        public function store(Request $request) {
            $order = $this->orderService->checkout($request->user(), $request->cart);
            return response()->json($order);
        }
    }

到底放哪?

场景 推荐位置 理由
CRUD 简单逻辑
(增删改查,薄校验)
模型(Model) 直接写在模型静态方法中,简单快捷。
中等复杂度逻辑
(涉及 1-2 个表的计算)
模型(Model) Order::calculateTotal(),保持高内聚。
复杂业务
(涉及 3+ 个模型、第三方 API、事务)
服务层(Service) 分离关注点,避免模型变成“上帝对象”。
绝不 控制器 无论何时,都避免在控制器中写 SQL 或复杂 if。

行业通用准则(经验总结)

  1. 瘦控制器,胖模型 是基础,控制器绝对不写业务逻辑,只负责 HTTP 请求的输入输出。
  2. 业务逻辑与数据访问分离,如果你的 Model 里既包含了验证、计算,又包含了对其他表的 where 查询,且代码超过 500 行,请考虑拆分为 Service + Repository 模式。
  3. 对于 Laravel 开发者:强烈建议利用 FormRequest(做请求参数校验) + Action/Service 类(做业务逻辑) + Model(做数据持久化),不要把校验逻辑写在控制器里,更不要写在模型中通过 $request->validate() 直接调用。

一句话结论: 优先放 模型(Model);如果模型太大,则提升到 服务层(Service);永远不要写在 控制器 里。

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