YAF 框架进阶:手把手新增 Service 业务层,告别控制器臃肿

16次阅读
没有评论

熟悉 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 业务层后,项目架构完成质的升级:

  1. 代码解耦:三层职责清晰,各司其职,彻底解决控制器臃肿问题
  2. 逻辑复用:通用业务全局可调用,告别重复代码,提升开发效率
  3. 易于维护:业务逻辑集中管理,迭代、改 Bug、功能扩展更高效
  4. 适配大型项目:架构规范化,支持业务持续迭代,可适配企业级项目开发

整套改造零侵入、不修改框架源码、兼容所有 YAF 版本,新手可直接照搬落地,快速规范项目架构。

后续拓展:可基于该架构继续封装服务缓存、统一事务、依赖注入等能力,让 YAF 项目媲美主流框架的工程化能力。

正文完
可以使用微信扫码关注公众号(ID:xzluomor)
post-qrcode
 0
评论(没有评论)
验证码