/* 選擇工廠和更新工廠模式,這個模式的類(UpdateFactory和SelectionFactory類)就是用來創建SQL語句的. 因為涉及到之前學習的內容比較多,這裡就儘量將之前相關模式的示例代碼放在一起來進行學習和回顧了。 以下的代碼都是代碼片段而且涉及到連接資料庫,無法進行整體的調試(某些... ...
/* 選擇工廠和更新工廠模式,這個模式的類(UpdateFactory和SelectionFactory類)就是用來創建SQL語句的. 因為涉及到之前學習的內容比較多,這裡就儘量將之前相關模式的示例代碼放在一起來進行學習和回顧了。 以下的代碼都是代碼片段而且涉及到連接資料庫,無法進行整體的調試(某些部分單獨拿出來的話就可以),因此重在理解。 */ //更新工廠 abstract class UpdateFactory{ abstract function newUpdate(\woo\domain\DomainObject $obj); //拼接更新或插入的sql語句 protected function buildStatement($table,array $fields ,array $condition = null){ $terms = array(); //SQL語句中占位符所代表的實際值 if(!is_null($condition)){ //有條件則拼接更新語句,無條件則拼接插入語句 $query = "UPDATE {$table} SET"; $query .= implode(" = ? " ,array_keys($fields)) . " = ? "; $terms = array_values($fields); $cond = array(); $query .= " WHERE "; foreach($condition as $key=>$val){ $cond[] = " $key = ? "; $terms[] = $val; } $query .= implode(" AND ",$cond); } else { $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); } } //通過領域模型生成SQL語句 class VenueUpdateFactory extends UpdateFactory{ function newUpdate(\woo\domain\DomainObject $obj){ $id = $obj->getId(); $cond = null; $values['name'] = $obj->getName(); if($id > -1 ){ $cond["id"] = $id; } return $this->buildStatement("venue",$values,$cond); } } //選擇工廠 abstract class SelectionFactory{ abstract function newSelection(IdentityObject $obj); //通過標識對象拼接sql語句的where條件語句 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); } } //拼接select語句 class VenuSelectionFactory extends SelectionFactory { function newSelection(IdentityObject $obj){ $field = implode(',',$obj->getObjectFields()); $core = "SELECT $fields FROM venue"; list($where,$values) = $this->buildWhere($obj); return array($core." ".$where,$values); } } //現在來回顧一下之前學習過的相關知識 //欄位對象 class Field { protected $name = null; //欄位名稱 protected $operator = null; //操作符 protected $comps = array(); //存放條件的數組 protected $incomplete = false; //檢查條件數組是否有值 function __construct ($name){ $this->name= $name; } //添加where 條件 function addTest($operator,$value){ $this->comps[] = array('name'=>$this->name,'operator'=>$operator,'value'=>$value); } //獲取存放條件的數組 function getComps(){ return $this->comps; } function isIncomplete(){ return empty($this->comps); } } //標識對象,主要功能就是拼接where條件語句 class IdentityObject { protected $currentfield = null; //當前操作的欄位對象 protected $fields = array(); //欄位對象集合 private $and = null; private $enforce = array(); //限定的合法欄位 function __construct($field = null, array $enforce = null){ if(!is_null($enforce)){ $this->enforce = $enforce; } if(!is_null($field)){ $this->field($field); } } //獲取限定的合法欄位 function getObjectFields(){ return $this->enforce; } //主要功能為設置當前需要操作的欄位對象 function field($fieldname){ if(!$this->isVoid()&& $this->currentfield->isIncomplete()){ throw new \Exception("Incomplete field"); } $this->enforceField($fieldname); if(isset($this->fields[$fieldname]){ $this->currentfield = $this->fields[$fieldname]; } else { $this->currentfield = new Field($fieldname); $this->fields[$fieldname] = $this->currentfield; } return $this; //採用連貫語法 } //欄位集合是否為空 function isVoid(){ return empty($this->fields); } //檢查欄位是否合法 function enforceField ($fieldname){ if(!in_array($fieldname,$this->enforce) && !empty($this->enforce)){ $forcelist = implode(',',$this->enforce); throw new \Exception("{$fieldname} not a legal field {$forcelist}"); } } //向欄位對象添加where條件 function eq($value){ return $this->operator("=",$value); } function lt($value){ return $this->operator("<",$value); } function gt($value){ return $this->operator(">",$value); } //向欄位對象添加where條件 private function operator($symbol,$value){ if($this->isVoid){ throw new \Exception("no object field defined"); } $this->currentfield->addTest($symbol,$value); return $this; //採用連貫語法 } //獲取此類中所有欄位對象集合的where條件數組 function getComps(){ $ret = array(); foreach($this->fields as $key => $field){ $ret = array_merge($ret,$field->getComps()); } return $ret; } } //數據映射器 class DomainObjectAssembler { protected static $PDO; //PersistenceFactory本例中並未實現,按原書的說法讀者自己完成 //其主要功能就是生成相應類型的選擇工廠類、更新工廠類或Collection對象 //在初始化的時候根據傳入的PersistenceFactory對象決定了這個類是映射那個資料庫表和領域模型的 function __construct (PersistenceFactory $factory){ $this->factory = $factory; if(!isset(self::$PDO)){ $dsn = \woo\base\ApplicationRegistry::getDSN(); if(is_null($dsn)){ throw new \woo\base\AppException("NO DSN"); } self::$PDO = new \PDO ($dsn); self::$PDO->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION); } } //獲取預處理對象,用sql語句本身做為對象數組的鍵 function getStatement($str){ if(!isset($this->statements[$str])){ $this->statements[$str] = self::$PDO->prepare($str); } return $this->statements[$str]; } //根據where條件返回一條數據 function findOne(IdentityObject $idobj){ $collection = $this->find($idobj); return $collection->next(); } //根據where條件返回一個數據集合 function find(IdentityObject $idobj){ $selfact = $this->factory->getSelectionFactory(); //獲取選擇工廠對象 list($selection,$values) = $selfact->newSelection($idobj); //獲取sql語句 $stmt = $this->getStatement($selection); //獲取預處理對象 $stmt->execute($values); $raw = $stmt->fetchAll(); return $this->factory->getCollection($raw); //還記得Collection對象嗎,下麵粘出代碼回顧一下 } //根據where條件插入或更新一條數據 function insert(\woo\domain\DomainObject $obj){ $upfact = $this->factory->getUpdateFactory(); //獲取更新工廠對象 list($update,$values) = $upfact->newUpdate($obj); //獲取sql語句 $stmt = $this->getStatement($update); //獲取預處理對象 $stmt->execute($values); if($obj->getId()<0){ $obj->setId(self::$PDO->lastInsertId); } $obj->markClean(); } } /* 這裡在回顧一下Collection類,當然這個類和上面使用的Collection類是有差別的,因為至少這裡的Mapper類與之前相比發生了一些變化 Iterator介面定義的方法: rewind() 指向列表開頭 current() 返回當前指針處的元素 key() 返回當前的鍵(比如,指針的指) next() valid() 下麵這個類是處理多行記錄的,傳遞資料庫中取出的原始數據和映射器進去,然後通過數據映射器在獲取數據時將其創建成對象 */ abstract class Collection implements \Iterator{ protected $mapper; //數據映射器 protected $total = 0; //集合元素總數量 protected $raw = array(); //原始數據 private $result; private $pointer = 0; //指針 private $objects = array(); //對象集合 function __construct (array $raw = null,Mapper $mapper= null){ if(!is_null($raw)&& !is_null($mapper)){ $this->raw = $raw; $this->total = count($raw); } $this->mapper = $mapper; } function add(\woo\domain\DmainObject $object){ //這裡是直接添加對象 $class = $this->targetClass(); if(!($object instanceof $class)){ throw new Exception("This is a {$class} collection"); } $this->notifyAccess(); $this->objects[$this->total] = $object; $this->total ++; } abstract function targetClass(); //子類中實現用來在插入對象時檢查類型的 protected function notifyAccess(){ //不知道幹嘛的 } private function getRow($num){ //獲取集合中的單條數據,就是這裡通過數據映射器將數據創建成對象 $this->notifyAccess(); if($num >= $this->total || $num < 0){ return null; } if(isset($this->objects[$num]){ return $this->objects[$num]; } if(isset($this->raw[$num]){ $this->objects[$num] = $this->mapper->createObject($this->raw[$num]); return $this->objects[$num]; } } public function rewind(){ //重置指針 $this->pointer = 0; } public function current(){ //獲取當前指針對象 return $this->getRow($this->pointer); } public function key(){ //獲取當前指針 return $this->pointer; } public function next(){ //獲取當前指針對象,並將指針下移 $row = $this->getRow($this->pointer); if($row){$this->pointer ++} return $row; } public function valid(){ //驗證 return (!is_null($this->current())); } } //子類 class VenueColletion extends Collection implements \woo\domain\VenueCollection{ function targetClass(){ return "\woo\domain\Venue"; } } //客戶端 //初始化一個能夠獲取venue類型的選擇工廠類、更新工廠類或Collection對象的超級工廠類 $factory = \woo\mapper\PersistenceFactory::getFactory("woo\\domain\\Venue"); $finder = new \woo\mapper\DomainObjectAssembler($factory); //數據映射器 $idobj = $factory->getIdentityObject()->field('name')->eq('The Eyeball Inn'); //設置where條件語句 $collection = $finder->find($idobj); //根據where條件查找一個數據集合(一個Collection對象) foreach($collection as $venue){ print $venue->getName()."\n"; }