談談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,如果喜歡我的博客可以直接訪問:魚兒的博客

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM