zoukankan      html  css  js  c++  java
  • 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....
  • 相关阅读:
    GNU make manual 翻译(九十九)
    GNU make manual 翻译( 九十五)
    Shell的 for 循环小例子
    makefile中对目录遍历的小例子
    GNU make manual 翻译(九十三)
    GNU make manual 翻译( 一百)
    GNU make manual 翻译( 九十七)
    GNU make manual 翻译( 九十八)
    mapserver4.8.3 的readme.win32的中文翻译文件
    遥控器编程
  • 原文地址:https://www.cnblogs.com/iplus/p/4490215.html
Copyright © 2011-2022 走看看