/*選擇工廠和更新原廠模式,這個模式的類(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";}