php 設計模式-數據映射模式(應用程序與數據庫交互模式)

               

前面提到的設計模式大大提高了代碼的可讀性與可維護性。然而,在WEB應用設計與開發中一個基本的需求與挑戰:數據庫應用,這些設計模式都沒有涉及到。數據映射模式使您能更好的組織你的應用程序與數據庫進行交互。

下面我將用實際代碼說明,如果一個表發生變動。我們要修改客戶端代碼就可以了。特別是遊戲項目,需求經常可能會經常變動。修改表結構,可能引起大片代碼的改動。

首先我們使用pdo進行數據庫訪問:

<?php/** * Filename:db.class.php *  * db class ,use PDO lib *  * @author guisu.huang * @version 1.0 *  */class Db public static $db = nullprivate $_dbh = nullpublic static function getInstance() {  if( self::$db == null ){   self::$db = new self(BACKEND_DBHOST ,BACKEND_DBUSER ,BACKEND_DBPW ,BACKEND_DBNAME);  }  return self::$db; } private function __construct( $host ,$user ,$pass ,$dbname ){  try {   $this->_dbh = new PDO('mysql:dbname='.$dbname.';host='.$host,$user,$pass);   $this->_dbh->query('SET NAMES '. BACKEND_DBCHARSET);   $this->_dbh->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);   $this->_dbh->setAttribute(PDO::ATTR_ERRMODE, true);  } catch (PDOException $e) {   throw new Exception('Can not connect db');  } } private function getExecuteResult($sql, $sth){  $type = strtolower(substr(trim($sql), 0,6));  switch ($type) {   case 'update': case 'delete':    $result = $sth->rowcount();//返回影響的行數   break;   case 'insert':    $result = $this->getLastId();    break;   case 'select':    $result = $sth->fetchAll(PDO::FETCH_ASSOC);    break;   default:    break;  }  return $result; }  /**************************************sql ************************/  public function getOne($sql){  try {   $rs = $this->_dbh->query($sql);   $result = $rs->fetch(PDO::FETCH_ASSOC);   if(!empty($result)) {    return $result;   }  } catch (PDOException $e) {   throw new Exception($this->_dbh->errorInfo());  }  return false; } public function getAll($sql){  try {   $rs = $this->_dbh->query($sql);   $result = $rs->fetchAll(PDO::FETCH_ASSOC);   if(!empty($result)) {    return $result;   }  } catch (PDOException $e) {   throw new Exception($this->_dbh->errorInfo());  }  return false; } public function exec($sql){  try {   $exec = $this->_dbh->exec($sql);  } catch (PDOException $e){   throw new Exception($this->_dbh->errorInfo());  }  return $exec; } /**  * 不關注鍵值  *  Execute a prepared statement by passing an array of values   $sth = $dbh->prepare('SELECT name, colour, calories      FROM fruit      WHERE calories < ? AND colour = ?');  $sth->execute(array(150, 'red'));  $red = $sth->fetchAll();  $sth->execute(array(175, 'yellow'));  $yellow = $sth->fetchAll();  * @param unknown_type $sql  * @param unknown_type $arr  * @return unknown  */ public function executeArr($sql, $arr){  try {   $sth = $this->_dbh->prepare($sql);   $r = $sth->execute($arr);   if ($r) {    return  $this->getExecuteResult($sql, $sth);   }  } catch (PDOException $e){   throw new Exception($e->getMessage() . $this->_dbh->errorInfo());  }  } /**  * 關聯數組:  *  Execute a prepared statement by passing an array of values    $sql = 'SELECT name, colour, calories      FROM fruit      WHERE calories < :calories AND colour = :colour';  $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));  $sth->execute(array(':calories' => 150, ':colour' => 'red'));  $red = $sth->fetchAll();  *  * @param unknown_type $sql  * @param unknown_type $arr  * @return unknown  */ public function executeAsoc($sql, $arr){  try {   $array = array();   if ($arr) {    foreach ($arr as $key=>$v) {     if (strpos($sql, ':' . $key )!==false) {      $array[':' . $key] = $v;     }    }   }   $sth = $this->_dbh->prepare($sql);   $r = $sth->execute($array);   if ($r) {    return  $this->getExecuteResult($sql, $sth);   }  } catch (PDOException $e){   throw new Exception($e->getMessage() . $this->_dbh->errorInfo());  }  }  public function beginTransaction(){  return $this->_dbh->beginTransaction(); } public function commit(){  return $this->_dbh->commit(); } public function rollBack(){  return $this->_dbh->rollBack(); } public function getLastId() {  return $this->_dbh->lastInsertId(); }  }?>

數據映相關類射類,使用__call達到動態生成getter 和setter方法.

<?php/** * 抽象數據映射 * */abstract  class Tablepublic function __call($method, $args) {  if (preg_match('/^(get|set)(\w+)/', strtolower($method), $match)  && $attribute = $this->validateAttribute($match[2])) {   if ('get' == $match[1]) {    return $this->$attribute;   } else {    $this->$attribute = $args[0];   }  }else {   throw new Exception(   'Call to undefined method ' . __CLASS__  . '::'.$method.'()');  } } protected function validateAttribute($method) {  if ( in_array(strtolower($method), array_keys(get_class_vars(get_class($this))))) {   return strtolower($method);  } }  }/** * 數據映射到表 * 一般根據表的結構由工具自動生成, * 要不然程序員經常得copy和修改這個類 * */class UserTable extends Table /**  * fields  *  * @var unknown_type  */ protected $uid = nullprotected $username = nullprotected $level = nullprotected $exp = nullprotected $ctime = nullprotected $mtime = null/**  * table   *  * @var unknown_type  */ public   $tableName = 'user'public  $primaryKey = 'uid';  public  static $tablefileds = array(      'uid',      'username',      'level',      'exp',      'ctime',      'mtime', );  /**  * 對象生成數組  *  * @return array  */ function toArray(){  $arr = array();  foreach (UserTable::$tablefileds as $filed) {   $getMethod  = 'get' .ucwords($filed);   $value = $this->$getMethod();   if ($value !== null) {    $arr[$filed] = $value;   }  }  return $arr; }  /**  * 數組生成對象  *  * @return array  */ function toObj($arr){  if (!$arr) {   return $this;  }  foreach (UserTable::$tablefileds as $filed) {   $setMethod  = 'set' .ucwords($filed);   $this->$setMethod($arr[$filed]);  }  return $this; }}/** *  * */class Mapper{  protected  $conn = null;  /**  * 自動插入  * 不想對某一列插入,把對應的屬性設置成null就ok  *  * @param Table $table  * @return unknown  */ function save(Table $table){  $arr  =  $table->toArray();  $set = '';  if ($arr) {   foreach ($arr as $field=> $v) {    if ($set) $set .=',';    $set .= $field . "='" . $v ."'";   }  }  if ($set) {   $this->conn->exec( 'insert into ' . $table->tableName . ' SET ' . $set);   return $this->conn->getLastId();  }     }  /**  * 更新  * 不想對某一列更新,把對應的屬性設置成null就ok  *  * @param Table $table  * @return unknown  */ function update(Table $table){  $arr  =  $table->toArray();  $set = '';  if ($arr) {   foreach ($arr as $field=> $v) {    if ($set) $set .=',';    $set .= $field . "='" . $v ."'";   }  }  $primayGet = 'get'.ucwords($table->primaryKey);  if ($set) {   return $this->conn->exec( 'update ' . $table->tableName . ' SET ' . $set . ' where ' . $table->primaryKey ."='" . $table->$primayGet() . "'" );  } } }class UserMapper extends Mapper {  const INSERT_SQL = "insert into user (username, level,exp, ctime, mtime) values (:username, :level, :exp, now(), now())"const UPDATE_SQL = "update user SET username=:username, level=:level, exp=:exp WHERE uid=:uid "const SELECT_SQL = "select * from user  WHERE uid=:uid "const DELETE_SQL = "delete from user  WHERE uid=:uid ";  function __construct(){  $this->conn =  Db::getInstance(); } /**  * 我們可以實現覆蓋save  *  * @param unknown_type $userTable  */ public function save2($userTable) {  $rs =  Db::getInstance()->executeArr( self::INSERT_SQL, $userTable->toArray());  return $rs; }  /**  * Enter description here...  *  * @param unknown_type $userTable  */ public function update2($userTable) {  return $this->conn->execute(self::UPDATE_SQL, $userTable->toArray()); }  /**  * Enter description here...  *  * @param unknown_type $arr  */ public function find($userTable) {  $rs = $this->conn->executeAsoc( self::SELECT_SQL, $userTable->toArray());  return $rs ? $userTable->toObj($rs[0]) : $userTable; }}?>

實際客戶測試:

<?php/** * 數據庫配置文件 * */define('BACKEND_DBHOST', 'localhost');define('BACKEND_DBUSER', 'root');define('BACKEND_DBPW', '123456');define('BACKEND_DBNAME', 'sample');define('BACKEND_DBCHARSET', 'utf-8');//sql/*CREATE TABLE IF NOT EXISTS `user` (  `uid` int(11) NOT NULL AUTO_INCREMENT,  `username` varchar(64) NOT NULL,  `level` int(11) NOT NULL DEFAULT '0',  `exp` int(11) NOT NULL DEFAULT '0',  `ctime` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',  `mtime` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',  PRIMARY KEY (`uid`),  KEY `username` (`username`)) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=12 ;*/class clientstatic function main(){  $userMapper = new UserMapper();  $user = new UserTable();  //插入  //$user->setUserName('guisu');  //$user->setLevel(1);  //$user->setExp(10);  //  //$userMapper = new UserMapper();  //$r = $userMapper->save($user);    //查找  $user->setUid(10);  $user = $userMapper->find($user);  var_dump($user);  //更新  $user->setUserName('guisu2');  $r = $userMapper->update($user);  var_dump($r); } }

一般,client是業務邏輯層,UserMapper是數據訪問層。UserTable底層數據結構。

我們儘量做到如果表User修改了:

1)工具重新自動生成UserTable類

2)只修改client代碼和少量的UserMapper代碼,一般修改UserMapper的常量const的內容就可以了。

說明轉載最好說明出處:php 設計模式-數據映射模式(應用程序與數據庫交互模式)http://blog.csdn.net/hguisu/article/details/7569968

           

再分享一下我老師大神的人工智能教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智能的隊伍中來!https://blog.csdn.net/jiangjunshow

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章