熟悉 YAF 框架的开发者都知道,YAF 是一款轻量、高性能的 PHP 框架,原生仅提供Controller(控制器)+ Model(模型) 的基础 MVC 分层。
在项目初期、业务简单的场景下,原生架构完全够用。但随着业务迭代、接口增多、逻辑复杂化,很多开发者会遇到两个核心痛点:
- 控制器臃肿:参数校验、业务判断、数据组装、多模型调用等逻辑全部堆在 Controller 中,代码冗长、可读性极差
- 业务逻辑无法复用:相同的业务流程(如用户登录校验、订单状态处理、数据统计),多个控制器需要重复编写,冗余度极高
- 维护与测试困难:业务逻辑与请求耦合,单元测试难以开展,迭代改 Bug 容易牵一发而动全身
解决以上问题的最优方案,就是给 YAF 框架手动新增 Service 业务层,搭建 Controller → Service → Model 的标准三层架构。今天就手把手带大家从零实现,全程实战、可直接落地。
一、为什么 YAF 必须加 Service 层?
先明确三层架构的核心职责,彻底理清分层逻辑:
- Controller 控制层:只负责「接收请求、参数校验、调用服务、返回响应」,不写任何核心业务逻辑,保持极致轻薄
- Service 业务层(新增):核心业务逻辑载体,封装所有业务规则、数据处理、多模型联动、事务控制,可全局复用
- Model 数据层:只负责数据库 CURD 操作,单纯做数据持久化,不参与业务判断
简单来说:Controller 管“请求调度”,Service 管“业务逻辑”,Model 管“数据存取”,三层职责完全解耦,完美适配中大型项目迭代。
二、规范 YAF 项目目录结构
YAF 原生不支持 Service 目录,我们需要手动创建标准化目录,贴合 YAF 自动加载规范,无需修改框架核心源码。最终目录结构如下(适配 YAF 标准项目):
application/
├── controllers/ # 控制器层(原有)
├── models/ # 数据模型层(原有)
├── services/ # 【新增】业务服务层
│ ├── BaseService.php # 服务基类(统一封装公共方法)
│ └── UserService.php # 业务模块服务(示例:用户服务)
├── library/ # 公共工具类
└── conf/ # 配置文件
核心规范:按业务模块拆分 Service 文件,一个业务模块对应一个 Service,如用户模块 UserService、订单模块 OrderService,结构清晰易维护。
三、手把手实现 Service 层(可直接复制使用)
整体分为三步:创建服务基类 → 实现具体业务服务 → 控制器调用服务,全程无侵入、兼容原生 YAF 特性。
1. 创建 Service 基类 BaseService
在 application/services/ 下新建 BaseService.php,封装所有服务通用的公共能力,避免重复代码:
<?php
/**
* 服务层基类
* 所有业务服务统一继承此类
*/
class BaseService
{
// 全局静态服务实例,实现单例调用
protected static $instance = [];
/**
* 单例获取服务实例
* @return static
*/
public static function getInstance()
{
$class = static::class;
if (!isset(self::$instance[$class])) {
self::$instance[$class] = new static();
}
return self::$instance[$class];
}
// 统一返回成功数据
public function success($data = [], $msg = 'success')
{
return [
'code' => 200,
'msg' => $msg,
'data' => $data
];
}
// 统一返回失败数据
public function error($msg = 'fail', $code = 400)
{
return [
'code' => $code,
'msg' => $msg,
'data' => []
];
}
}
通过单例模式实现服务全局唯一实例,减少资源消耗,同时统一接口返回格式,规范项目输出。
2. 实现具体业务 Service(示例:用户服务)
以常见的「用户信息查询、用户登录校验」为例,新建 UserService.php,封装核心业务逻辑:
<?php
/**
* 用户业务服务层
* 所有用户相关业务逻辑统一封装此处
*/
class UserService extends BaseService
{
/**
* 根据ID获取用户信息
* @param int $uid 用户ID
* @return array
*/
public function getUserInfoById(int $uid): array
{
// 1. 业务参数校验(业务层校验,区别于控制器请求校验)
if ($uid <= 0) {
return $this->error('用户ID不合法');
}
// 2. 调用Model层获取数据
$userModel = new UserModel();
$userInfo = $userModel->findById($uid);
// 3. 封装业务逻辑、数据处理
if (empty($userInfo)) {
return $this->error('用户不存在');
}
// 过滤敏感字段
unset($userInfo['password']);
return $this->success($userInfo);
}
/**
* 用户登录业务校验
* @param string $username 用户名
* @param string $password 密码
* @return array
*/
public function loginCheck(string $username, string $password): array
{
// 业务规则校验
if (empty($username) || empty($password)) {
return $this->error('用户名或密码不能为空');
}
// 调用模型查询用户
$userModel = new UserModel();
$user = $userModel->findByUsername($username);
if (empty($user) || !password_verify($password, $user['password'])) {
return $this->error('用户名或密码错误');
}
// 可扩展:登录日志记录、令牌生成、状态校验等复杂业务
return $this->success(['uid' => $user['id'], 'username' => $user['username']]);
}
}
可以看到,所有核心业务判断、数据处理、规则校验全部放在 Service,Model 只负责查询数据,完全符合单一职责原则。
3. 控制器调用 Service(极致精简)
改造原有控制器,删除臃肿业务逻辑,只保留请求接收和响应返回,以 UserController 为例:
<?php
class UserController extends Yaf_Controller_Abstract
{
/**
* 获取用户信息接口
*/
public function infoAction()
{
// 1. 接收请求参数
$uid = (int)$this->getRequest()->getParam('uid', 0);
// 2. 调用Service业务层
$res = UserService::getInstance()->getUserInfoById($uid);
// 3. 统一返回响应
return $this->getResponse()->setBody(json_encode($res, JSON_UNESCAPED_UNICODE));
}
/**
* 用户登录接口
*/
public function loginAction()
{
$username = trim($this->getRequest()->getParam('username', ''));
$password = trim($this->getRequest()->getParam('password', ''));
// 极简调用业务逻辑
$res = UserService::getInstance()->loginCheck($username, $password);
return $this->getResponse()->setBody(json_encode($res, JSON_UNESCAPED_UNICODE));
}
}
改造后的控制器代码极度简洁,可读性、维护性大幅提升,新增业务只需修改对应 Service,无需改动控制器。
四、配置 YAF 自动加载(关键步骤)
YAF 默认不会加载 services 目录文件,需要在项目启动文件中注册自动加载规则,打开 application/Bootstrap.php,添加初始化方法:
<?php
class Bootstrap extends Yaf_Bootstrap_Abstract
{
/**
* 注册服务层自动加载
*/
public function _initServiceLoader()
{
// 注册services目录到自动加载路径
Yaf_Loader::getInstance()->registerLocalNamespace('services');
}
}
添加后无需手动引入文件,全局可直接调用所有 Service 类,完全适配 YAF 自动加载机制,无任何兼容问题。
五、YAF Service 层开发规范 & 避坑指南
结合实战经验,整理一套稳定落地的开发规范,避免后期架构混乱:
1. 分层绝对隔离
- Controller 禁止直接操作 Model,必须通过 Service 中转
- Service 不接收请求参数,参数由 Controller 预处理后传入
- Model 不写业务逻辑,只做数据查询、新增、修改、删除
2. 服务拆分原则
- 按业务模块拆分:用户、订单、商品、支付独立 Service
- 通用公共业务(如短信、文件上传、日志)可新建
CommonService - 单个 Service 方法只做一件事,方法职责单一、命名见名知意
3. 常见避坑点
- 禁止在 Service 中使用
$this->getRequest()获取请求,避免与请求耦合 - 所有 Service 统一继承 BaseService,保证返回格式、公共方法统一
- 不重复实例化 Service,统一使用单例
getInstance()调用 - 复杂事务、多模型联动逻辑,全部封装在 Service 层统一处理
六、总结:新增 Service 层的核心价值
YAF 原生轻量高效,但原生 MVC 架构仅适合小型项目。新增 Service 业务层后,项目架构完成质的升级:
- 代码解耦:三层职责清晰,各司其职,彻底解决控制器臃肿问题
- 逻辑复用:通用业务全局可调用,告别重复代码,提升开发效率
- 易于维护:业务逻辑集中管理,迭代、改 Bug、功能扩展更高效
- 适配大型项目:架构规范化,支持业务持续迭代,可适配企业级项目开发
整套改造零侵入、不修改框架源码、兼容所有 YAF 版本,新手可直接照搬落地,快速规范项目架构。
后续拓展:可基于该架构继续封装服务缓存、统一事务、依赖注入等能力,让 YAF 项目媲美主流框架的工程化能力。