I have two Models, namely Product and ProductSpecification which have the following relations in place:
(Product Model)
public $hasMany = array(
'ProductSpecification' => array(
'className' => 'ProductSpecification',
'foreignKey' => 'product_id',
'dependent' => true
)
);
and
(ProductSpecification Model)
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id'
)
);
Using the CakePHP Form helper I post ProductSpecification data and then I use the saveAll method (or saveAssociated, I've tried both) to save the data. debug($this->request->data) gives me the following output after POSTing:
array(
'Product' => array(
'id' => '2'
),
'ProductSpecification' => array(
'title' => 'test',
'step' => '1',
'position' => '1'
)
)
This is great, right..? Now, the line after the debug I use the following code to save (I've also tried saveAssociated):
if($this->Product->saveAll($this->request->data))
For some odd reason this saves three(!) empty rows in my ProductSpecification table, with only the product_id field (and id) set; the fields title, step and position are empty. Exactly the same behavior happens when I run saveAssociated. What am I doing wrong?
I'm running CakePHP 2.x.
Your save data should look more like this:-
array(
'Product' => array(
'id' => '2'
),
'ProductSpecification' => array(
array(
'title' => 'test',
'step' => '1',
'position' => '1'
)
)
);
The values for ProductSpecification need to be passed as a numerically indexed array for the hasMany relationship.
Also, make sure you use saveAssociated() rather than saveAll() as you are passing associated data so there is no need to use the wrapper method (which should be avoided wherever possible).
Related
I cant get data from 2 tables with conditions on each.
There is no example in the docs.
I just need rows from students table where a field is flagged as inactive and corresponding rows from Guardian table the email field is not null.
Guardian has many Students.
I get results but I get null values for Guardian email.
I have tried many combinations with ID, model name etc but I am just not getting it.
$guardianFound = $this->Student->find('all', array(
'contain' => array( 'Guardian', array(
'conditions' => array('guardian_email !=' => null),
'fields' => array('id,guardian_email','guardian_first_name,guardian_last_name')
)),
'conditions' => array('student_inactive' => 1),
'fields'=> array('student_inactive' ),
'recursive'=> -1
));
Result:
(int) 0 => array(
'Student' => array(
'student_inactive' => true
),
'Guardian' => array(
'guardian_email' => '',
'guardian_first_name' => 'Tulay',
'guardian_last_name' => 'Karadavut',
'id' => '100'
)
)
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
It looks like your query is working as expected, except that you probably want to exclude where guardian_email is null OR empty.
To specify multiple values for a field in CakePHP, you can usually wrap it in another array, but for your case, since you're negating NULL and empty, it's a little complicated because of how CakePHP handles db field types. See this question for more info: Cake PHP complex find 'OR' is not working properly with null and empty string. So to simplify it you can set your Guardian conditions like so:
'conditions' => array(
'guardian_email IS NOT NULL',
'guardian_email != ""'
)
You don't need the recursive key here since you're specifying what to contain using containable. So the final find call would be:
$guardianFound = $this->Student->find('all', array(
'contain' => array(
'Guardian' => array(
'conditions' => array(
'guardian_email IS NOT NULL',
'guardian_email != ""'
),
'fields' => array(
'id', 'guardian_email', 'guardian_first_name', 'guardian_last_name'
)
)
),
'conditions' => array('student_inactive' => 1),
'fields' => array('student_inactive')
));
Note that you're finding all inactive students, then only performing the guardian conditions on the guardians associated with that student. If no Guardian record matches the conditions the field would still be returned but with all the values as NULL.
I'm assuming here that because Student belongsTo Guardian, and there would only be one Guardian per Student, you actually want to only return student records where the guardian email is provided. If that's the case, you just need to move the guardian conditions into the main conditions.
$guardianFound = $this->Student->find('all', array(
'contain' => array(
'Guardian' => array(
'fields' => array(
'id', 'guardian_email', 'guardian_first_name', 'guardian_last_name'
)
)
),
'conditions' => array(
'student_inactive' => 1,
'Guardian.guardian_email IS NOT NULL',
'Guardian.guardian_email != ""'
),
'fields' => array('student_inactive')
));
Right, so my data returns in the following way,
(int) 0 => array(
'MODEL-XX' => array(
//DATA HERE
'xxs_id' => '11',
'aas_id' => '44',
'vvs_id' => '2'
),
'xxs' => array(
'id' => '11',
'customername' => 'XX name here'
),
'aas' => array(
'id' => '44',
'clientname' => 'aa name here',
'xxs_id' => '11'
),
'vvs' => array(
'id' => '2',
'start' => '1405296000',
'end' => '1405814400',
'users_id' => '1'
)
This works fine, but I want to know how to link my users table to this model. So the details of each user for my VV model would become apart of the data. My MODEL-XX does not have any links with my users table so the place I need to call in the users details are held with my VV model.
I have been looking into this but have not been able to find a simple easy method for doing this?
I was thinking that this would be doable with my model, so I opened my my XX model and added the following within my '$belongsTo' section,
'Users' => array(
'className' => 'Users',
'foreignKey' => 'vvs.users_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
So is there a easy method for linking data like this?
Please give me time, if I have not explained myself right or not enough data, please tell me and let me fix or explain better.
Thanks,
Either set your recusive higher:
$this->MODEL-XX->recursive = 1; //or 2
Or and this should be your prefered way to go, start using the containable behaviour:
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
In your appModel:
public $actsAs = array('Containable');
Then try this find:
$this->MODEL-XX->recursive = -1;
$data = $this-MODEL-XX>find(
'all', array(
'conditions' => $conditions,
'contain' => array('xxs', 'aas', 'vvs', 'user')
)
);
I might be 'vvs.user' but I forgot what is used for deeper models
I have a database:
Companies | Users | Files
- ID - ID - ID
- company_id - user_id
So for my models:
Company hasmany User
User belongsto Company & hasmany File
File belongsto User
How can I list al the files belonging to a company? Without the user and company data? So a single array listing files.
I could fetch all the user IDs followed by a query in File searching with these IDs. But I was wondering, is there a best practice cakePHP way?
I'm fairly new to cake.
Thanks
As far as I know it's not possible to get company files without any user or company data, but you can limit the data by using the Containable behaviour. For example, if you want to get files that belong to a company in the company controller you could use this:
$users = $this->Company->User->find('all', array(
'conditions' => array('User.company_id' => $id),
'fields' => array('id', 'company_id'),
'contain' => array('File'),
));
The result ($users) would look something like this:
array(
0 => array(
'User' => array(
'company_id' => '75',
'id' => '51'
),
'File' => array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
)
...
)
)
1 => array(
'User' => array(
'company_id' => '75',
'id' => '65'
),
'File' => array(
0 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
)
)
You can then get an array of files using the Hash utility.
$files = Hash::extract($users, '{n}.File.{n}');
Which will give you something like this:
array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
),
3 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
Don't forget to enable the behaviour. I suggest you do it in your app model by adding the following line:
public $actsAs = array('Containable');
CakePHP will not help you on this and even if want to write mysql query you cant do this by using files table only until and unless you will add company_id in files table.
I'm new to CakePHP. Now I'm struggeling with a problem I can't find an answer for on the internet for the past hour. I'm trying to save the following array to my database:
array(
'Product' => array(
'product_id' => '77adfe7754esda71r1999431',
'supplier_id' => 'zalando_it',
'product_name' => 'Sweater',
'price_new' => '68.97',
'affiliate_url' => 'http://example.com'
),
'Image' => array(
'image' => '99988_DP.jpg'
)
)
Now this is an example of my ERD: https://www.dropbox.com/s/1zvfqfxes53gibb/example.png
I connected the tables correctly in the Models (by using belongsTo and hasMany variables), but still I think I'm doing something wrong.... Cause when I use the function $this->Supplier->saveAssociated($data) in the Model "Feed" it doesn't save anything.
And when I use $this->Supplier->Product->saveAssociated($data) it saves the Product and it creates records in the Image table with the product_id but leaves the "image" field empty.
Only if I use $this->Supplier->Product->Image->saveAssociated($data) it saves everything correctly. But isn't that wrong? Cause to my opinion it doesn't seem right to go through each model ($this->Supplier->Product->Image...) to save all tables.... Or am I wrong?
Assuming Product hasMany Image, your data needs to be like this (notice "Image" is numbered, so there can be more than one):
array(
'Product' => array(
'product_id' => '77adfe7754esda71r1999431',
'supplier_id' => 'zalando_it',
'product_name' => 'Sweater',
'price_new' => '68.97',
'affiliate_url' => 'http://example.com'
),
'Image' => array(
0 => array(
'image' => '99988_DP.jpg'
)
)
)
As I understood from the cakephp-documentation, one of the advantages of the 'containable'-Behavior is being able to fetch fewer data if you need fewer data...
But that doesn't seem to work in my case of a connection between users and usergroups.
My associations look like:
Group
hasMany: Membership
User
hasMany: Membership
Membership
belongsTo: User, Group
(I'm not using HABTM, instead use the Model 'Membership' in between to join users and groups).
All Models implement the 'Containable'-Behavior.
Now I want to get all the members of a group with a certain id, only their ids and mail-addresses. My query is built like that:
$members = $this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
),
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
));
But the resulting array looks like:
array(
(int) 0 => array(
'Membership' => array(
'id' => '1',
'group_id' => '1',
'user_id' => '1',
'role' => 'member'
),
'Group' => array(
'id' => '1',
'name' => 'Foo Group'
),
'User' => array(
'password' => '*****',
'id' => '1',
'name' => 'Dr. Foo'
'mail' => 'foo#bar.baz',
'role' => 'admin'
)
)
)
So there are definietely more fields fetched than I wanted to... (it's the same thing btw wenn I set the 'contain'-key to:
'contain' => array(
'User.fullName', 'User.id'
)
Am I using the containable-behavior wrong?
Your models don't seem to be acting containabl-y at all. Have you set your models to act as containable?
class Post extends AppModel {
public $actsAs = array('Containable');
}
If so, maybe the problem is with the recursion (to avoid getting the Group array with the query). Containable behavior should handle the recursion level on its own, but try setting it on the AppModel just to be sure
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
Your first attempt
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
looks good in terms of syntax, so it probably the actAs thing.
Also, for debugging also, try
$this->Membership->contain('User');
$this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
));
and see if you get the expected results that way.