I am working on a project with CodeIgniter. I extended CI's base Controller class with a custom MY_Controller. MY_Controller has a authentication flag variable $auth = FALSE. On pages that require authentication, I call my auth_model->runAuth() function to run checks and this flag should be updated to TRUE if all checks pass. For some reason I can't update my $auth variable in MY_Controller directly from the auth_model with $this->auth = TRUE, but I have to pass the check result back to the page controller first and then update the $auth variable in MY_Controller. Any ideas how to update the $auth flag in MY_Controller directly from the model without going through a controller? Thank you very much in advance!
Your best bet would be to assign the flag directly via the method call like this
$this->auth = $this->auth_model->runAuth();
In the MY_Controller class! The method runAuth() does not need a big change:
Instead of calling $auth = TRUE or FALSE, just return true or false like this:
public function runAuth()
{
// do stuff
return true; // or false depending on success.
}
Hope that helps. Otherwise you would need a refernce to the MY_Controller object somehow. For Example:
$this->auth_model->runAuth($this);
Now in your method:
public function runAuth(MY_Controller $myctrl)
{
// do stuff
$myctrl->auth = true; // or false
}
Another option would be to use static fields:
class MY_Controller extends Controller
{
public static $auth = false;
// the other stuff
}
Now you can update this without an object refernce:
public function runAuth()
{
// do stuff
MY_Controller::$auth = true;
}
In your models you can access it like this:
if (static::$auth) echo "Boo Yeah!";
Related
Lets say I am building an OOP-based user authentication system, and I would like to incorporate the following principles: Direct Injection, Inheritance, Encapsulation, Polymorphism and the Single Responsibility Principle.
My background in programming is has always relied on procedural programming, and thus, am finding it difficult to really put these practices into correct use.
Assume I have these classes:
class Config
{
public function set($key, $value);
public function get($key, $default = null);
}
class User
{
public function __construct(PDO $dbh, $id = null);
public function setProfile(Profile $profile);
}
class Auth
{
public function __construct(Config $config);
public function login($username, $password, $keepLoggedIn = true);
public function isLoggedIn();
public function getLoggedInUser();
public function logout();
public function register(array $data);
}
class Session
{
public function start($sessionName = null);
public function write($key, $value);
public function read($key, $default = null);
}
class Profile
{
public function setAddress(Address $address);
public function setName($name);
public function setDOB(DateTime $date);
public function getAge();
}
class Validator
{
public function validate($input);
}
I have intentionally left off the function bodies to keep things simple.
To the best of my knowledge, I believe I'm using the principles correctly. However, I am still unclear as to how you would connect classes like: the Validator to the User model, the User model to the Auth and the Session to the Auth class. All of which depend on each other.
You are on the right track. The way these classes connect to each other is called extending. I tend to go towards an MVC setup, meaning Model, View, Controller.
Your logic goes into the controller, all your DB queries and concrete back end methods go in the model. The controller receives requests and returns responses. It's the middleman. It talks to the back end after a request has been made to it, and feeds the front in via response.
So you have a core controller (keep it bare minimal), then each class you make extends the core controller. So your controller is where you tie all this together.
<?php
//your main core controller, where you load all these things you need avilable, so long as this class is extended
class CoreController {
public $auth
public $session;
public $view;
function construct__ ()
{
$this->auth = instantiateAuthClassHere();
$this->session = instantiateSessionClassHere();
$this->view = instantiateViewClassHere();
}
public function anotherHelperForSomething(){
//helper stuff for this method
}
}
//index, page, or content controller, depending on how many you need, i.e. if you want a controller for each page, thats fine, e.g indexController, etc..
//this is the middle man, has logic, receives requst, returns response to view.
class Controller extends CoreController {
public function index (){
$userModel = new userModel();
//do something with this
$session = $this->session;
$content = 'some html';
$userInfo = $userModel->getUsers();
$view = $this->view->render( array(
'content' => $content,
'userInfo' => $userInfo,
));
return $view;
}
}
//Core LIbraries
class Validator {
//your validator stuff
}
//Core LIbraries
class Session {
//your validator stuff
}
//Core LIbraries
class Auth {
//your validator stuff
}
class CoreModel{
public $validator;
function __construct(){
$this->validator = instantiateValidatorClassHere();
}
}
//a user model class (back end). you want a model class for each db table pretty much.
class UserModel extends CoreModel {
// if you need the validator anywhere inside this class, its globally available here inside any class that extends the CoreModel, e.g. $this->validator->methodName()
public function getUsers (){
$sql = 'SELECT * from users';
$result = $db->get($sql);
return $result;
}
}
Notice, on the Controller, this is a generic name for something like indexController, or anything custom. Also, I have the word extends there. It inherits all the objects from the parent that it extends. Inside it, now they will be available via $this->. See my example where I get $this->session.
Try to avoid constructs - you probably don't need them anywhere except for the core, and under special circumstances, which you might then need to check for yourself before you do even that. I dont use constructs much anymore. It can be a bit clunky and unmanageable.
For each request I have to load or, at least, create instance of a MyUser, which contains username, some internal permissions info, link to avatar and so on.
The thing is that I need this info for each and every controller and, for most of the views (to render or not to render some controls depending on user status and permissions).
It sounds like the need for a global variable, created at the time request being handled. What is the best way to solve this problem?
Override CWebUser (which is what you call when you issue Yii::app()->user) with your custom class WebUser (placed in the components or other folder that has it's classes autoincluded), and define some getters like it is done with getRole() example below:
<?php
class WebUser extends CWebUser {
private $_model = null;
function getRole() {
if($user = $this->getModel()){
return $user->userRole->name;
}
}
private function getModel(){
if (!$this->isGuest && $this->_model === null){
$this->_model = User::model()->findByPk($this->id);
}
return $this->_model;
}
}
If you user the custom class instead of CWebUser, you have to explicitly tell which class to use in application's config:
'user'=>array(
'class' => 'WebUser',
// …
),
You can create (or inject) an instance of MyUser in the constructor of your base controller, and set it to a public property:
//i am not familiar with Yii naming conventions, so ignore class name etc
class BaseController
{
public $user;
//presuming you can inject, if not $user = new MyUser();
function __construct(MyUser $user){
$this->user = $user;
}
}
Then all controllers that inherit BaseController can access if they need to:
class HomeController extends BaseController
{
function someAction(){
$name = $this->user->name;
}
}
And regardless of whether an action accesses the instance, its available in all views, without passing as a parameter to render:
//someview
echo $this->user->name;
I was trying to learn how to use unit testing in CakePhp, I'm trying to write a controller test. I read about testAction() and debug() function, but It doesn't work for me, I mean, the test method passes, but the debug() returns null (because testAction does)
This is my code:
<?php
App::uses('Controller', 'Controller');
App::uses('View', 'View');
App::uses('PostsController', 'Controller');
class PostsControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
$Controller = new Controller();
$View = new View($Controller);
$this->Posts = new PostsController($View);
}
public function testIndex() {
$result = $this->testAction('Posts/Index');
debug($result);
}
}
Posts/index controller returns a list of all posts stored in the DB.
I'm assuming you're using CakePHP 2.
$this->testAction() can return a few different results, depending on the options you give it.
For example, if you set the return option to vars, the testAction() method will return an array of the vars that have been set in the tested action:
public function testIndex() {
$result = $this->testAction('/posts/index', array('return' => 'vars'));
debug($result);
}
In this example, the debug data should be an array of the vars that you set in the /posts/index action.
The CakePHP documentation describes the possible results that you can have returned here: http://book.cakephp.org/2.0/en/development/testing.html#choosing-the-return-type
Note that the default option, result, gives you the value that your controller action returns. For most controller actions this will be null, so the fact that you're getting null in your example is to be expected.
mtnorthrop's answer did work for me, but only once I also dealt with my site's authorization as well.
If your site uses authorization, then testAction('/action', array('return' => 'contents') will return null. I've seen a few solutions for this:
One is to follow the solution given here:
CakePHP Unit Test Not Returning Content or View
where you check in AppController::beforeFilter() whether you are in debug mode, and if so, always authenticate the user:
// For Mock Objects and Debug >= 2 allow all (this is for PHPUnit Tests)
if(preg_match('/Mock_/',get_class($this)) && Configure::read('debug') >= 2){
$this->Auth->allow();
}
Another is to follow the suggestions given in this discussion: https://groups.google.com/forum/#!topic/cake-php/eWCO2bf5t98
and to mock the Auth object using the ControllerTestCase's generate function:
class MyControllerTest extends ControllerTestCase {
public function setUp() {
parent::setUp();
$this->controller = $this->generate('My',
array('components' => array(
'Auth' => array('isAuthorized')
))
);
$this->controller->Auth->expects($this->any())
->method('isAuthorized')
->will($this->returnValue(true));
}
}
Note (I'm using CakePhp 2.3.8)
I have this thing that I need in multiple places:
public function init()
{
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
}
These two lines:
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) $this->_redirect('/'); #Logout the user
Whats the best way to do it in ZendFramework?To create a plugin or? I mean I want to execute it in multiple places but If I need to edit it I want to edit it in one place.
Here is an example of an Action Helper that you can call from your controllers easily.
<?php
class My_Helper_CheckFbLogin extends Zend_Controller_Action_Helper_Abstract
{
public function direct(array $params = array())
{
// you could pass in $params as an array and use any of its values if needed
$request = $this->getRequest();
$view = $this->getActionController()->view;
$fbLogin = new Zend_Session_Namespace('fbLogin'); #Get Facebook Session
if(!$fbLogin->user) {
$this->getActionController()
->getHelper('redirector')
->gotoUrl('/'); #Logout the user
}
return true;
}
}
In order to use it, you have to tell the helper broker where it will live. Here is an example code you can put in the bootstrap to do so:
// Make sure the path to My_ is in your path, i.e. in the library folder
Zend_Loader_Autoloader::getInstance()->registerNamespace('My_');
Zend_Controller_Action_HelperBroker::addPrefix('My_Helper');
Then to use it in your controller:
public function preDispatch()
{
$this->_helper->CheckFbLogin(); // redirects if not logged in
}
It doesn't go into much detail, but Writing Your Own Helpers is helpful as well.
If you need this check in every Controller you could even set up a baseController from which you extend instead of the default one:
class My_Base_Controller extends Zend_Controller_Action
{
public function init()
{ ...
class IndexController extends My_Base_Controller
{ ...
Shift your init() into the base controller and you don't need to repeat yourself in every specific controller.
Need a varying init() in a specific controller?
class FooController extends My_Base_Controller
{
public function init()
{
parent::init();
...
So I'm starting up in Zend framework and looking to implement a site-wide "User" session.... something I can easily access from ALL modules/controllers in the application.
I'm like, should I make a new namespace in the library and extend the controller, like:
class MYCUSTOMLIB_Controller_Action extends Zend_Controller_Action
{
protected $_userSession;
function preDispatch(Zend_Controller_Request_Abstract $req)
{
$this->_userSession = new Zend_Session_Namespace('user');
}
}
ANd then have all my controllers/modules/etc extend from that?
Or should I create a Plugin or what? How would you go about making this plugin to pass the user session to the controller?
Or do I do it in the bootstrap?? Again how to pass to controller?
Also should I use Zend_Session_Namespace or Zend_Http_Cookie and also how do I encrypt and xss clean the cookie or is that done automagically?
I would initialise in the bootstrap too:
//Bootstrap.php
protected function _initUserSession()
{
return new Zend_Session_Namespace('user');
}
Then I would use an action helper:
// library/App/Controller/Action/Helper/Session.php
class App_Controller_Action_Helper_Session extends Zend_Controller_Action_Helper_Abstract
{
function direct()
{
return $this->getFrontController()->getParam('userSession');
}
}
You access it in your controller like this:
function indexAction()
{
$session = $this->_helper->session;
}
You should initialize your session in the bootstrap. You can either put it in the Zend_Registry and access it that way or from your controllers you can access your bootstrap by calling $this->getInvokeArg('bootstrap').
// in your controllers
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$this->_session = $bootstrap->getResource('session');
}