I am working on cake php and I have an association with users and user_details table, When I sign up then its working fine. Its post data on both the tables but when I redirect to profile page then it throws an error of userDetail array. When I printed that info array I got only one array instead of two arrays one is User and another one is UserDetail. Its working fine on my local machine but not working on server, But when I used recursive=>1 than its works. Please let me know what wrong here.
Query
$userInfo = $this->User->find('first',array('conditions'=>array('User.id'=>$userId)));
when I pass recursive than it works
$userInfo = $this->User->find('first',array('recursive'=>1,'conditions'=>array('User.id'=>$userId)));
Thanks
Shiv
Please use Containable behavior. It's more intuitive and clean then recursive variable. You can do this adding in your AppModel (or specific models if you don't want this behavior in all of them):
public $actsAs = array('Containable');
Then, in your find:
$userInfo = $this->User->find('first',array(
'conditions'=>array(
'User.id'=> $userId
),
'contain' => array('UserDetail')
));
If you have declared your association, you will retrieve your User along any UserDetail for that User
If you always want UserDetail to be fetched when you do a find on User, add the $recursive property to your User model:
public $recursive = 1;
However, the above is not recommended as usually, $recursive should be set to -1 in your AppModel. This is because Model::find() should normally only fetch data from a single model and not ALL related models. E.g. if your User model is related to say UserDetail and UserRole model, data from all of them will be fetched if $recursive is not equal to -1. This slows down your app.
When you do require related model data like in your question, use the containable behavior like this:
$userInfo = $this->User->find('first', array(
'conditions' => array('User.id'=>$userId),
'contain' => array('UserDetail')
));
Related
I am tring to do the following:
My 'Company' Models are being stored in a 'default' datasource. In to
model I set the var $useDbConfig = 'default';
My 'User' Models are being stored in another datasource called
'users_db'. In this model I set the var $useDbConfig = 'users_db';
There is a 'belongsTo' relationship between 'Company' and 'User' models.
I want to do a recursive find query that brings back all users for each company in one go. I have the feeling that I cannot do this with Cake as it currently is.
When I try the following:
$res2 = $this->Company->find('all', array(
'conditions' => array('Company.id' => 1),
'recursive'=>2
));
I get a message that it cannot find the users table(its looking in the default source):
Error: Table user_groups for model User was not found in datasource default.
Is there any way I can do this?
Many thanks for any help you can provide.....
kSeudo.
Why not replace the your original statement by this:
$this->loadModel('User');
$res2 = $this->User->find('all',
array( 'conditions' => array( 'User.company_id' => 1 ) ));
This will produce the results you want.
Otherwise, if you wanted something tidier, you could add a fetch_users() function to your Company class that implemented a similar logic.
Ok, problem solved. Once I set the datasource in the model correctly everything seems to work fine.
Thanks guys
Is it possible to get the Model that is trying to save data to a related model in a saveAll transaction. If forexample, User hasMany Goal, and I make a call;
$this->User->saveAll($data);
Can I get the Model User in the Goal Model class using some predefined attritbute, method or mechanism?
Thanks for your help in advance,
Roland.
Edit:
Assume a hypothetical situation where a user has pictures, with a Picture model and Posts( blogs) with a corresponding model. These both can be commented upon. So rather than creating a separate comment model for each of these models, I create one central Comment model. The association might look like this;
//In the Picture model
var $hasMany = array(
`` 'Comment' => array(
'className' => 'Comment',
'foreignKey' => 'picture_id',
'conditions' => array('Comment.objectType' => 'picture')
)`
);
The association for the Post model will be similar, the only difference being;
'conditions' => array('Comment.objectType' => 'post')
With this structure, if I were to query any of these Models, their corresponding comments will be retrieved from the DB table using the objectType field.
If I were to do something like;
$this->Picture->saveAll($data);
, or
$this->Post->saveAll($data);
with the $data array well structured and containing a Comments part to be saved, at some point in time during the saving transaction, data will be saved to the comments table through the Comment model.
What I want is to be able to know that it is the Picture model trying to save comment in the Comment model's beforeSave method, i.e;
//In the Comment mode
beforeSave() {
//Post model is trying to save a comment here
}
With CakePHP, if you associate your models using the hasMany, while you are in the controller you can access that model.
In UserModel
var $hasMany = array('Goal');
In GoalModel
function myCrazyFunc() {
//crazy stuff happens here
}
In UsersController
$this->Goal->myCrazyFunc();
//more stuff
$this->User->saveAll($data);
In my controller I have
var $uses = array('User','Customer');
then I am using read to call the users
$loggedOutCustomer = $this->User->read(null, $this->Auth->user('id'));
this gives me users and cutsomers that I can use but I want to do a sort on customers name. How can i do that in cake?
You can set the default order in your User model.
If Customer belongsTo User, User hasMany Customer.
In your User model, under the hasMany variable, find the Customer record and add Customer.name to the order array key.
Also, please remove the cakephp version tag that you are not using.
You can also add your sorting options when you're using the find() method of your model.
This can also be defined in the pagination array
Another option would be to use the Containable behavior
Give this a try
$loggedOutCustomer = $this->User->find('first', array('conditions' => array('User.id' => $this->Auth->user('id')), 'order' => 'Customer.name ASC'));
In one of my models, I have a "LONGTEXT" field that has a big dump of a bunch of stuff that I never care to read, and it slows things down, since I'm moving much more data between the DB and the web app.
Is there a way to specify in the model that I want CakePHP to simply ignore that field, and never read it or do anything with it?
I really want to avoid the hassle of creating a separate table and a separate model, only for this field.
Thanks!
Daniel
As #SpawnCxy said, you'll need to use the 'fields' => array(...) option in a find to limit the data you want to retrieve. If you don't want to do this every time you write a find, you can add something like this to your models beforeFind() callback, which will automatically populate the fields options with all fields except the longtext field:
function beforeFind($query) {
if (!isset($query['fields'])) {
foreach ($this->_schema as $field => $foo) {
if ($field == 'longtextfield') {
continue;
}
$query['fields'][] = $this->alias . '.' . $field;
}
}
return $query;
}
Regarding comment:
That's true… The easiest way in this case is probably to unset the field from the schema.
unset($this->Model->_schema['longtextfield']);
I haven't tested it, but this should prevent the field from being included in the query. If you want to make this switchable for each query, you could move it to another variable like $Model->_schemaInactiveFields and move it back when needed. You could even make a Behavior for this.
The parameter fields may help you.It doesn't ignore fields but specifies fields you want:
array(
'conditions' => array('Model.field' => $thisValue), //array of conditions
'fields' => array('Model.field1', 'Model.field2'), //list columns you want
)
You can get more information of retrieving data in the cookbook .
Another idea:
Define your special query in the model:
function myfind($type,$params)
{
$params['fields'] = array('Model.field1','Model.field2',...);
return $this->find($type,$params);
}
Then use it in the controller
$this->Model->myfind($type,$params);
Also try containable behaviour will strip out all unwanted fields and works on model associations as well.
Containable
class Post extends AppModel { <br>
var $actsAs = array('Containable'); <br>
}
where Post is your model?
You can add a beforeFilter function in your Table and add a select to the query
Excample:
public function beforeFind(Event $event, Query $query){
$protected = $this->newEntity()->hidden;
$tableSchema = $event->subject()->schema();
$fields = $tableSchema->columns();
foreach($fields as $key => $name){
if(in_array($name,$protected)){
unset($fields[$key]);
}
}
$query->select($fields);
return $event;
}
In this excample I took the hidden fields from the ModelClass to exclude from result.
Took it from my answer to a simular question here : Hidden fields are still listed from database in cakephp 3
I'm having trouble with my HABTM relationship in CakePHP.
I have two models like so: Department HABTM Location. One large company has many buildings, and each building provides a limited number of services. Each building also has its own webpage, so in addition to the HABTM relationship itself, each HABTM row also has a url field where the user can visit to find additional information about the service they're interested and how it operates at the building they're interested in.
I've set up the models like so:
<?php
class Location extends AppModel {
var $name = 'Location';
var $hasAndBelongsToMany = array(
'Department' => array(
'with' => 'DepartmentsLocation',
'unique' => true
)
);
}
?>
<?php
class Department extends AppModel {
var $name = 'Department';
var $hasAndBelongsToMany = array(
'Location' => array(
'with' => 'DepartmentsLocation',
'unique' => true
)
);
}
?>
<?php
class DepartmentsLocation extends AppModel {
var $name = 'DepartmentsLocation';
var $belongsTo = array(
'Department',
'Location'
);
// I'm pretty sure this method is unrelated. It's not being called when this error
// occurs. Its purpose is to prevent having two HABTM rows with the same location
// and department.
function beforeSave() {
// kill any existing rows with same associations
$this->log(__FILE__ . ": killing existing HABTM rows", LOG_DEBUG);
$result = $this->find('all', array("conditions" =>
array("location_id" => $this->data['DepartmentsLocation']['location_id'],
"department_id" => $this->data['DepartmentsLocation']['department_id'])));
foreach($result as $row) {
$this->delete($row['DepartmentsLocation']['id']);
}
return true;
}
}
?>
The controllers are completely uninteresting.
The problem:
If I edit the name of a Location, all of the DepartmentsLocations that were linked to that Location are re-created with empty URLs. Since the models specify that unique is true, this also causes all of the newer rows to overwrite the older rows, which essentially destroys all of the URLs.
I would like to know two things:
Can I stop this? If so, how?
And, on a less technical and more whiney note: Why does this even happen? It seems bizarre to me that editing a field through Cake should cause so much trouble, when I can easily go through phpMyAdmin, edit the Location name there, and get exactly the result I would expect. Why does CakePHP touch the HABTM data when I'm just editing a field on a row? It's not even a foreign key!
From the CookBook the 1st problem is:
By default when saving a
HasAndBelongsToMany relationship, Cake
will delete all rows on the join table
before saving new ones.
I am not quite sure why Cake is trying to save the HABTM data even though you don't have a foreign key in your data, but there is an easy solution for that. Simply destroy the association for the save call:
$this->Location->unbindModel(
array('hasAndBelongsToMany' => array('Department'))
);
I'm thinking of one reason why this might be happening. When you retrieve Location, you also retrieve locations_departments data. And when you do a save($this->data) it looks for models in the array and saves them.
A way to solve this is setting the recursive attribute (of a model) to -1 or 0 (try, I'm not sure, just print out the data to see what comes out). You can set it in the model: var $recursive = -1; or in the controller method (action): $this->ModelName->recursive = -1;
More about recursive: http://book.cakephp.org/view/439/recursive
It's really similar to what harpax suggested, just if you don't need that data, tell it to Cake, so that it won't fetch it.
Trouble is that when saving your Location, you gave the save method an array containing all the DepartmentsLocations too. Thus CakePHP destroys everything and try to recreate it.
This is a common mistake with cake since it will often pull far too many results for you.
Be sure to pass only the data that needs to be saved, or better to fetch only the datas you need.