php 领域模型,[php]领域模型和数据映射器

业务逻辑层使用的是领域模型,因为它能使用数据映射器中的大部分模式。

“万物皆对象”,领域模型就是对于项目中各种个体的抽象表达,就是一个类。它常常被描述为一组属性及附加的操作。它们是做某些相关事的某个东西。

领域模型的复杂性主要来自于尝试使模型纯粹(pure),即将领域模型从应用中其他层中分离出来。把领域模型的参与者从表现层分离出来不难,但将这些参与者从数据层中分离出来则不太容易。在理想情形下,领域模型应该只包含它要表达和解决的问题,但在现实中领域模型很难完全去除数据库操作。

领域模型常常映射到数据库结构上。通过将模型与数据库分离,整个层会更加容易测试,而且不会受到数据库结构的改变的影响,也不会受到存储机制的影响。领域模型只关心要完成的核心工作和承担的责任。领域模型设计的简单还是复杂取决于业务逻辑的复杂度。

先来个简单例子(之后都是用这个例子):一个Classroom有多个Student,每个Student有个Score。

sql脚本:

CREATE TABLE `classroom` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(32) NOT NULL,

PRIMARY KEY (`id`)

)

CREATE TABLE `student` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`cid` int(11) NOT NULL,

`name` varchar(16) NOT NULL,

PRIMARY KEY (`id`),

KEY `cs_id` (`cid`),

CONSTRAINT `cs_id` FOREIGN KEY (`cid`) REFERENCES `classroom` (`id`)

)

CREATE TABLE `score` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`sid` int(11) NOT NULL,

`course_name` varchar(32) NOT NULL,

`score` tinyint(4) NOT NULL,

PRIMARY KEY (`id`,`sid`,`course_name`),

KEY `sc_id` (`sid`),

CONSTRAINT `sc_id` FOREIGN KEY (`sid`) REFERENCES `student` (`id`)

)

领域模型抽象基类:DomainObject

namespace demo\domain;

/**

* 领域模型抽象基类

*/

abstract class DomainObject {

protected $id;

public function __construct($id = null) {

if (is_null($id)) {

$id = -1;

} else {

$this->id = $id;

}

}

public function getId() {

return $this->id;

}

public function setId($id) {

$this->id = $id;

}

// 现在比较简单,之后还会扩展

// ......

}

Score类(对应Score表):

namespace demo\domain;

use demo\domain\Student;

/**

* Score

* 对应表score

*/

class Score extends DomainObject {

private $score;

private $courseName;

// Student对象引用

private $student;

public function __construct($id = null, $score = 0, $courseName = 'unknown') {

parent::__construct($id);

$this->score = $score;

$this->courseName = $courseName;

}

public function getScore() {

return $this->score;

}

public function setScore($score) {

$this->score = $score;

}

public function setCourseName($courseName) {

$this->courseName = $courseName;

}

public function getCourseName() {

return $this->courseName;

}

public function getStudent() {

return $this->student;

}

public function setStudent(Student $student) {

$this->student = $student;

}

}

之前说到领域模型最复杂的是映射到数据库结构。我们可以使用数据映射器模式。

数据映射器是一个负责将数据库中的一行数据映射到一个对象的类。

有个概念叫“对象关系阻抗不匹配”,指的是对象和关系数据库性质上的差异,比如对象可以有复杂的继承层次,对象中还可以包含另一个对象(关系型数据库的表不行吧),而表可以通过外键表示与其他表之间的关联等。

来看看Mapper的类层次图吧:

0818b9ca8b590ca3270a3433284dd417.png

Mapper抽象基类:

namespace demo\mapper;

use demo\base\AppException;

use \demo\base\ApplicationRegistry;

/**

* Mapper

*/

