CakePHP $this->Auth->user('id') returns incorrect user in controller - cakephp

I've got a really bizarre bug occurring in a CakePHP app I've been working on.
I've got the following method in my users_controller.php that reads the currently signed in user and sends the data to a view and sets the title_for_layout to the user's name:-
function account() {
$this->User->id = $this->Auth->user('id');
$this->set('User', $this->User->read());
$this->set('title_for_layout', 'Welcome '.$this->User->Contact->field('name'));
}
In my view I've got (among other things):-
<?php echo $User['Contact']['name'] ?>
Everything in the view looks fine. It is outputting the fields for the correct user (the one I am currently signed in as). However the title_for_layout is using a completely different user's details. So $this->User->Contact->field('name') is not the same as $User['Contact']['name']!
I can't spot what is going wrong here so hoping someone out there can point out my mistake.

Model read() simply does a find('first'), sets the result as the Model $data, and returns it. It doesn't set $ids of associated models.
So in your case, $this->User->id is set, but $this->User->Contact->id isn't.

Related

1146 Table 'Accounting.users' doesn't exist Cake\Database\

I'm working with cakephp3. I want to make login page. Name of table in Accounting database is 'users'.
This is my code:
<?php
namespace App\Controller;
use App\Controller\AppController;
class UsersController extends AppController {
public function login() {
if ($this->request->is('post')) {
$data = $this->request->data;
$cnt = $data->Users->find()
->count();
if ($cnt > 0) {
$this->redirect(['action' => 'index']);
} else {
$this->set('error', 'username or password is incorrct ');
}
}
}}
and this is Users.php
<?php
namespace App\Model\Table;
use Cake\ORM\Table;
class UsersTable extends Table {
}
after login in login page:
Error: Call to a member function find() on a non-object
In your opinion, what is the problem.
$data is not a Table object.
$data = $this->request->data;
$cnt = $data->Users->find()
This is pretty obvious.
I strongly recommend you to take some time and learn about debugging techniques and how to tackle this kind of problem and error messages. A developer should be able to resolve this kind of problem pretty quickly without external help. This is considered normal ever days work for a developer.
1) Read the whole error message 2) Search for it on Google and Stackoverflow, it is very unlikely nobody else ever got that message before. 3) Act according to whatever the cause of the error message is.
In the case of this error message debug what kind of object you're dealing with and figure out why it is not the object you expect it to be. Going trough the call stack helps. Use Xdebugs profiler for that, it's a great tool.
Also don't use variable names like $cnt I assume this is supposed to mean "account" which doesn't even fit into the context it is used. It's very bad named. Instead use proper variable names that are readable and fit into the context. It is a totally wrong assumption that keeping variable names short is any kind of time saver - it is clearly not. The next person working with this will need a dictionary or do a lot of guesswork on what these variables mean.
Instead of $cnt = $data->Users->find()->count(); use $cnt = $this->{$this->modelClass}->find('count');

Difference in accessing variables in views

