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....
  • 相关阅读:
    加载数据量大,页面卡死解决办法
    [存档]开启window7的隐藏功能虚拟wifi
    IIS发布Asp.Net网站注意事项
    [转载]总结几种C#窗体间通讯的处理方法
    调整和删除Win7休眠文件Hiberfil.sys的方法技巧,释放系统空间! ...
    [存档]Div+Css布局中经常使用的小技巧合集
    Android AndroidManifest.xml 结构详解
    Android权限详细说明
    Activity 生命周期详解
    程序员的文采
  • 原文地址:https://www.cnblogs.com/iplus/p/4490215.html
Copyright © 2011-2022 走看看