2008年6月18日 星期三

Zend_Acl_Role的設計




/**

* 程式碼是以陳瑩光老師新版學務系統的認證模組改寫的。

*/




要實做Zend_Acl_Role的多重繼承架構,有四個區塊的觀念必須弄清楚,才能把整個架構完整的實做,這四個區塊都弄楚了,程式的實做就是水到渠成的事情。




第一個要弄清楚的區塊就是Zend_Acl_Role的多重繼承觀念,Zend_Acl_Role的繼承關係,跟Zend_Acl_Resource不一樣,Zend_Acl_Resource只能有一個parent,但Zend_Acl_Role卻可以有很多個parent,就像現實生活中的人物一樣,每個人都身兼多重身份,Zend_Acl_Role也可以同時繼承多個parent,與一個實例說明:假設一個Zend_Acl_Role的身份是專案經理,同時繼承了程式設計師、架構設計師及資料庫規畫師的身份,那麼專案經理就同時繼承了指定給這三個身份的所有資源。




第二個要弄清楚的區塊是資料庫結構,如果不使用資料庫,而將Zend_Acl_Role的資料寫在程式碼中,程式碼會變得很冗長,要一行一行coding,不是用幾個迴圈就可以解決的,也難以維護,如果資料非常龐雜,不但程式碼的行數會呈等比級數增加,也會很難搞清楚其中的繼承關係,程式也容易寫錯,為了一勞永逸,最好將這些資料都存入資料庫進行維護管理。存成xml或是ini檔也是可以解決部份的問題,不過資料如果太複雜,也會造成難以維護的結果,最好的解決辦法,就是使用資料庫。




第三個要弄清楚的就是陣列結構,使用陣列結構,只要從資料庫中抓一次資料,將資料轉為陣列結構,在後續的程序中直接使用陣列結構進行資料的操作,如果不使用陣列,那麼後續的程序,會不斷的讀取資料庫,雖然不能說這樣的作法有錯,但資料庫的資料都是存放在硬碟中的,每次讀取資料庫中的資料都會讀取硬碟,而陣列是存放在記憶體裡頭,在速度上,記憶體比硬碟快很多,因此,陣列的操作速度會比資料庫的操作速度快,為了加快速度,最好儘量使用陣列結構進行程式資料的存取。




第四個區塊就是程式的邏輯,這部份要花費很大的心思才能搞懂,如果弄懂了,程式的實做就很快了,只要照著你的思考順序,一步步的將解決方式寫出來就可以建構完整的程式碼了。




用freemind畫了四個區塊的架構圖,以後如果忘記了,不用看程式碼,也不用看說明,看這張圖就一目了然了。



以Zend Framework實做Zend_Acl_Role的雛型


class AclController extends Zend_Controller_Action{
public function init(){
$this->_helper->viewRenderer->setNoRender();
Zend_Loader::loadClass('Zend_Acl');
Zend_Loader::loadClass('Zend_Acl_Role');
Zend_Loader::loadClass('Zend_Acl_Resource');

$acl=new Zend_Acl();
Zend_Registry::set('acl',$acl);
}

public function roleAction(){

$baseDir=Zend_Registry::get('baseDir');

$config=new Zend_Config(require($baseDir.'/application/etc/db.php'));

$db=Zend_Db::factory($config->public);

$db->query('set names utf8');

$sql='select a.id,b.parent_id from acl_role as a left outer join acl_inheritance as b on a.id=b.child_id';
$stmt=$db->query($sql);
$rows=$stmt->fetchAll();

/**
* 將資料轉為所需的陣列結構
* Zend_Acl_Role註冊時所需的陣列結構
*/
$roles=array();
foreach($rows as $row){
if(!isset($roles[$row['id']]))
$roles[$row['id']]=array();
if(isset($row['parent_id']))
$roles[$row['id']][]=$row['parent_id'];
}

$acl=Zend_Registry::get('acl');
do{
$flag=false;

/**
* 註冊role
*/
foreach($roles as $key=>$role){
if(!$acl->hasRole($key) && (!isset($role) || $this->hasAllRole($role))){
$acl->addRole(new Zend_Acl_Role($key),$role);
}
/**
* 檢查第一維的key是否都己註冊為role
* 是---離開
* 否---繼續
*/
if(!$acl->hasRole($key))
$flag=true;
}
}while($flag);

}

public function hasAllRole(array &$role){
$acl=Zend_Registry::get('acl');
foreach($role as $parent_id){
if(!$acl->hasRole($parent_id))
return false;
}
return true;
}

}

沒有留言: