Малко объркано звучи заглавието, но искам да споделя как се справям с типа на изгледа (прим. HTML, PDF, JSON и т.н.) и достъпа до определени действия за отделните потребители.
Става въпрос за CodeIgniter рамката.
Първо създавам Контролер клас, който обслужва йерархията от класове:
GeSHi (PHP):
<?php
/**
* Remap enabled Controller class
*
*
* @package ExtCI
* @subpackage Libraries
* @category Libraries
* @author VladSun
*
*/
abstract class Remap_Controller extends Controller
{
public function __construct()
{
parent::__construct();
}
protected function preRemap($method, $arguments) {}
protected function postRemap($method, $arguments) {}
protected function getRemapping($method, $arguments)
{
$remapping = new stdClass();
$remapping->method = $method;
$remapping->arguments = $arguments;
return $remapping;
}
public function _remap($method)
{
$arguments = array_slice($this->uri->segment_array(), 3);
$this->preRemap($method, $arguments);
$remapping = $this->getRemapping($method, $arguments);
{
show_404
("{".get_class($this)."}/{$remapping->method}");
}
else
{
$this->postRemap($remapping->method, $remapping->arguments);
}
}
}
Класът е прост и няма много нужда от обяснения - създава getRemapping метод за всички наследници, които трябва да го извикват след методът от родителския клас.
Второ - управление на изгледите:
GeSHi (PHP):
<?php
/**
* View context enabled Controller class
*
*
* @package ExtCI
* @subpackage Libraries
* @category Libraries
* @author VladSun
*
* @property string $requestedViewMode
* @property string $defaultViewMode
*
*/
abstract class ViewContext_Controller extends Remap_Controller
{
protected $requestedViewMode = null;
protected $defaultViewMode = 'json';
public function __construct()
{
parent::__construct();
}
public function view($view, $data = null, $viewMode = null)
{
$this->load->view($this->getViewMode($viewMode).'/'.$view, $data);
}
protected function setDefaultViewMode($viewMode)
{
$this->defaultViewMode = $viewMode;
}
protected function getViewMode($viewMode = null)
{
if ($viewMode)
return $viewMode;
if ($this->requestedViewMode)
return $this->requestedViewMode;
if ($this->defaultViewMode)
return $this->defaultViewMode;
return 'html';
}
protected function getRemapping($method, $arguments)
{
$remapping = parent::getRemapping($method, $arguments);
$mv = explode('.', $remapping->method);
{
$this->requestedViewMode = $mv[1];
else
throw new Exception ('Грешно зададен формат.');
}
else
{
$this->requestedViewMode = null;
}
$remapping->method = $mv[0];
return $remapping;
}
}
Класът определя въз основа на URL-a, какъв изглед трябва да се зареди. Прим. ако е изивкан
http://example.com/user/get.html ще се зареди изглед с HTML формат. Ако е извикан
http://example.com/user/get.pdf ще се зареди PDF формата. Това става с извикването на методa view(), който зарежда изгледа от директорията съответстваща на разширението. Прим.:
http://example.com/user/get.html => $this->view('user/get') => /html/user/get.php
Създаваме абстрактен клас за контрол на достъпа (и документиране на действията):
GeSHi (PHP):
/**
* Authenthification check enabled Controller class
*
* Checks for proper authenthification if $noRoleCheck is false.
*
* @package ExtCI
* @subpackage Libraries
* @category Libraries
* @author VladSun
*
* @property bool $permissionCheckRequired
* @property bool $loggingRequired
*
*/
abstract class BaseAuth_Controller extends ViewContext_Controller
{
const WITHOUT_PERMISSION_CHECK = false;
const WITH_PERMISSION_CHECK = true;
const WITHOUT_LOGGING = false;
const WITH_LOGGING = true;
private $loggingRequired = true;
private $permissionCheckRequired = true;
public function __construct()
{
parent::__construct();
}
protected function suspendLogging()
{
$this->loggingRequired = false;
}
protected function resumeLogging()
{
$this->loggingRequired = true;
}
protected function suspendPermissionCheck()
{
$this->permissionCheckRequired = false;
}
protected function resumePermissionCheck()
{
$this->permissionCheckRequired = true;
}
protected function getRemapping($method, $arguments)
{
$remapping = parent::getRemapping($method, $arguments);
if (!$this->isPertmitted($remapping->method, $remapping->arguments))
{
$remapping->arguments = array($remapping->method, $remapping->arguments);
$remapping->method = 'notPermitted';
}
return $remapping;
}
protected function postRemap($method, $arguments)
{
if ($this->loggingRequired)
{
$this->prepareToLog($method, $arguments);
}
}
protected function hasPermission($method, $arguments)
{
return false;
}
protected function notPermitted($method, $arguments)
{
}
private function prepareToLog($method, $data)
{
$this->log(get_class($this), $method, $data);
}
protected
function log($module, $action, $data) {
}
private function isPertmitted($method, $arguments)
{
if ($this->permissionCheckRequired === self::WITHOUT_PERMISSION_CHECK)
return true;
return $this->hasPermission($method, $arguments);
}
}
За простота ще покажа класовете необходими за една единствена роля (т.е. идентифициран и неидентифициран потребител):
GeSHi (PHP):
<?php
/**
* Authenthification check enabled Controller class
*
* Checks for proper authenthification if $noRoleCheck is false.
*
* @package ExtCI
* @subpackage Libraries
* @category Libraries
* @author VladSun
*
* @property CurrentUser_Model $currentUser
* @property Logger_Model $logger
*
*/
abstract class Auth_Controller extends BaseAuth_Controller
{
function __construct()
{
parent::__construct();
$this->load->model('application/CurrentUser_Model', 'currentUser');
$this->load->model('application/Logger_Model', 'logger');
$this->logger->setCurrentUser($this->currentUser);
}
protected function hasPermission($method, $arguments)
{
return $this->currentUser->isLogged;
}
protected function notPermitted($method, $arguments)
{
Error::register('Не сте автентифицирали пред системата.');
$this->view('error/notLogged');
}
protected
function log($module, $action, $data) {
if (!$data)
$this->logger->log($module, $action, array_merge($data, $_POST));
}
}
Моделът за текущия потребител:
GeSHi (PHP):
class CurrentUser_Model extends Model
{
public $isLogged = false;
public $userRecord = null;
public function __construct()
{
parent::__construct();
$this->isLogged = empty($_SESSION) ?
false : (empty($_SESSION['user_logged']) ?
false : true);
if ($this->isLogged)
{
if (!$this->userRecord = Doctrine::getTable('RUser')->find($_SESSION['user_id']))
{
unset($_SESSION['user_id']);
unset($_SESSION['user_logged']);
unset($this->userRecord);
$this->userRecord = null;
$this->isLogged = false;
}
}
}
public function getId()
{
return $this->userRecord ? $this->userRecord->id : 0;
}
public function getUsername()
{
return $this->userRecord ? $this->userRecord->username : null;
}
public function doLogin()
{
$this->load->library('validation');
$rules['user_name'] = "xss_clean|trim|required|min_length[3]|max_length[20]|htmlspecialchars";
$rules['user_password'] = "xss_clean|trim|required|min_length[3]|max_length[20]|htmlspecialchars";
$this->validation->set_rules($rules);
$fields['user_name'] = 'Потребител';
$fields['user_password'] = 'Парола';
$this->validation->set_fields($fields);
if ($this->isLogged)
Error::register('Вече сте влезли в системата.');
elseif ($this->validation->run() == false)
Error::register($this->validation->error_string);
elseif (!$this->login($this->validation))
Error::register('Грешни потребителско име и/или парола.');
else
{
$this->isLogged = true;
$_SESSION['user_id'] = $this->userRecord->id;
$_SESSION['user_logged'] = true;
}
}
public function doLogout()
{
unset($_SESSION['user_id']);
unset($_SESSION['user_logged']);
$this->isLogged = false;
}
protected function login($data)
{
$this->userRecord = Doctrine::getTable('RUser')->findOneByUsername($data->user_name);
if ($this->userRecord)
{
return ($this->userRecord->userpass === $data->userpass && $this->userRecord->active);
}
return false;
}
}
Моделът за документиране:
GeSHi (PHP):
<?php
class Logger_Model extends Model
{
private $currentUserModel = null;
public function __construct()
{
parent::__construct();
}
public function setCurrentUser($currentUser)
{
$this->currentUserModel = $currentUser;
}
public function log($module, $action, $data) {
$record = new RAdministratorLog();
$record->FK_user_id = $this->currentUserModel->getId();
if (!$record->FK_user_id)
return;
$record->module = $module;
$record->action = $action;
$record->data = json_encode($data);
$record->save();
}
}
Изполвал съм Doctrine за ORM и DAL. По ваш избор можете да замените съответния кода за работа с ДБ.
Контролерът за login/logout:
GeSHi (PHP):
<?php
class CurrentUser extends Auth_Controller
{
function __construct()
{
parent::__construct();
$this->suspendPermissionCheck();
}
public function login()
{
$this->currentUser->doLogin();
$this->view('currentUser/login');
}
public function logout()
{
$this->currentUser->doLogout();
$this->view('currentUser/logout');
}
}
Всеки друг Контролер, изискващ идентификация трябва да е наследник на Auth_Controller.
Вижда се, че използвам статичен Error клас за съхраняване на съобщенията за грешки - пак променяйте по ваш избор.