任何需要和数据库打交道的系统都需要使用Sql,但系统本身是由领域对象和业务规则而不是数据库组成的。这里介绍的选择工厂和更新工厂模式可以为树状的领域结构和表格式的数据库之间搭建一座桥梁。在将领域数据转换为数据库可以理解的格式时,我们需要进行解耦。
之前可以看到标记对象(IdentityObject)模式所带来的好处,它能更加动态地生成查询语句,因为各种查询条件组合的可能性非常多。
那么先来看看更新工厂:
UpdateFactory:
namespace demo\mapper;
require_once 'demo/domain/DomainObject.php';
use demo\domain\DomainObject;
/**
* 更新工厂
* @author happen
*
*/
abstract class UpdateFactory {
public abstract function newUpdate(DomainObject $obj);
/**
* 返回update或者insert的查询sql和值
* @param string $table
* @param array $fileds
* @param array $condition
*/
protected function buildStatement($table, array $fileds = null, array $conditions = null) {
$terms = array();
if (!is_null($conditions)) {
// 带有条件则为update
$query = "UPDATE {$table} SET ";
$query .= implode(' = ?, ', array_keys($fileds)) . ' = ?';
$terms = array_values($fileds);
$cond = array();
$query .= " WHERE ";
foreach ($conditions as $key=>$val) {
$cond[]="$key = ?";
$terms[]=$val;
}
$query .= implode( " AND ", $cond );
} else {
// 不带条件的为insert
$query = "INSERT INTO {$table} (";
$query .= implode(",", array_keys($fields));
$query .= ") VALUES (";
foreach ($fields as $name => $value) {
$terms[] = $value;
$qs[] = '?';
}
$query .= implode(",", $qs);
$query .= ")";
}
return array($query, $terms);
}
}
ClassroomUpdateFactory子类:
class ClassroomUpdateFactory extends UpdateFactory {
public function newUpdate(DomainObject $obj) {
// 类型安全检测
// if (!($obj instanceof Classroom)) {
// throw new Exception('obj must be instanceof Classroom');
// }
$id = $obj->getId();
$value['name'] = $obj->getName();
$cond = null;
if ($id >= 0) {
// id大于0 说明为update, 否则$cond为null
$cond['id'] = $id;
}
return $this->buildStatement('classroom', $value, $cond);
}
}
例子:
$classroom = new Classroom();
$classroom->setId(5);
$classroom->setName('class 7');
$crUpdateFact = new ClassroomUpdateFactory();
var_dump($crUpdateFact->newUpdate($classroom));
接下来是选择工厂:
SelectionFactory:
namespace demo\mapper;
require_once 'demo/mapper/IdentityObject.php';
/**
* 选择工厂
* @author happen
*
*/
abstract class SelectionFactory {
public abstract function newSelection(IdentityObject $obj);
/**
* 返回查询的where语句
* @param unknown_type $obj
*/
protected function buildWhere(IdentityObject $obj) {
if ($obj->isVoid()) {
// 字段条件为空,则为不使用条件查询
return array('', array());
}
$compStrings = array();
$values = array();
foreach ($obj->getComps() as $comp) {
$compStrings[] = "{$comp['name']} {$comp['operator']} ?";
$values[] = $comp['value'];
}
$where = "WHERE " . implode(" AND ", $compStrings);
return array($where, $values);
}
}
ClassroomSelectionFactory子类:
class ClassroomSelectionFactory extends SelectionFactory {
public function newSelection(IdentityObject $obj) {
// 安全检测
// if ($obj instanceof ClassroomIdentityObject) {
// throw new Exception('$obj must be instanceof ClassroomIdentityObject');
// }
$fileds = implode(', ', $obj->getObjectFields());
$core = "SELECT {$fileds} FROM classroom";
list($where, $values) = $this->buildWhere($obj);
return array($core . ' ' . $where, $values);
}
}
例子:
$idobj = new IdentityObject();
$idobj->field('id')->eq(5);
$crSelectFact = new ClassroomSelectionFactory();
var_dump($crSelectFact->newSelection($idobj));
结合使用标记对象和查询语句工厂的最大好处之一是可以生成各种各样的查询语句,但这样做会导致缓存问题。这些方法即时生成查询,重复劳动的次数不计其数,但我们可以缓存这些生成的Sql语句。