前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >php 设计模式-数据映射模式(应用程序与数据库交互模式)

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

作者头像
黄规速
发布2022-04-14 18:54:29
4980
发布2022-04-14 18:54:29
举报
文章被收录于专栏:架构师成长之路

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

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

代码语言:javascript
复制
<?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 $ar
     * @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 $ar
     * @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 $ar
     */
    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的内容就可以了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2012/05/15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档