Filter datasource by auth.user.id - cakephp

I'm a newbie to cakephp and I don't quit get it.
I'm building a system where users login and register some data. And I want the users to only see their own data. How do I do this? I was thinking about making a kind of restriction in the model or do I have to code this in every function (Connected to the views)?
I have a well functioning system With user login etc, but I can't separate the data access to the users.
I can't figure this out and I might think that's a bit because I might not know what to ask about. Hoping that some can give me a hand.

You have the logged in users id via $this->Auth->user('id');. You then have to set a condition on your find calls where user_id = $userId.
$userId = $this->Auth->user('id');
$this->SomeRelatedModel->find('all', array(
'conditions' => array(
'SomeRelatedModel.user_id' => $userId,
),
));
You can also pass the $userId variable to the model and do your find calls in there (either methods or custom find calls).
If a lot of your models/find calls need to filter by user id, you may want to create a behavior and use beforeFind callback to add the condition to the query.

When user will login, Auth will store Id in session. you can access it by using
$this->Session->read('Auth.User.id') or $this->Auth->user('id') or AuthComponent::user('id')
$this->User->find('all',array(
'conditions'=>array('id'=>$this->Session->read('Auth.User.id')),
'fields'=>'...............'));

Related

CakePHP - Manual login

I have an application developed with CakePHP, where I'm using the AuthComponent for Authentication and Authorization.
I'm making an action where the user can register on the application using Facebook.
I used HybridAuth to integrate Facebook with my application and I get the user data information to manipulate.
Now, I'm receiving the data correctly, but when I try to force a manual login and redirect the user to an authorized page, the user receives the authentication error.
My code:
$this->Auth->login($user_exists['User']);
$this->redirect($this->Auth->redirect());
Where $user_exists['User'] is equal to:
$user_exists = $this->User->findByUsername($profile->email);
Can somebody tell me what I'm doing wrong and why CakePHP won't accept my data to make a forced login?
Thanks!
Where $user_exists['User'] is equal to: $user_exists
That is your problem right there.
Those cannot be equal since the latter contains a deep array with
'User' => array(...)
and the first one is directly the array(...)
So make sure you pass in the (flat) User array directly.
->login($data)
with
$data = array('id' => 1, 'username' => 'foo, ...)

How can I minimize the 'contain' queries in CakePHP?

I have three models, Users, Comments and Pages.
Users has many Comments, and Comments belong to Pages.
All models use the containable behavior, and default to recursive -1.
If I call a find() query on Comments, with the contain request including the Page model's field, this correctly returns the results using a single query, automagically joining the Page table to the user.
If I call a similar query from the User model (containing Comment and Comment.Page), the result is a query to source the Comments, followed by a query per comment to source the relevant Page.
Is there a way to configure the models to maintain the JOIN optimisation? I assumed the belongsTo declaration on the related model (Comments) would follow through to the host model (Users).
UPDATE
I should clarify, my question used a simplified version of my actual case study. Although the minimal solution I require would include this initial Model hasMany Model belongsTo Model structure, I am also looking for the solution at one or more additional belongsTo Models down the chain (which, I though, would automagically use LEFT JOINs, as this would be feasible).
Hmm that's interesting. That's a sort of optimization that should be implemented in the core :)
At any rate, I think you could get the same results (perhaps formatted differently) by building the query a little differently:
$this->User->Comment->find('all', array(
'conditions' => array(
'Comment.user_id' => $userId
),
'contain' => array(
'User',
'Page'
)
));
By searching from the Comment model, it should use two left joins to join the data since they are both 1:1 relationships. Note: The results array may look a little different than from when you search from the User model.
So are you asking if there is an easier way to just contain all your queries? If you want to contain everything within the current controller. You could do the contain in the beforeFilter() callback and it would apply to all your queries within that controller.
I am not quite sure if I understand your question, but I think you have a problem with the many sql-calls for the Comment -> Page linkage? If that is correct, then
try linkable behaviour which reduces sql calls and works almost as contain does
or if its pretty much the same data you want, then create a function in a specific model from where you are happy with hte sql calls (for example the Comment-Model) and call it from the user model by $this->Comment->myFindFct($params);
hope that helps
EDIT: one thing that comes to my mind. You were able to change the join type in the association array to inner, which made cake to single call the associated model as well
I find a good way to do this is to create a custom find method.
As a for instance I'd create a method inside your User model say called _findUserComments(). You'd then do all the joins, contains, etc.. inside this method. Then in your controllers, wherever you need to get all of your user's comments you would call it thusly:
$this->User->find('UserComments', array(
"conditions" => array(
'User.id' => $userId
)
));
I hope this helps.
If model definition like bellow:
Comment model belongs to Page and User.
Page belongs to User and has many Comment.
User has many Page and Comment
code bellow will return one joined query:
$this->loadModel('Comment');
$this->Comment->Behaviors->attach('Containable');
$queryResult = $this->Comment->find('all', array(
'contain' => array(
'User',
'Page'
)
));
The code bellow will return two query. Page and User joined into one query and all comment in another query
$this->loadModel('Page');
$this->Page->Behaviors->attach('Containable');
$queryResult = $this->Page->find('all', array(
'contain' => array(
'User',
'Comment'
)
));
and also bellow code will return three query, one for each model:
$this->loadModel('User');
$this->User->Behaviors->attach('Containable');
$queryResult = $this->User->find('all', array(
'contain' => array(
'Page',
'Comment'
)
));

