概述

BizFramework 定位为简单、易用的业务层框架,采用经典的 Service/Dao 模式来编写项目的业务逻辑,可以跟 Symfony、Laravel、Silex、Lumen、Phalcon 等框架搭配使用。

那么已经有了 Symfony、Laravel 这样流行的框架,Biz Framework 的意义又在哪里呢?

Symfony、Laravel 等框架完整的 Web 开发框架,每个框架都有各自的业务层解决方案,并且跟 Web 框架本身绑定,无法做到各个项目之间共享业务层代码。而往往不同的项目会基于实际情况采用不同的 Web 开发框架,虽然各个项目之间的 UI 相差会比较大,但一些通用模块的业务逻辑其实是一致的,比如用户注册、私信等。

Biz Framework 的目标,给出一套组织业务代码的约定以及最佳实践,以让一些通用的模块的业务代码,能跨项目、跨 Web 开发框架的重用。使用 Biz Framework 能给团队带来的好处有:

  • 提高生产效率,减少重复开发。
  • 能保证一些通用模块的质量,一些通用模块往往经过各个项目不断的锤炼,会有较高的质量。
  • 方便团队各 Team 之间人员流动,因为大家都采用了一致的业务层框架,很容易就能上手新项目。

集成

EduSoho 默认已集成Biz Framework,相关集成代码可参见app/AppKernel.php。如新的应用集成,可通过Composer安装:

  1. composer require codeages/biz-framework

开发示例

在应用运行的整个生命周期,Biz 对象贯穿始终,它是业务层的依赖注入容器(Dependency Injection Container)对象。Biz 类直接继承自Pimple\Container,拥有 Pimple 的所有容器特性,请阅读 Pimple 的文档,来了解容器的使用。

目录结构

以下为含 User, Article 两个业务模块的推荐的目录结构示例:

  1. src/
  2. Biz/
  3. User/
  4. Dao/
  5. Impl/
  6. UserDaoImpl.php
  7. UserDao.php
  8. Service/
  9. Impl/
  10. UserServiceImpl.php
  11. UserService.php
  12. Article
  13. Dao/
  14. Impl/
  15. ArticleDaoImpl.php
  16. CategoryDaoImpl.php
  17. ArticleDao.php
  18. CategoryDao.php
  19. Service/
  20. Impl/
  21. ArticleServiceImpl.php
  22. ArticleService.php

命名约定

  • 约定应用级业务层的顶级命名空间为 Biz,命名空间的第二级为模块名;
  • 约定 Service 接口的接口名以 Service 作为后缀,命名空间为 Biz\模块名\Service, 上述例子中 UserService 的完整类名为 Biz\User\Service\UserService
  • 约定 Service 实现类的类名以 ServiceImpl 作为后缀,命名空间为 Biz\模块名\Service\Impl, 上述例子中 UserServiceImpl 的完整类名为 Biz\User\Service\Impl\UserServiceImpl
  • Dao 接口、类名的命名约定,同 Sevice 接口、类名的命名约定。

创建数据库

在编写业务代码之前,我们首先需要创建数据库,Biz Framework 使用 phpmig 作为数据库变更脚本的引擎,并约定项目根目录下的 migrations 目录为默认的 migration 脚本所在目录。

以创建 user 表为例:

  • 生成 migration 脚本文件:
  1. bin/phpmig generate user

