Skip to content

概述

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安装:

bash
composer require codeages/biz-framework

开发示例

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

目录结构

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

src/
  Biz/
    User/
      Dao/
        Impl/
          UserDaoImpl.php
        UserDao.php
      Service/
        Impl/
          UserServiceImpl.php
        UserService.php
    Article
      Dao/
        Impl/
          ArticleDaoImpl.php
          CategoryDaoImpl.php
        ArticleDao.php
        CategoryDao.php
      Service/
        Impl/
          ArticleServiceImpl.php
        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 表为例:

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

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

  1. 我们打开由第1步生成的文件,修改内容为:
php
<?php
use Phpmig\Migration\Migration;

class User extends Migration
{
    public function up()
    {
        $container = $this->getContainer();

        $sql = "
            CREATE TABLE `user` (
              `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
              `username` varchar(32) NOT NULL,
              `email` varchar(128) NOT NULL,
              `password` varchar(128) NOT NULL,
              `salt` varchar(64) NOT NULL,
              `created_time` int(10) unsigned NOT NULL,
              `updated_time` int(10) unsigned NOT NULL,
              PRIMARY KEY (`id`),
              UNIQUE KEY `username` (`username`),
              UNIQUE KEY `email` (`email`),
              KEY `mobile` (`mobile`)
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
        ";
        $container['db']->exec($sql);
    }

    public function down()
    {
        $container = $this->getContainer();
        $container['db']->exec("DROP TABLE `user`");
    }
}

编写Dao

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

php
<?php
namespace Biz\User\Dao;

use Codeages\Biz\Framework\Dao\GeneralDaoInterface;

interface UserDao extends GeneralDaoInterface
{

}

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

php
<?php
namespace Codeages\Biz\Framework\Dao;

interface GeneralDaoInterface extends DaoInterface
{
    public function create($fields);

    public function update($id, array $fields);

    public function delete($id);

    public function get($id);

    public function search($conditions, $orderBy, $start, $limit);

    public function count($conditions);

    public function wave(array $ids, array $diffs);
}

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

php
<?php
namespace Biz\User\Dao\Impl;

use Biz\User\Dao\UserDao;
use Codeages\Biz\Framework\Dao\GeneralDaoImpl;

class UserDaoImpl extends GeneralDaoImpl implements UserDao
{
    protected $table = 'user';

    public function declares()
    {
        return array(
            'timestamps' => array('created_time', 'update_time'),
            'serializes' => array(),
            'orderbys' => array(),
            'conditions' => array(
                'username = :username',
            ),
        );
    }
}

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

编写Service

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

php
<?php
namespace Biz\User\Service;

interface UserService
{
    public function getUser($id);

    // ...
}

然后创建 User Service 的实现类:

php
<?php
namespace Biz\User\Service\Impl;

use Codeages\Biz\Framework\Service\BaseService;
use Biz\User\Service\UserService;

class UserServiceImpl extends BaseService implements UserService
{
    public function getUser($id)
    {
        return $this->getUserDao()->get($id);
    }

    // ...

    protected function getUserDao()
    {
        return $this->biz->dao('User:UserDao');
    }
}

这里我们 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 代码大致为:

php
<?php

class UserController
{
    public function showAction($id)
    {
        $user = $this->getUserService()->getUser($id);
        // ...
        return $this->render('user.html.twig', array('user' => $user));
    }
    // ...

    protected function getUserService()
    {
        return $this->biz->service('User:UserService');
    }
}

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