abstract class Mapper {

// PDO

protected static $PDO;

// config

protected static $dsn, $dbUserName, $dbPassword;

// PDO选项

protected static $options = array(

\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',

\PDO::ATTR_ERRMODE,

\PDO::ERRMODE_EXCEPTION,

);

public function __construct() {

if (!isset(self::$PDO)) {

// ApplicationRegistry获取数据库连接信息

$appRegistry = ApplicationRegistry::getInstance();

self::$dsn = $appRegistry->getDsn();

self::$dbUserName = $appRegistry->getDbUserName();

self::$dbPassword = $appRegistry->getDbPassword();

if (!self::$dsn || !self::$dbUserName || !self::$dbPassword) {

throw new AppException('Mapper init failed!');

}

self::$PDO = new \PDO(self::$dsn, self::$dbUserName, self::$dbPassword, self::$options);

}

}

/**

* 查找指定ID

* @param int $id

*/

public function findById($id) {

$pStmt = $this->getSelectStmt();

$pStmt->execute(array($id));

$data = $pStmt->fetch();

$pStmt->closeCursor();

$obj = null;

if (!is_array($data) || !isset($data['id'])) {

return $obj;

}

$obj = $this->createObject($data);

return $obj;

}

/**

* 插入数据

* @param \demo\domain\DomainObject $obj

*/

public function insert(\demo\domain\DomainObject $obj) {

return $this->doInsert($obj);

}

/**

* 删除指定ID

* @param int $id

*/

public function deleteById($id) {

$pStmt = $this->getDeleteStmt();

$flag = $pStmt->execute(array($id));

return $flag;

}

/**

* 生成一个$data中值属性的对象

* @param array $data

*/

public function createObject(array $data) {

$obj = $this->doCreateObject($data);

return $obj;

}

public abstract function update(\demo\domain\DomainObject $obj);

protected abstract function doInsert(\demo\domain\DomainObject $obj);

protected abstract function doCreateObject(array $data);

protected abstract function getSelectStmt();

protected abstract function getDeleteStmt();

}

一个具体Mapper子类ScoreMapper:

namespace demo\mapper;

use demo\base\AppException;

use \demo\domain\DomainObject;

use \demo\domain\Score;

use \demo\mapper\StuStudentMapper;

/**

* ScoreMapper

*/

class ScoreMapper extends Mapper {

private static $selectStmt;

private static $insertStmt;

private static $updateStmt;

private static $deleteStmt;

private static $init = false;

public function __construct() {

if (!self::$init) {

parent::__construct();

$selectSql = 'select * from score where id = ?';

$insertSql = 'insert into score (sid, course_name, score) values (?, ?, ?)';

$updateSql = 'update score set sid = ?, course_name = ?, score = ? where id = ?';

$deleteSql = 'delete from score where id = ?';

// 预编译生成prepareStatement对象

self::$selectStmt = self::$PDO->prepare($selectSql);

self::$insertStmt = self::$PDO->prepare($insertSql);

self::$updateStmt = self::$PDO->prepare($updateSql);

self::$deleteStmt = self::$PDO->prepare($deleteSql);

self::$init = true;

}

}

public function update(DomainObject $obj) {

// 类型安全检查

// if (!($obj instanceof Score)) {

// throw new AppException('Object is not instance of Student');

// }

$data = array($obj->getStudent()->getId()

, $obj->getCourseName(), $obj->getScore(), $obj->getId());

$flag = self::$updateStmt->execute($data);

return $flag;

}

protected function doInsert(DomainObject $obj) {

$data = array($obj->getStudent()->getId() , $obj->getCourseName(), $obj->getScore());

$flag = self::$insertStmt->execute($data);

// 数据行返回设置对象

if ($flag) {

$lastId = self::$PDO->lastInsertId();

$obj->setId($lastId);

}

return $flag;

}

protected function doCreateObject(array $data) {

$obj = new Score($data['id']);

$obj->setScore($data['score']);

$obj->setCourseName($data['course_name']);

// setStudent()

$stuMapper = new StudentMapper();

$stuObj = $stuMapper->findById($data['sid']);

$obj->setStudent($stuObj);

return $obj;

}

protected function getSelectStmt() {

return self::$selectStmt;

}

protected function getDeleteStmt() {

return self::$deleteStmt;

}

}

使用的例子:

$score = new Score(0);

$scoreMapper = new ScoreMapper();

$score->setCourseName('Math');

// 插入

$scoreMapper->insert($score);

// 查找

$score = $scoreMapper->findById($score->getId());

var_dump($score);

$score->setCourseName('English');

// 更新

$scoreMapper->update($score);

// 删除

$scoreMapper->deleteById($score->getId());

数据映射器的好处是消除了领域层和数据库操作之间的耦合,Mapper可以应用各种对象关系映射。比如insert、update的传递的参数是DomainObject对象,保存到数据库的是数据行;findById把数据库的数据行转换成DomainObject对象。 而它的缺点是需要创建大量的具体的映射器类,不过大部分都是相似的代码,也可以通过反射机制来生成这些相似的代码。

findById是获取一条数据,而findAll是获取一个数据集,那么需要一个什么对象来保持和数据集的映射才比较好呢?接下来介绍Collection对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值