zoukankan      html  css  js  c++  java
  • 谈谈php里的DAO Model AR

    这次要谈的3个关键字:DAO、Model、AR,是我们在做web应用时常见的几个概念,也被称作设计模式(design pattern),先简单看看它们的全拼和中文:

    • DAO:Data Access Object 数据访问对象
    • Model:数据模型
    • AR:Active Record 活动记录

    几乎所有的web开发框架在设计的时候,都或多或少用到了这些设计模式来实现了MVC中的M层,通过为开发者提供强有力的类库,简单便捷的完成数据库访问。

    很多同学对这些概念的理解相对模糊,因此下面我将通过几个例子循序渐进的描述这3个概念,希望与大家分享与讨论我的认识。

    M

    这里的M就是MVC框架中的M,下面我会先通过DAO和Model两个设计模式来实现一个M层,它提供了对Mysql数据库中的Group表的访问能力。

    Model

    model是一个类,它生成的1个对象代表了数据库中的一行记录。既然要代表1行记录,那么它应该有若干和数据库列对应的属性,这里就拿一个Group表为例,看看Model类如何定义:

    /**
    * Object represents table 'groups'
    *
    * @author: http://phpdao.com
    * @date: 2009-10-17 02:53	 
    */
    class Group{
    
    var $id;
    var $name;
    
    }
    ?>
    

      

    可见,Group就是一个Model类,它定义了2个属性对应了数据库表Group中的2个列,这个类的1个对象就代表数据库中的一行记录,就这么简单。

    DAO

    Data Access Object,数据访问对象,意思就是通过这个类的对象可以实现对数据库的真实访问。

    前面我们也看到了,Model仅仅代表了一行数据库记录,但是没有任何与数据库打交道的代码,这是因为DAO才是真正负责与数据库进行通讯的"人"呐...

    我们知道,对于任何一张表来说,它们都有一些统一的常见操作,例如:按主键查询,插入,查询,删除。

    观察这些操作,我们可以知道无论是查询还是修改操作,其实都是在与数据库里的记录行交互,对应到程序里也就等价于之前看到的Model对象了。因此,我们基本可以想象到DAO的职责:

    • 通过向DAO对象的新增/修改方法,传入model对象,实现对数据库中对应记录的新增/修改。
    • 通过DAO对象的查询方法,从数据库读取数据,并返回model对象作为结果。

    但是我们知道,数据库可能是Mysql,Mongodb,Oracle等等,因此DAO的实现可能不止一种,所以通常会为DAO定义一个通用的接口,并为不同的数据库提供不同的实现。这里,我们为Group model设计一个DAO的interface,无论任何数据库在实现时都应该实现这些接口:

    /*
    * Intreface DAO
    *
    * @author: http://phpdao.com
    * @date: 2009-10-17 02:53
    */
    interface GroupsDAO{
    
    /**
    * Get Domain object by primry key
    *
    * @param String $id primary key
    * @Return Groups 
    */
    public function load($id);
    
    /**
    * Get all records from table
    */
    public function queryAll();
    
    /**
    * Get all records from table ordered by field
    * @Param $orderColumn column name
    */
    public function queryAllOrderBy($orderColumn);
    
    /**
    * Delete record from table
    * @param group primary key
    */
    public function delete($id);
    
    /**
    * Insert record to table
    *
    * @param Groups group
    */
    public function insert($group);
    
    /**
    * Update record in table
    *
    * @param Groups group
    */
    public function update($group);	
    
    /**
    * Delete all rows
    */
    public function clean();
    
    public function queryByName($value);
    
    
    public function deleteByName($value);
    
    
    }
    ?>
    

      

    现在,我们继承GroupDAO接口,为Group表实现访问Mysql的DAO类:

    /*
    * Class that operate on table 'groups'. Database Mysql.
    *
    * @author: http://phpdao.com
    * @date: 2009-10-17 02:53
    */
    class GroupsMySqlDAO implements GroupsDAO{
    
    /**
    * Get Domain object by primry key
    *
    * @param String $id primary key
    * @return GroupsMySql 
    */
    public function load($id){
    	$sql = 'SELECT * FROM groups WHERE id = ?';
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->setNumber($id);
    	return $this->getRow($sqlQuery);
    }
    
    /**
    * Get all records from table
    */
    public function queryAll(){
    	$sql = 'SELECT * FROM groups';
    	$sqlQuery = new SqlQuery($sql);
    	return $this->getList($sqlQuery);
    }
    
    /**
    * Get all records from table ordered by field
    *
    * @param $orderColumn column name
    */
    public function queryAllOrderBy($orderColumn){
    	$sql = 'SELECT * FROM groups ORDER BY '.$orderColumn;
    	$sqlQuery = new SqlQuery($sql);
    	return $this->getList($sqlQuery);
    }
    
    /**
    * Delete record from table
    * @param group primary key
    */
    public function delete($id){
    	$sql = 'DELETE FROM groups WHERE id = ?';
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->setNumber($id);
    	return $this->executeUpdate($sqlQuery);
    }
    
    /**
    * Insert record to table
    *
    * @param GroupsMySql group
    */
    public function insert($group){
    	$sql = 'INSERT INTO groups (name) VALUES (?)';
    	$sqlQuery = new SqlQuery($sql);
    
    	$sqlQuery->set($group->name);
    
    	$id = $this->executeInsert($sqlQuery);	
    	$group->id = $id;
    	return $id;
    }
    
    /**
    * Update record in table
    *
    * @param GroupsMySql group
    */
    public function update($group){
    	$sql = 'UPDATE groups SET name = ? WHERE id = ?';
    	$sqlQuery = new SqlQuery($sql);
    
    	$sqlQuery->set($group->name);
    
    	$sqlQuery->setNumber($group->id);
    	return $this->executeUpdate($sqlQuery);
    }
    
    /**
    * Delete all rows
    */
    public function clean(){
    	$sql = 'DELETE FROM groups';
    	$sqlQuery = new SqlQuery($sql);
    	return $this->executeUpdate($sqlQuery);
    }
    
    public function queryByName($value){
    	$sql = 'SELECT * FROM groups WHERE name = ?';
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->set($value);
    	return $this->getList($sqlQuery);
    }
    
    
    public function deleteByName($value){
    	$sql = 'DELETE FROM groups WHERE name = ?';
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->set($value);
    	return $this->executeUpdate($sqlQuery);
    }
    
    
    
    /**
    * Read row
    *
    * @return GroupsMySql 
    */
    protected function readRow($row){
    	$group = new Group();
    
    	$group->id = $row['id'];
    	$group->name = $row['name'];
    
    	return $group;
    }
    
    protected function getList($sqlQuery){
    	$tab = QueryExecutor::execute($sqlQuery);
    	$ret = array();
    	for($i=0;$i $ret[$i] = $this->readRow($tab[$i]);
    	}
    	return $ret;
    }
    
    /**
    * Get row
    *
    * @return GroupsMySql 
    */
    protected function getRow($sqlQuery){
    	$tab = QueryExecutor::execute($sqlQuery);
    	return $this->readRow($tab[0]);	
    }
    
    /**
    * Execute sql query
    */
    protected function execute($sqlQuery){
    	return QueryExecutor::execute($sqlQuery);
    }
    
    
    /**
    * Execute sql query
    */
    protected function executeUpdate($sqlQuery){
    	return QueryExecutor::executeUpdate($sqlQuery);
    }
    
    /**
    * Insert row to table
    */
    protected function executeInsert($sqlQuery){
    	return QueryExecutor::executeInsert($sqlQuery);
    }
    }
    ?>
    

      

    我们定义了GroupMysqlDAO类并实现了所有的接口方法,2个比较典型的DAO行为如下:

    • load($id)方法:传入主键id,从mysql查询回记录,并最终通过readRow($row)方法将数据库的记录行转换成了一个Group model对象返回。
    • insert($group)方法:传入一个Group model对象,方法中将其name属性取出拼成insert语句,最终插入到了mysql中。

    这里见到的SqlQuery和QueryExecutor对象,都是对Mysql基础API的进一步封装,因此不做深入,可以认为它们与PDO库功能类似。

    通过这个DAO类的对象,我们可以非常方便的操作Group model对象来与mysql进行数据交换,对于上述的简单数据库操作来说,开发者根本不需要写任何SQL即可完成开发,仅仅利用GroupMysqlDAO和Group2个类既可完成业务逻辑的开发。

    然而现实情况并没有这么简单,通常我们需要按其他索引进行数据库查询或者排序,按照一些复杂的条件进行数据库的记录批量更新,甚至进行多表Join,这些能力当前的DAO并没有实现,因此我们自然想到对这个GroupMysqlDAO类进行扩展,添加一些我们想要的函数,这些函数可以接受任意参数,返回任意值,这里我并不考虑新函数的通用性(例如排序规则是用户传入的)。

    这里拿一个另外的Content model为例,我们继承它的DAO添加2个特殊的查询方法,并且添加1个联查User表的方法,它返回若干UserContent model对象,然而这个UserContent model对象并不对应数据库中真实存在的表,这是没有任何问题的:

    /*
    * Class that operate on table 'content'. Database Mysql.
    * Here you can write non standard sql queries
    *
    * @author: http://phpdao.com
    * @date: 2009-10-17 03:43
    */
    class ContentMySqlExtDAO extends ContentMySqlDAO{
    
    function queryByContentAndCreatedBy($content, $createdBy){
    	$sql = "SELECT * FROM content WHERE title like '%".$content."%' AND created_by=?";
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->setNumber($createdBy);
    	return $this->getList($sqlQuery);
    }	
    
    /**
    * Get rows count where column created_by is equal to method param
    */
    function getCountByCreatedBy($createdBy){
    	$sql = "SELECT count(*) FROM content WHERE created_by=?";
    	$sqlQuery = new SqlQuery($sql);
    	$sqlQuery->setNumber($createdBy);
    	return $this->querySingleResult($sqlQuery);
    }
    
    /**
    * This method returns array of object UserContent. 
    * Here sql query gets data from two tables.
    * Developer must loop by variable tab and create for all rows objec UserContent
    * and add this object to new array
    */
    function getUserNameAndContentTitle(){	
    	$sql = "SELECT u.name, c.title FROM users u, content c WHERE c.created_by=u.id";
    	$sqlQuery = new SqlQuery($sql);
    	$tab = $this->execute($sqlQuery);
    	$ret = array();
    	for($i=0;$i $userContent = new UserContent();
    		$userContent->username = $tab[$i]["name"];
    		$userContent->title = $tab[$i]["title"];
    		$ret[$i] = $userContent; 
    	}
    	return $ret;
    }
    }
    /**
    * Non standard transfer object 
    */
    class UserContent{
    	var $username;
    	var $title;
    }
    ?>
    

      

    综上,

    1,DAO对象实现了Model和数据库Row之间的互相转化能力。

    2,Model不一定必须对应数据库中真实存在的表,但它通常应该对应。

    3,DAO的实现并不一定是访问数据库,它的数据来源可以是任意的,例如一个远程的网络服务,只要通过DAO接口可以实现model与数据源之间的互相转换既可,这就是它的抽象能力所在了。

    4,DAO+model是实现M层的最基础模式。

    AR

    Active Record是一种在DAO+model基础上演化出来的东西,像一个混血儿。

    它首先是一个model,也就是AR的1个对象对应了数据库中的一行记录,它有若干属性对应了数据库中的列。

    此前我们知道,DAO和model是2个类,相当于在2个层级上,彼此分离。而AR则将DAO的能力挪到了model本身,也就是说原本这样的调用:

    $model = new Model(); $model->name = "owen"; $dao = new Dao(); $dao->insert($model);

    现在变成了这样:

    $ar = new AR(); $ar->name = "owen"; $ar->insert();

    从直观上来看,AR模式使用起来更加方便,如果你仔细观察2者的差异,你会发现AR模式并不神秘,你只需要简单的将DAO和Model组合一下就可以实现AR了,例如:

    class AR extens Model {
    	// 继承自model的属性
    	// public $id;
    	// public $name;
    
    	// dao对象
    	private static $dao_ = new Dao();
    
    	// 封装dao方法
    	public function insert() {
    		self::$dao_->insert($this);
    	}
    }
    

      

    • 我们让AR继承model,这样AR类的对象就可以进行属性赋值:$ar->name = "owen" 了。
    • 其次,我们将DAO放在static属性实现单例,因为它仅仅是一个工具类。
    • 为AR类添加实例方法insert,给dao对象传入$this(相当于$model),这样通过:$ar->insert() 既可完成数据的插入。

    无论是DAO还是AR,其实都可以实现更加通用的接口,例如:查询可以传入条件,更新/删除支持传入的条件批量更新(本文仅支持1个model对象的更新),并且不再针对具体的model(本文的DAO竟然只针对Group model)。

    另外,现在主流思路一般是:针对关系型数据库实现一套DAO/AR,它们的接口尽量通用和全面,底层则通过PDO进行数据库访问的实现统一。对于非关系型数据库,则分别实现自己的DAO/AR类。

    更多文章无法转载到cnblog,如果喜欢我的博客可以直接访问:鱼儿的博客

  • 相关阅读:
    【转】【矩阵】坐标的矩阵变换
    cocos2d-x聊天气泡
    lua自用的函数收集
    lua错误收集
    cocos2d-x中CCEditbox导出到lua
    love2d杂记9--光照效果
    (转)love2d有用的辅助库--gamework
    XPath语法 在C#中使用XPath示例
    WCF的CommunicationObjectFaultedException异常问题
    WCF绑定(Binding)
  • 原文地址:https://www.cnblogs.com/qq120848369/p/6101038.html
Copyright © 2011-2022 走看看