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

来源:互联网 发布:linux批量删除命令 编辑:程序博客网 时间:2024/06/05 02:05

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

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

         领域模型的复杂性主要来自于尝试使模型纯粹(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的类层次图吧:

        

        Mapper抽象基类:

namespace demo\mapper;use demo\base\AppException;use \demo\base\ApplicationRegistry;/** * Mapper */abstract  class Mapper {// PDOprotected static $PDO;// configprotected 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对象。

原创粉丝点击