我们会在 migrations 目录下看到,上述命令创建了 {date}_user.php 文件。

  • 我们打开由第1步生成的文件,修改内容为:
  1. <?php
  2. use Phpmig\Migration\Migration;
  3. class User extends Migration
  4. {
  5. public function up()
  6. {
  7. $container = $this->getContainer();
  8. $sql = "
  9. CREATE TABLE `user` (
  10. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  11. `username` varchar(32) NOT NULL,
  12. `email` varchar(128) NOT NULL,
  13. `password` varchar(128) NOT NULL,
  14. `salt` varchar(64) NOT NULL,
  15. `created_time` int(10) unsigned NOT NULL,
  16. `updated_time` int(10) unsigned NOT NULL,
  17. PRIMARY KEY (`id`),
  18. UNIQUE KEY `username` (`username`),
  19. UNIQUE KEY `email` (`email`),
  20. KEY `mobile` (`mobile`)
  21. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  22. ";
  23. $container['db']->exec($sql);
  24. }
  25. public function down()
  26. {
  27. $container = $this->getContainer();
  28. $container['db']->exec("DROP TABLE `user`");
  29. }
  30. }

编写Dao

以编写 User Dao 为例,我们首先需要创建 UserDao接口

  1. <?php
  2. namespace Biz\User\Dao;
  3. use Codeages\Biz\Framework\Dao\GeneralDaoInterface;
  4. interface UserDao extends GeneralDaoInterface
  5. {
  6. }

这里我们直接继承了 Codeages\Biz\Framework\Dao\GeneralDaoInterface,在 GeneralDaoInterface 中,我们声明了常用的 Dao 接口:

  1. <?php
  2. namespace Codeages\Biz\Framework\Dao;
  3. interface GeneralDaoInterface extends DaoInterface
  4. {
  5. public function create($fields);
  6. public function update($id, array $fields);
  7. public function delete($id);
  8. public function get($id);
  9. public function search($conditions, $orderBy, $start, $limit);
  10. public function count($conditions);
  11. public function wave(array $ids, array $diffs);
  12. }

同样我们的 UserDao 实现类,也可继承自 Codeages\Biz\Framework\Dao\GeneralDaoImpl

  1. <?php
  2. namespace Biz\User\Dao\Impl;
  3. use Biz\User\Dao\UserDao;
  4. use Codeages\Biz\Framework\Dao\GeneralDaoImpl;
  5. class UserDaoImpl extends GeneralDaoImpl implements UserDao
  6. {
  7. protected $table = 'user';
  8. public function declares()
  9. {
  10. return array(
  11. 'timestamps' => array('created_time', 'update_time'),
  12. 'serializes' => array(),
  13. 'orderbys' => array(),
  14. 'conditions' => array(
  15. 'username = :username',
  16. ),
  17. );
  18. }
  19. }

这样我们就拥有了 GeneralDaoInterface 接口所定义的所有方法功能。关于方法 declares() 的详细说明,请参见文档 编写Dao

编写Service

以编写 UserService 为例,我们首先需创建 UserService 接口:

  1. <?php
  2. namespace Biz\User\Service;
  3. interface UserService
  4. {
  5. public function getUser($id);
  6. // ...
  7. }

然后创建 User Service 的实现类:

  1. <?php
  2. namespace Biz\User\Service\Impl;
  3. use Codeages\Biz\Framework\Service\BaseService;
  4. use Biz\User\Service\UserService;
  5. class UserServiceImpl extends BaseService implements UserService
  6. {
  7. public function getUser($id)
  8. {
  9. return $this->getUserDao()->get($id);
  10. }
  11. // ...
  12. protected function getUserDao()
  13. {
  14. return $this->biz->dao('User:UserDao');
  15. }
  16. }

这里我们 UserServiceImpl 继承了 Codeages\Biz\Framework\Service\BaseService ,使得 UserServiceImpl 可以自动被注入Biz容器对象。

getUserDao() 方法中,我们通过 $this->biz->dao('User:UserDao'),获得了 User Dao 对象实例Biz\User\Dao\Impl\UserDaoImpl,具体参见 获取 Dao / Service 的实例对象

使用Service

以实现显示用户的个人主页为例,我们的 Controller 代码大致为:

  1. <?php
  2. class UserController
  3. {
  4. public function showAction($id)
  5. {
  6. $user = $this->getUserService()->getUser($id);
  7. // ...
  8. return $this->render('user.html.twig', array('user' => $user));
  9. }
  10. // ...
  11. protected function getUserService()
  12. {
  13. return $this->biz->service('User:UserService');
  14. }
  15. }

其中 getUserService() 同上个例子中的 getUserDao() 原理类似,通过调用 getUserService() 我们获得了Biz\User\Service\Impl\UserServiceImpl 对象实例。