Cakephp: $this->Auth->user vs $this->User vs $user

I realize this might not be a clear cut "problem/answer" question, but I think it's worth asking.
In controllers, it seems that there are three options which access the Auth object:
$this->Auth->user
$this->User
$user
They each return the record for the logged in user, and I cannot see much of a difference between them.
Now, it occurs to me that, at a glance, $this->User could be a bit confusing or unclear if working in an associated model $this->Posts->User.
But apart from that, is there a difference between these three options?
$this->Auth->user() returns the currently authenticated user from the session.
$this->User is a model and you won't get the currently authenticated user unless you use the session data (either from Session or Auth component) to get the user id. Either way you'd have to do a query every request to get info about the logged in user.
$user .. is just a variable. I don't understand how this is an "options which access the Auth object"
If you want info about the currently logged in user, use $this->Auth->user();
In cakephp 2 you must use AuthComponent::user($user_field) to access authenticated user data , for example :
for id of user that authenticated you must use AuthComponent::user('id').
As tirang said $this->User is a model and $user just a variable.

CakePHP - What is the best approach to create an Admin Section

I am looking for an insight into the best approach to create an administrator section in CakePHP. I've looked at plugins like BrowniePHP as well as others, but I am not entirely satisfied with using plugins. So I am trying to create my own which will encompass the things I need. I;ve looked at some tutorials, but cant find the right answer.
I am currently creating a vast application, which is about 10% done, but I now feel the need to have an admin section before moving on.
Basically I would like a section where I can add new articles, approve comments, deny user access, etc. This section should only be accessible by an administrator.
Also, this administrator section must be able to save to any other model.
I am still learning CakePHP and any detailed instruction would be appreciated.
to create an admin-section the first thing you have to do is to manually edit the core.php within /app/config and write the setting Routing.prefixes. This line should be around line 88 somewhere and you just have to uncomment it.
In case you can't find it, it should look like this:
Configure::write('Routing.prefixes', array('admin'));
So now you can write your admin-functions within your controllers like this:
function admin_edit($id = null) {
//your admin function
}
You don't need access to every model since your writing these function within your controllers like every other "normal" action.
You just have to connect a route to handle the admin-actions:
Router::connect('/admin/:controller/:action/*', array('admin' => true, 'prefix' => 'admin', 'controller' => 'pages'));
// 'admin' => true is a variable for you so you can check if it's an admin-action which is requested
// 'prefix' => 'admin' means that you can write function with this prefix like above
You can then access these actions via the url http://yourapp.com/admin/controller/action
If you now use the Auth-Component you can write methods for checking if a user is allowed to access these methods.
For further information please read these manual-entrys:
Prefix-Routing
Authentication (Auth-Component)

Cake PHP: How do I make one variable avaiable across all view and elements

I want to access
id of current logged user
name of current logged user
group_id of current logged user
group_name of current logged user
across the view files, to switch menus and tabs on and off according to group_id.
How can I achieve this with minimum sacrifice of performance?
Thanks
If you use the AuthComponent, it'll store the record of the currently logged-in user in the Session under the key Auth. You can access this anywhere through the session component or helper:
$this->Session->read('Auth.User.name')
Even if you're not using the AuthComponent, the Session is the best place to store information about the current user.
Otherwise and in general, the Configure class is usually a good place to store this kind of global information:
Configure::write('User', array('id' => $id, ...));
Configure::read('User.id');
What I always do is create a AppHelper and create a method for this.
Off course this is similar to deceze's answer but it reduces some code you need to write ;)
function user($key) {
$user = $this->Session->read('Auth.User');
if (isset($user[$key])) {
return $user[$key];
}
return false;
}
Then you can call the id of the user by $this->Html->user('id');
Perhaps you could set the variables you want in your AppController (extended by all sub controllers). You should then be able to access them from all views, though be careful to name them uniquely.
CakePHP book - App Controller

Resources