I've two controllers one is "Upload" which deals with images uploads and other is "Page" whid deals with the creation of pages of CMS now if in my "Upload" controller I load both the models i.e 'image_m' which deals with image upload and "page_m" which deals with the pages creation I've highlighted the relevant code my problem is if I access the variables in the view
$this->data['images'] = $this->image_m->get(); sent by this I can access in foreach loop as "$images->image_title, $images->image_path" etc
But the variable sent by this line ***$this->data['get_with_images'] = $this->page_m->get_no_parents();*** as $get_with_images->page_name, $get_with_images->page_id etc produces given error
A PHP Error was encountered
Severity: Notice
Message: Trying to get property of non-object
Filename: upload/index.php
Line Number: 20
what is the difference between these two access levels one for $image & other for $get_with_images because I can only access its values as $get_with_images
class Upload extends Admin_Controller {
public function __construct() {
parent::__construct();
***$this->load->model('image_m');
$this->load->model('page_m');***
}
public function index($id = NULL) {
//var_dump($this->data['images'] = $this->image_m->get_with_images());
//$this->data['images'] = $this->image_m->get_with_images();
***$this->data['images'] = $this->image_m->get();***
$this->data['subview'] = 'admin/upload/index';
if ($id) {
$this->data['image'] = $this->image_m->get($id);
count($this->data['image']) || $this->data['errors'][] = 'Page Could not be found';
}
$id == NULL || $this->data['image'] = $this->image_m->get($id);
/*this calls the page_m model function to load all the pages from pages table*/
***$this->data['get_with_images'] = $this->page_m->get_no_parents();***
You are not posting all your code so its hard to tell but is it because you used $this-> in the controller, but you haven't done the same thing in the view?
In this case i would recommend not using $this-> because its not necessary. Also its much better to check for errors etc when you call the model so do something like
if ( ! $data['images'] = $this->image_m->get($id) ) {
// Failure -- show an appropriate view for not getting any images
// am showing $data in case you have other values that are getting passed
$this->load->view( 'sadview', $data ); }
else {
// Success -- show a view to display images
$this->load->view( 'awesomeview', $data ); }
so we are saying if nothing came back - the ! is a negative - then show the failure view. Else $data['images'] came back, and it will be passed to the view. note i have not had to use $this-> for anything and it won't be needed in the view.
Would also suggest using separate methods - have one method to show all images and a separate method like returnimage($id) to show an image based on a specific validated $id.
====== Edit
You can access as many models as you want and pass that data to the View. You have a different issue - the problem is that you are waiting until the View to find out - and then it makes it more difficult to figure out what is wrong.
Look at this page and make sure you understand the differences between query results
http://ellislab.com/codeigniter/user-guide/database/results.html
When you have problems like this the first thing to do is make a simple view, and echo out directly from the model method that is giving you problems. Its probably something very simple but you are having to look through so much code that its difficult to discover.
The next thing is that for every method you write, you need to ask yourself 'what if it doesn't return anything?' and then deal with those conditions as part of your code. Always validate any input coming in to your methods (even links) and always have fallbacks for any method connecting to a database.
On your view do a var_dump($get_with_images) The error being given is that you are trying to use/access $get_with_images as an object but it is not an object.
or better yet on your controller do a
echo '<pre>';
var_dump($this->page_m->get_no_parents());
exit();
maybe your model is not returning anything or is returning something but the data is not an object , maybe an array of object that you still need to loop through in some cases.

CakePHP User Lookup based on their ID

I thought this would be a relatively common thing to do, but I can't find examples anywhere, and the Cookbook's section on find() was not clear in the slightest on the subject. Maybe it's just something that's so simple Cake assumes you can just do it on your own.
All I'm looking to do here is retrieve a User's name (not the currently logged-in user…a different one) in Cake based on their ID passed to my by an array in the view.
Here's what I've got in the controller:
public function user_lookup($userID){
$this->User->flatten = false;
$this->User->recursive = 1;
$user = $this->User->find('first', array('conditions' => $userID));
//what now?
}
At this point, I don't even know if I'm on the right track…I assume this will return an array with the User's data, but how do I handle those results? How do I know what the array's gonna look like? Do I just return($cakeArray['first'].' '.$cakeArray['last'])? I dunno…
Help?
You need to use set to take the returned data, and make it accessible as a variable in your views. set is the main way you send data from your controller to your view.
public function user_lookup($userID){
$this->User->flatten = false;
$this->User->recursive = 1;
// added - minor improvement
if(!$this->User->exists($userID)) {
$this->redirect(array('action'=>'some_place'));
// the requested user doesn't exist; redirect or throw a 404 etc.
}
// we use $this->set() to store the data returned.
// It will be accessible in your view in a variable called `user`
// (or what ever you pass as the first parameter)
$this->set('user', $this->User->find('first', array('conditions' => $userID)));
}
// user_lookup.ctp - output the `user`
<?php echo $user['User']['username']; // eg ?>
<?php debug($user); // see what's acutally been returned ?>
more in the manual (this is fundamental cake stuff so might be worth having a good read)

cakePHP hasOne relationship not auto completing dropdown field

I'm trying to implement a hasone relationship between 2 models, but I can't have the 'add' form autocomplete with the possible options in the second model (the one that belongsTo the first one). This is my code:
- model 1: item.php
<?php
class Item extends AppModel{
var $name = 'Item';
var $primaryKey = 'id';
var $hasOne = 'CoverImage';
}
?>
- model 2: cover_image.php
<?php
class CoverImage extends AppModel{
var $name = 'CoverImage';
var $primaryKey = 'id';
var $belongsTo = array(
'Item' => array(
'className' => 'Item',
'foreignKey' => 'item_id'
));
}
?>
- add view of model 2: add.ctp
<?php echo $this->Form->create('CoverImage',array('url' => array('controller' => 'admins', 'action' => 'add')));?>
<fieldset>
<legend><?php __('Info'); ?></legend>
<?php
echo $this->Form->input('item_id');
echo $this->Form->input('description');
?>
</fieldset>
<?php echo $this->Form->end(__('Create', true));?>
For what I see in Cake's documentation, with this relationship, in the add view I should see a dropdown list in the item_id field to be able to select to which item does this CoverImage belongs to, but the dropdown is empty (and yes, I have some items in the items table already).
Maybe I'm missing something or I've done something wrong, but I can't figure it out. Thanks so much in advance for any clues!
EDIT
One think I've just realized is that if I do this:
echo $this->Form->input('item_id', array('type'=>'text'));
instead of this:
echo $this->Form->input('item_id');
I can add/edit the *item_id* field, I can see its value in the text box. However, if I leave the other one, I just see an empty dropbox and when I try to add/edit a CoverImage, it doesn't work, it just shows an empty white page, not even with errors...
Maybe this is a lead to something...
In order for that to work you have to create a list of possible options in the controller. That does not happen automatically.
public function add() {
$items = $this->CoverImage->Item->find('list');
$this->set(compact('items'));
}
The FormHelper only automatically infers that the field item_id should be populated by the options in the variable $items (plural, no _id).
Do be careful that Items that already haveOne CoverImage should not be part of that list. find('list', array('conditions' => array('CoverItem.id' => null))) will probably* take care of that, but you'll need to recheck just before saving as well, or you need to rethink your associations.
* Not sure off the top of my head whether that'll work for 'list' searches.
EXCELLENT QUESTION. You've run afoul of a disingenuous feature of Cake's associations:
Considering you defined the relationship as hasOne? Guessing at the trace but Cake probably even correctly inferred your preference for list functionality. You got your automagic list...
...of One.
$hasOne is pretty exclusive like that. It "uses up" those "has" relationships (it's makes the relationship a de facto Singleton - so Users only have 1 Profile <-> Profile only has 1 User). Consider - Database can have many configurations, but Dbo will only ever have one Connection at a time and Connection will only have one Dbo. Thus -> hasOne is for marrying two Models til die() do they part.
-- So it doesn't get used nearly as much as hasMany and belongsTo.
For your purpose, you probably want to change to a different association.
Adding an additional $this->Item->find doesn't really fix what's wrong (and I wouldn't recommend it, unless you're mostly done with both models/controllers, or you actively want things to start getting weird fast.)
Also, changing how you call the Form Helper methods - if you return a 'list' type fetch from a find, Cake will automatically produce an option list out of it. What's actually happening is, you're sneaking around your Model on a very thin margin of View functionality. That's why specifying the input type to "break the magic" tends to be discouraged (which, you totally can if you want. Just understand what's actually happening, or: see, weird, fast.)
But you might want to rethink how you've associated your models - wouldn't it also be correct to say, each Item belongsTo a CoverImage (same as each CoverImage belongs to an Item) -- because you have a form expressly permitting a CoverImage to select an Item, any Item, to be displayed with? You'll probably get better results.
HTH. :)

CakePHP user session not updating but database yes

I'm developing with cakePhP and I have the following problem:
When a user logs in with his name and password to the account system that I've created, he can save items (images) as favorites. This is saved in a text field into the database. What is saved is the image ID.
The saving process works perfectly, the user clicks on the images and they're added to that field (it actually saves all the IDs as a text array that I process later).
The problem comes when removing images. When the user does it (I'll post the code below), the images is removed correctly from the database (I go to PHP MyAdmin and I see it). This means that the array that holds the favorite images IDs is updated instantly. However, when I reload that array from the website, it hasn't been updated. It's like it's stored in the caché or something. Then, if the user logs out and logs in again, then he can see the correct one. The thing is that I have other things in my website that work in a similar way and they all get updated instantly, so I can't see why this doesn't.
This is the code that I use to remove the ID from the database:
function remove_favorite($pictureID) {
$this->User->id = $this->Auth->User('id'); //We get the ID of the current user
$favoritesArray = $this->User->deleteFavoritePicture($this->User->id, $pictureID); //This function retrieves the array (string) of pictures from the user's table, and deletes all the images with the ID passed as parameter, returning the updated array (string)
$fields = array('images_favorites' => $favoritesArray, 'modified' => true); //We indicate the field that we're going to update in the users table
//We save the new string that doesn't contain the deleted image anymore
if($this->User->save($fields, false, array('images_favorites'))) {
$this->Session->setFlash(__('The image has been removed from your favorites', true));
} else {
$this->Session->setFlash(__('Error removing image from favorites, please try again', true));
}
$this->redirect(array('action' => 'manage_favorites',$this->User->id));
}
This is how the deleteFavoritePicture function looks like:
function deleteFavoritePicture($userID, $pictureID) {
$userInfo = $this->find("id = $userID");
$favoritePicturesString = $userInfo['User']['images_favorites'];
$favoritePicturesArray = explode(",", $favoritePicturesString); //Array
$i = 0;
while ($i < count($favoritePicturesArray)) {
//We remove from the array the images which ID is the one we receive to delete
if ($favoritePicturesArray[$i] == $pictureID) unset($favoritePicturesArray[$i]);
$i++;
}
$favoritePicturesString = implode(",", $favoritePicturesArray); //String
return ($favoritePicturesString);
}
That's it. Does anyone now what can be going on? Thanks so much in advance for any clue!
EDIT
Ok, I think I found something that may give a clue of what's going on here:
This is the code for the manage_favorites action:
function manage_favorites($id) {
//$user = $this->User->find("id = $id");
$user = $this->Auth->user();
$this->set('user', $user);
}
That is the action that is called for the page when a user wants to modify his favorites. The same action is called once the user removes a favorite. Here's the thing:
If I use the $id parameter in the manage_favorites function and the $user = $this->User->find("id = $id"); line (the one quoted now), then the problem does not exist! This is how I used to have it. HOWEVER, I had to change it because it was a big security flaw, since the user id ($id) was a visible parameter who anyone could change, and then access other users accounts. What I did was changing the way I obtain the user array of favorite images, using the following line: $user = $this->Auth->user();. This is how I have it now (well, and also without the $id parameter in the function header), so the user information (including the favorites array) comes from the Auth component, instead directly from the database.
So, the problem is clear: when the user deletes a favorite, it's doing it on the array in the database. WHen I show the result of that operation, the array I'm retrieving is not the one in the DB, it's the one in the session. That's why it's not showing the changes.
How can I avoid this without using a non-secure method like the one I had before?
When you save, the array passed to the save method of the model should look like this:
[User] => array(
[field] => value,
[field2] => value2,
...
)
In your example, you clearly haven't added the [User] key.
Also, is your modified field actually the default Cake modified field? That is, the DATETIME field which changes to the current time when the row is updated?
Lastly, maybe you have debugging set to 2 in config.php. try changing this to 0 (as in production) and see if caching persists.
Hope some of the points I have mentioned above will solve your problem. Please let me know!
There could be two things wrong with this.
What does your deleteFavoritePicture method look like? There could be something being done wrong there.
You're passing false as the second parameter to the User::save method, which means that you don't want to validate. Unless there is a SQL error, then this will return true even if it doesn't validate properly, I believe. Try changing this false to true and see if your results differ.

Resources