เอาล่ะครับ มาต่อกันจากปีที่แล้ว 555+
ช่วงนี้ผมไปวุ่นๆ กับไอ้ Blogs ตัวนี้ จนไม่ได้มาอัพเดทอะไรเลย แต่วันนี้พอจะมีเวลาเหลือสักหน่อยหลังจากทำเรื่อง Order เสร็จก็จะมาเล่าให้ฟังถึงวิธีการทำ Controller ให้สมบูรณ์ยิ่งขึ้น นั่นก็คือ Controller ที่ใช้ใน Engine นี้แหละ
ก่อน อื่นต้องมาทำความเข้าใขกับตัว C ใน MVC เสียก่อน ตัวมันเองนั้นถือว่าเป็นหหัวใจเลย เพราะมันเป็นทั้งตัวเชื่อมต่อ models กับ views เข้าหากัน และยังเป็นตัวทำงาน Login สำคัญๆ เช่น validation ก่อน สั่งให้ไปหน้านู้น หน้านี้ ถือว่าเป็นแกนหลัก ใน MVC เลยทีเดียว
ตัว Controller ของ CI นั้นจริงๆแล้วก็พอจะมีความสามารถอยู่ในระดับนึง ทำงานค่อนข้างเร็วและมีประสิทธิภาพ แต่ที่ขาดไป และเป็นหัวใจเลยนั่นก็คือ ACL (Access Controller List) แล้ว CI ก็ไม่ได้เตรียม lib ในส่วนนี้มาให้เราเสียด้วย เราก็เลยต้องมาเหนื่อยหน่อย แต่ทำทีเดียวจบครับ....
ก่อนอื่นเราก็ต้องมาสร้าง MY_Controller ขึ้นมา โดยเอาใส่ไว้ใน application/libraries/
ซึ่ง method ที่ผมจะเพิ่มไปให้มันก็คือการทำงานเพื่อเช็ค สิทธิพื้นฐานของ user นั่นเอง ตรงนี้ผมเอา Zend_Acl เข้ามาช่วย จุดประสงค์ของ MY_Controller มีอย่างเดียวคือ ต้องทำ Bootstrap พื้นฐานในการเช็คตรงนี้ให้ได้ สุดท้ายแล้วผมได้ออกมาหน้าตาแบบนี้ครับ
ช่วงนี้ผมไปวุ่นๆ กับไอ้ Blogs ตัวนี้ จนไม่ได้มาอัพเดทอะไรเลย แต่วันนี้พอจะมีเวลาเหลือสักหน่อยหลังจากทำเรื่อง Order เสร็จก็จะมาเล่าให้ฟังถึงวิธีการทำ Controller ให้สมบูรณ์ยิ่งขึ้น นั่นก็คือ Controller ที่ใช้ใน Engine นี้แหละ
ก่อน อื่นต้องมาทำความเข้าใขกับตัว C ใน MVC เสียก่อน ตัวมันเองนั้นถือว่าเป็นหหัวใจเลย เพราะมันเป็นทั้งตัวเชื่อมต่อ models กับ views เข้าหากัน และยังเป็นตัวทำงาน Login สำคัญๆ เช่น validation ก่อน สั่งให้ไปหน้านู้น หน้านี้ ถือว่าเป็นแกนหลัก ใน MVC เลยทีเดียว
ตัว Controller ของ CI นั้นจริงๆแล้วก็พอจะมีความสามารถอยู่ในระดับนึง ทำงานค่อนข้างเร็วและมีประสิทธิภาพ แต่ที่ขาดไป และเป็นหัวใจเลยนั่นก็คือ ACL (Access Controller List) แล้ว CI ก็ไม่ได้เตรียม lib ในส่วนนี้มาให้เราเสียด้วย เราก็เลยต้องมาเหนื่อยหน่อย แต่ทำทีเดียวจบครับ....
ก่อนอื่นเราก็ต้องมาสร้าง MY_Controller ขึ้นมา โดยเอาใส่ไว้ใน application/libraries/
ซึ่ง method ที่ผมจะเพิ่มไปให้มันก็คือการทำงานเพื่อเช็ค สิทธิพื้นฐานของ user นั่นเอง ตรงนี้ผมเอา Zend_Acl เข้ามาช่วย จุดประสงค์ของ MY_Controller มีอย่างเดียวคือ ต้องทำ Bootstrap พื้นฐานในการเช็คตรงนี้ให้ได้ สุดท้ายแล้วผมได้ออกมาหน้าตาแบบนี้ครับ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | <?php if (!defined( 'BASEPATH' )) exit ( 'No direct script access allowed' ); require_once ( 'Zend/Registry.php' ); require_once ( 'Zend/Locale.php' ); class MY_Controller extends Controller { protected $_controller = "" ; protected $_method = "" ; public $app_acl = null; public $app_acl_cache = null; public function __construct() { parent::Controller(); // Load user site info $user_site_info = user_site_info(); // Set default timezone for application $timezone = $this ->config->item( 'timezone' ); date_default_timezone_set( $timezone ); // Set default language $language = ( $user_site_info [ 'locale' ]) ? $user_site_info [ 'locale' ] : $this ->config->item( 'locale' ); $this ->lang->setInstance( $language ); $this ->lang->load( 'site_www' ); $this ->_controller = $this ->uri->rsegment(1); $this ->_method = $this ->uri->rsegment(2); // load zend cache for acl $this ->load->loadClass( 'Zend_Cache' ); // config cache use for access control list $cacheFrontends = array ( 'lifetime' => 86400, 'automatic_serialization' => true, 'automatic_cleaning_factor' => 50 ); $cacheBackends = array ( 'cache_dir' => config_item( 'cache_dir' ) . '/acl' , 'cache_db_complete_path' => config_item( 'cache_dir' ) . '/acl/cache.sqlite' , 'file_name_prefix' => 'acl' , 'hashed_directory_umask' => '0777' , 'cache_file_umask' => '644' , 'hashed_directory_level' => '0' , 'server' => config_item( 'cache_server' ), 'compression' => true ); $this ->app_acl_cache = Zend_Cache::factory( 'Core' , config_item( 'cache_method' ), $cacheFrontends , $cacheBackends ); // cache for access control list if (! $this ->app_acl = $this ->app_acl_cache->load( 'app_acl' )) { $this ->app_acl = $this ->zacl; // this may be not necessary if you give permission to the table name `roles_privileges` $this ->load->model( 'model_privileges' , 'privileges' ); $resources = $this ->privileges->getDistinctControllers(); foreach ( $resources as $resource ) { $this ->app_acl->addResource( $resource [ 'controller' ]); } // select all roles $this ->load->model( 'model_roles' , 'roles' ); $roles = $this ->roles->getRoles(); foreach ( $roles as $role ) { $acl [ $role [ 'id' ]][ 'inherit' ] = $role [ 'inherit' ]; } // select relation betwenn role and privileges $this ->load->model( 'model_roles_privileges' , 'roles_privileges' ); $roles_privileges = $this ->roles_privileges->getRolesPrivileges(); foreach ( $roles_privileges as $roles_privilege ) { // allow and deny data if ( $roles_privilege [ 'allow' ] == '1' ) $acl [ $roles_privilege [ 'role_id' ]][ 'allow' ][ $roles_privilege [ 'privilege_controller' ]][] = $roles_privilege [ 'privilege_action' ]; else $acl [ $roles_privilege [ 'role_id' ]][ 'deny' ][ $roles_privilege [ 'privilege_controller' ]][] = $roles_privilege [ 'privilege_action' ]; } if ( is_array ( $acl ) && sizeof( $acl ) > 0): foreach ( $acl as $role => $data ): // inherite from another role if ( array_key_exists ( 'inherit' , $data ) && $data [ 'inherit' ] != '' ) $this ->app_acl->addRoleInherit( $role , $data [ 'inherit' ]); else $this ->app_acl->addRole( $role ); if ( array_key_exists ( 'allow' , $data )) { foreach ( $data [ 'allow' ] as $controller => $actions ) { foreach ( $actions as $action ) { if ( strcmp ( $controller , '#all' ) == 0 && strcmp ( $action , '#all' ) == 0) $this ->app_acl->allowPermission( $role ); else $this ->app_acl->allowPermission( $role , $controller , $action ); } } } if ( array_key_exists ( 'deny' , $data )) { foreach ( $data [ 'deny' ] as $controller => $actions ) { foreach ( $actions as $action ) { if ( strcmp ( $controller , '#all' ) == 0 && strcmp ( $action , '#all' ) == 0) $this ->app_acl->denyPermission( $role ); else $this ->app_acl->denyPermission( $role , $controller , $action ); } } } endforeach ; endif ; // end if acl // cache acl $this ->app_acl_cache->save( $this ->app_acl, 'app_acl' ); } // end acl cache log_message( 'debug' , 'MY_Controller Class Initialized' ); // run access control list $role = user_info_data( 'role_id' ); if (! $this ->app_acl->isAllowed( $role , $this ->_controller, $this ->_method)) { redirect( '/auth/login?access-denied' ); } } } ?> |
ตรง ที่ include Zend_Locale นั้น ไม่ได้หยิบมาใช้ตรงนี้ครับ มันจะถูกไปใช้กับ MY_Language ที่ผมเอา CI มาแก้อีกที ส่วน Zend_Registry ผมก็เอาไปใช้เรื่องอื่น แต่ไหนๆ ตรงนี้มันก็เป็น แกนหลัก ผมเลยเอาฝากไว้เท่านั้นเอง ดังนั้น ตอนนี้ยังไม่ต้องไปสนใจมากก็ได้ครับ
ที่สำคัญก็คือ
1 | $user_site_info = user_site_info(); |
1 2 | $this ->_controller = $this ->uri->rsegment(1); $this ->_method = $this ->uri->rsegment(2); |
ส่วนเรื่อง Cache เป็นตัวช่วยลดการทำงานของ DB เท่านั้นเองครับ
1 | $this ->app_acl = $this ->zacl; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <?php if (!defined( 'BASEPATH' )) exit ( 'No direct script access allowed' ); require_once 'Zend/Acl.php' ; require_once 'Zend/Acl/Role.php' ; require_once 'Zend/Acl/Resource.php' ; class CI_Zacl { private $_acl = null; public function __construct() { $this ->_acl = new Zend_Acl(); } public function addRole( $roleName ) { $this ->_acl->addRole( new Zend_Acl_Role( $roleName )); } public function addRoleInherit( $roleName , $inheritFromRole ) { $this ->_acl->addRole( new Zend_Acl_Role( $roleName ), $inheritFromRole ); } public function addResource( $resource ) { $this ->_acl->add( new Zend_Acl_Resource( $resource )); } public function allowPermission( $roleName , $controllerName = null, $actionName = null) { return $this ->permission( 'allow' , $roleName , $controllerName , $actionName ); } public function denyPermission( $roleName , $controllerName = null, $actionName = null) { return $this ->permission( 'deny' , $roleName , $controllerName , $actionName ); } public function permission( $type , $roleName , $controllerName , $actionName = null) { if (!in_array( $type , array ( 'deny' , 'allow' ))) { return false; } if ( $this ->_acl->hasRole( $roleName )) { $this ->_acl->{ $type }( $roleName , $controllerName , $actionName ); return true; } return false; } public function isAllowed( $roleName , $controllerName , $actionName ) { if ( $this ->_acl->has( $controllerName )) { $roleName = ( $this ->_acl->hasRole( $roleName )) ? $roleName : 'Guest' ; return $this ->_acl->isAllowed( $roleName , preg_replace( '/^controller_/' , '' , $controllerName ), $actionName ); } return true; } } ?> |
กลับมาที่ MY_Controller ของเราต่อนะครับ
หลัง จากที่ผมทำการ Query พวกสิทธิออกมาจาก Database แล้วก็เอามาเข้า สูตรของ Zend_Acl (ถ้าใครยังงเรื่องนี้อยู่ขอให้ไปย้อนดูบทความเก่าๆ ของผมนะครับ)
จุดที่สำคัญที่สุดก็คือ
1 2 3 4 5 6 | // run access control list $role = user_info_data( 'role_id' ); if (! $this ->app_acl->isAllowed( $role , $this ->_controller, $this ->_method)) { redirect( '/auth/login?access-denied' ); } |
ตรงนี้ก็คือการเอา Role ของ Authen User มาทำการเช็คเข้ากับ Controller และ Method ที่ใช้งานอยู่ว่า สิทธิพอมั้ย ถ้าพอก็ให้ทำงาน ไม่พอผมสั่งไป Re-Login ใหม่ ก็เป็นอันว่า Bootstrap จาก MY_Controller ของเราเป็นอันเสร็จ
ซึ่งจริงๆ แล้วเรายังสามารถเอา ACL ชุดนี้ไปใช้ใย view ได้อีกด้วย ด้วยการเขียน Helper เล็กๆมา แบบนี้
1 2 3 4 5 6 7 8 9 | function is_allowed( $controller , $action , $role =null) { $CI =& get_instance(); if ( is_null ( $role )) { $role = user_info_data( 'role_id' ); } return $CI ->app_acl->isAllowed( $role , $controller , $action ); } |
เท่านี้เราก็จะได้เรื่อง ACL ที่แข็งแรงแลมีประสิทธิภาพแล้วครับ
PS. ต้องขออภัยจริงๆ นะครับ ที่เรื่องนี้ผมสามารถอธิบายได้ดีที่สุด คือแค่ให้ Concept เพราะว่า มันต้องทำเยอะมากๆ จริงๆ กว่าจะออกมาเสร็จสมบูรณ์ ไม่รู้จะอธิบายยังไงให้ครอบคลุม ก็เลยให้ไว้ได้แค่แนวทางครับ เจอกันคราวหน้า ^^
PS. ต้องขออภัยจริงๆ นะครับ ที่เรื่องนี้ผมสามารถอธิบายได้ดีที่สุด คือแค่ให้ Concept เพราะว่า มันต้องทำเยอะมากๆ จริงๆ กว่าจะออกมาเสร็จสมบูรณ์ ไม่รู้จะอธิบายยังไงให้ครอบคลุม ก็เลยให้ไว้ได้แค่แนวทางครับ เจอกันคราวหน้า ^^
create by http://www.jquerytips.com/blogs/view/1093/Beauty-Your-CI-Step-2-Beauty-My-Controller
ไม่มีความคิดเห็น:
แสดงความคิดเห็น