• php 设计模式数据映射模式(应用程序与数据库交互模式)


    前面提到的设计模式大大提高了代码的可读性与可维护性。然而,在WEB应用设计与开发中一个基本的需求与挑战:数据库应用,这些设计模式都没有涉及到。数据映射模式使您能更好的组织你的应用程序与数据库进行交互。

    下面我将用实际代码说明,如果一个表发生变动。我们要修改客户端代码就可以了。特别是游戏项目,需求经常可能会经常变动。修改表结构,可能引起大片代码的改动。

    首先我们使用pdo进行数据库访问:

    <?php
    /**
     * Filename:db.class.php
     * 
     * db class ,use PDO lib
     * 
     * @author guisu.huang
     * @version 1.0
     * 
     */
    class Db {
    	public static $db = null;
    	private $_dbh = null;
    	public 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 Table{
    
    	public 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 = null;
    
    	protected $username = null ;
    
    	protected $level = null;
    
    	protected $exp = null;
    
    	protected $ctime = null;
    
    	protected $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 client{
    	static 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

    Meet so Meet. C plusplus I-PLUS....
  • 相关阅读:
    Poj 1742 Coins(多重背包)
    Poj 2350 Above Average(精度控制)
    求二进制数中1的个数
    Poj 1659 Distance on Chessboard(国际象棋的走子规则)
    Poj 2411 Mondriaan's Dream(压缩矩阵DP)
    Poj 2136 Vertical Histogram(打印垂直直方图)
    Poj 1401 Factorial(计算N!尾数0的个数——质因数分解)
    poj 2390 Bank Interest(计算本利和)
    Poj 2533 Longest Ordered Subsequence(LIS)
    Poj 1887 Testing the CATCHER(LIS)
  • 原文地址:https://www.cnblogs.com/iplus/p/4490215.html
Copyright © 2020-2023  润新知