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的内容就可以了。

转自:http://blog.csdn.net/hguisu/article/details/7569968

原创文章,作者:s19930811,如若转载,请注明出处:http://www.178linux.com/2827

(0)
s19930811s19930811
上一篇 2015-04-07 19:28
下一篇 2015-04-07 19:29

相关推荐

  • Nginx配置与应用详解

    前言 Nginx是由俄罗斯软件工程师Igor Sysoev开发的一个高性能的HTTP和反向代理服务器,具备IMAP/POP3和SMTP服务器功能。Nginx最大的特点是对高并发的支持和高效的负载均衡,在高并发的需求场景下,是Apache服务器不错的替代品。目前,包括新浪、腾讯等知名网站都已使用Nginx作为Web应用服务器。本文带来的是Nginx配置与应用详…

    Linux干货 2015-06-15
  • Linux bash中的命令行展开介绍及应用示例

    Linux bash中的命令行展开介绍及应用示例 在Linux bash中可以使用命令行展开特性一步完成需要分开成多步完成的操作,达到事半功倍的效果。在Linux指令参数位置使用"{}" 将相应的参数括起来,括号中的参数以逗号分隔,然后bash在执行这一指令时会自动将括号中的内容进行展开。 相关示例如下: 同时创建多个目录,在/tmp目录…

    Linux干货 2016-11-07
  • 高效运维最佳实践(03):Redis集群技术及Codis实践

    前言 诚如开篇文章所言,高效运维包括管理的专业化和技术的专业化。前两篇我们主要在说些管理相关的内容,本篇说一下技术专业化。希望读者朋友们能适应这个转换,谢谢。 互联网早在几年前就已进入Web 2.0时代,对后台支撑能力的要求,提高了几十倍甚至几百倍。在这个演化过程中,缓存系统扮演了举足轻重的角色。 运维进化到今天,已经不是重复造轮子的时代。所以,我们在架构优…

    Linux干货 2015-04-03
  • N22-第六周博客作业

    请详细总结vim编辑器的使用并完成以下练习题 1、复制/etc/rc.d/rc.sysinit文件至/tmp目录,将/tmp/rc.sysinit文件中的以至少一个空白字符开头的行的行首加#; :%s@^[[:space:]]\+[^[:space:]]@#\1@g 或 :%s@^[[:space:]]\+[^[:space:]]@#&a…

    Linux干货 2016-09-26
  • 软链接和硬链接的区别

          什么是链接文件? 使用windows的朋友们应该会经常接触到快捷方式吧!它也就是能让我们快速的打开一个目标应用程序,文件,或者文件夹来使我们的操作更加快捷。那么下面我们就来简单聊聊linux上的“链接文件”吧!            &n…

    Linux干货 2016-10-19
  • class15磁盘管理(二) 高级磁盘管理(一)

    挂载点和/etc/fstab 配置文件系统体系 被mount、fsck和其它程序使用 系统重启时保留文件系统体系 可以在设备栏使用文件系统卷标 使用mount -a命令挂载  /etc/fstab 中的所有文件系统 文件挂载配置文件 /etc/fstab每行定义一个要挂载的文件系统;   &nbsp…

    Linux干货 2016-09-05