概述
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安装:
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 表为例:
- 生成 migration 脚本文件:
bin/phpmig generate user
我们会在 migrations 目录下看到,上述命令创建了 {date}_user.php
文件。
- 我们打开由第1步生成的文件,修改内容为:
<?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
namespace Biz\User\Dao;
use Codeages\Biz\Framework\Dao\GeneralDaoInterface;
interface UserDao extends GeneralDaoInterface
{
}
这里我们直接继承了 Codeages\Biz\Framework\Dao\GeneralDaoInterface
,在 GeneralDaoInterface
中,我们声明了常用的 Dao 接口:
<?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
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
namespace Biz\User\Service;
interface UserService
{
public function getUser($id);
// ...
}
然后创建 User Service 的实现类:
<?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
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
对象实例。