Is there a way in CakePhp 2.X where at each beginning of a page my user's session is refreshed with the data in the database ?
For the moment, i could only make it work if the user is inside the UsersController :
public function beforeRender($user = null){
parent::beforeRender($user);
if(AuthComponent::user()){
$info = $this->User->findById( $this->Auth->User('id'));
$this->Auth->login( $info['User']);
}
}
But i have no clue on how to automate this action through the whole site since the User object only exists in my UsersController.
I'm new to cakePhp so sorry if i miss some concepts.
Move the logic to the beforeRender callback of your AppController and remember to make sure that you've loaded the User model that you want to query:-
public function beforeRender() {
parent::beforeRender();
if (AuthComponent::user()) {
$this->loadModel('User');
$info = $this->User->findById( $this->Auth->User('id'));
$this->Auth->login($info['User']);
}
}
Also note that the beforeRender method takes no parameters.
I'd move this function to the AppController (which all controllers should inherit from), this would then fire on every page lifecycle
Related
I am currently using the codeigniter tank_auth, at the start of every controller method I have to do the following:
$data['profile'] = $this->tank_auth->get_profile();
The main reason I do this is to display the current logged in username, and also get their privilege level.
I am going over the code trying to go by the DRY principle and have moved a lot of repeated code over to the _constructor method (Like checking if the user is logged in). I am just wondering if there is a way to move this code from the start of every method to the constructor.
My current constructor method looks like so:
public function __construct()
{
parent::__construct();
// If the user isn't logged in redirect to login page.
if (!$this->tank_auth->is_logged_in())
redirect('auth/login');
}
Thanks!
Add variable $data to the controller and use it for all your view data. For example:
public function __construct()
{
parent::__construct();
$this->data['profile'] = $this->tank_auth->get_profile();
}
When calling the view remember to call it like this:
$this->load->view('my_view', $this->data);
You can also extend CI_Controller with MY_Controller and put the login check in the constructor of MY_Controller. Just extend all controllers which need this check from MY_Controller instead of CI_Controller.
I am working on building a lightweight MVC, mainly for the learning process but I would like it to be good enough to use eventually.
Below is a basic example/demo of how a basic controller might would look, let's assume the URI has been processed and routed to this controller and these 2 methods.
1) I need to get data from database/cache/etc... inside my Model classes, I just need help on how I should load my models into my example controller below, you can see that I have added this below $profileData = $this->model->getProfile($userId) that is just made up and does not exist's, how could I get something like that to work though? Or should I load the model into the class a different way?
2) A lot of pages will require a user to be logged into the site. SHould I process that part below in the controller to check if a user is logged in, example, before building the profile page, check if user is logged in, if not then build a login page instead and add these checks inside of each controller method/page?
/**
* Example Controller
*/
class User_Controller extends Core_Controller {
// domain.com/user/id-53463463
function profile($userId)
{
//GET data from a Model
$profileData = $this->model->getProfile($userId);
$this->view->load('userProfile', $profileData);
}
// domain.com/user/friends/
function friends()
{
//GET data from a Model
$friendsData = $this->model->getFriendlist();
$this->view->load('userFriends', $friendsData);
}
}
core
abstract class Core_Controller {
protected $view;
protected $model;
function __construct(DependencyContainer $dependencyContainer){
$this->view = new Core_View();
//$this->view = $dependencyContainer->get(view);
}
}
There are probably tons of ways to accomplish what you are trying.
The "easiest" is probably to just override the constructor and instantiate the model directly.
in User_Controller:
public function __construct(DependencyContainer $dc) {
parent::__construct($dc);
$this->model = new User_Model();
}
I'm guessing that you are looking for something a little more automated though. If you want the Model to have the same name as the controller minus "_Controller", just use get_class($this) in the constructor and use PHP's string functions to parse out what you want. Once you have that in a variable, you can use that variable to instantiate the model:
in Core_Controller:
public function __construct(DependencyContainer $dc) {
$this->view = new Core_View();
// $model_class should be 'User_Model' now
$model_class = str_replace('_Controller', '_Model', get_class($this));
// now instantiate the model
$this->model = new $model_class();
}
I haven't actually worked with any framework that can only have one model associated with each controller (except may CakePHP? I can't remember). With Symfony, the models and controllers are completely decoupled so you can use any model with any controller. You just instantiate the model as need. Symfony use the Doctrine ORM so for example, in a controller action, if you needed a model you would do something like this:
$model = Doctrine::getTable('User');
It might be worthwhile to consider a design more like that in order to promote a decoupled design and I promise that you will want more than one model in some controller at some point.
2.) As far as authentication. Something that seems to be fairly common is to have some sort of setting (whether in a config file or a member variable) that says whether or not the current action needs the user to be authenticated. This is processed each time the action runs (Yii calls these kinds of things filters). If the user needs to be logged in, it stores the page that they are trying to access, and then redirects them to a log in page (you should only ever have to create one). Once they properly authenticate, it will redirect them back to where they were originally heading.
By default the cakephp Auth class redirects the viewer to the front page if he/she is not logged in and tries to access a denied page.
I wish for the server to run some code when this happens (set some variables and stuff). How would I go about modifying the Auth class's behavior when it redirects the user to the home page due to lack of authentication.
From the sounds of your question, you need to specify your app's allowed actions, then test for authentication and call your method if users are not logged in. Place in AppController if you want this to be application-wide.
Auth::user() returns null if the user is not logged in.
class AppController extends Controller {
var $components = array('Auth', 'Session');
function beforeFilter() {
parent::__construct();
// Your app-wide beforeFilter code, if any
$this->Auth->allow('index', 'view', 'register', 'whatever');
if ($this->Auth->user() == null) {
$this->_attemptRestricted();
}
}
function _attemptRestricted() {
// set your variables, etc...
}
}
All of Auth's defaults can be customized as your application requires.
For more about Auth::user() --
http://book.cakephp.org/view/1264/user
All about AuthComponent --
http://book.cakephp.org/view/1250/Authentication
At first it is no good idea to change the Auth-Component-code itself since in an update you would loose all your changes.
You should write your own component which extends the built-in component like this:
// /app/controllers/components/my_auth.php
App::import('Component', 'Auth');
class MyAuthComponent extends AuthComponent {
function redirect($url = null) {
//have a look in the original auth-component to see how to change this behaviour
}
}
This way you can now use your new component with your extended redirect-method.
Unfortunately you would now have to change each $this->Auth to $this->MyAuth. If you do not want that you can do something like this in your AppController:
public function constructClasses() {
parent::constructClasses();
$this->Auth = $this->MyAuth;
}
I am very, very new with MVC (just started yesterday) so this question is probably stupid, but I need to know how to check automatically if user is logged in on my functions that are in admin/user models.
I can put the checking in construct, this would help, but I have several models and maybe there is some even better way. I hope you will understand better what I want after you see my folder structure and code. Oh, and by the way - I use Code Igniter 2.0
Folders:
controllers/
../admin/
../../item.php
../../cat.php
Let's see my item.php file...
<?php
class Item extends CI_Controller
{
function Index()
{
//Checking if admin is logged in on every function is bad
/*
* Is it possible to somehow make all admin functions go through
* some kind of Admin class that will check automatically?
*/
$isLogged = $this->session->userdata('is_logged_in');
if ($isLogged == true)
{
$this->load->view('admin/item/main');
}
else
{
$this->load->view('admin/login');
}
}
function Add()
{
$this->load->view('admin/item/add');
}
function Edit()
{
$this->load->view('admin/item/edit');
}
function Delete()
{
$this->load->view('admin/item/delete');
}
}
I hope that this is easy question, thanks in advance :)
I would implement the login-function in CI_Controller.
Then I would set an protected variable in Item protected $loginRequired = true;.
In function __construct() or Item() I would call parent::isLoginRequired($this->loginRequired) which checks if a login is required.
I would also redirect to a specific login page with a parameter which redirects the user back to the page he needs to be logged in.
Make a new class, for example, My_Controller extends Ci_Controller and write some auth checker code in it... in controller file just extend My_Controller
what i usually do is -like Teeed recommends- Create my own controller Class which is between the CI_Controller and each controller you might create.
In that class (MY_Controller) you can instantiate a model which handles all user related data and logics (loading session data, executing specific checks, etc..) and finally set as class variables those results, so you will end up having:
$this->isLogged ;
$this->isPaying ;
$this->isPlatinumMember ;
etc..
in any of your classes extending from MY_Controller
that makes very easy to check any condition within any of your Controllers.
I'm fairly new to CodeIgniter and I'm using Ion Auth for user authorization. With the Ion Auth library, you get a user object like:
$user = $this->ion_auth->get_user();
echo $user->email;
If a user is logged in, I want the $user object to be available on any of the pages (in any of the controllers). How can I do this? (I'm using CodeIgniter 2)
This blog post of mine goes into a lot of detail in specifically using Ion_Auth to allow your entire application (including views) to access your current user's information.
The short version... (specifically for the topic at hand)
Adding this to your MY_Controller.php file
class User_Controller extends CI_Controller {
protected $the_user;
public function __construct() {
parent::__construct();
if($this->ion_auth->logged_in()) {
$data->the_user = $this->ion_auth->get_user();
$this->the_user = $data->the_user;
$this->load->vars($data);
}
else {
redirect('/');
}
}
}
Then in your application just create your controller like this
class App_Controller extends User_Controller {
public function __construct() {
parent::__construct();
}
function index() {
//do stuff
}
}
Then not only in your controller will you have access to $this->the_user but you will also have $the_user loaded into every view with $this->load->vars()
Normally you'd cache something like this in the session, however I've moved away from that in most of my CI apps.
An example: you log the user in (caching the user data) and then they go to update their email on their profile page. You now have to reload you cache. Many other things could drive this reload. Usually the most I cache any more is the ID, then I do what you're doing and get the user's data on any page I need it.
One thing I found that helps is to have base controllers. For example I have a RegisteredUserController. Any controller whose actions all require the user to be logged in extend this. That way I can keep the logged in check and things like get the user data in one spot (in the constructor). For example:
class RegisteredUserController extends CI_Controller {
var $user;
function __construct() {
parent::__construct();
$this->user = $this->ion_auth->get_user();
}
}
Then any controller that extends RegisteredUserController instead of just controller can get to the user with $this->user. Following this pattern would get it on every page (that extends the controller) without resorting to caching.