How to prevent Cakephp from flattening level 1 associations - php

Suppose I have the following models. Users, Posts, and Comments. Users has many Posts. Posts have many Comments. If I try the following in the Posts controller:
$post = $this->Post->find('first', array('contain' => array('Comment')));
I get an array that looks like this:
array(
'Post' => array(
'postfield1',
'postfield2',
'etc'
),
'Comment' => array(
[0] => array('firstcommentfield1', 'etcfields'),
[1] => array('secondcommentfield1', 'etcfields'),
)
)
But if I run the following from the Users controller,
$user = $this->User->find('first', array('contain' => array('Post' => array('Comment'))));
and then look at the first post for a user $user['Post'][0] I get the following
array(
'Post' => array(
'postfield1',
'postfield2',
'etc'
'Comment' => array(
[0] => array('firstcommentfield1', 'etcfields'),
[1] => array('secondcommentfield1', 'etcfields'),
)
),
)
This makes it difficult to write functions that act on posts without doing formatting manually first.
Anyone have a workaround?

Related

CakePHP findbyList

I'm getting data by CakePHP findbyList
Array
(
[92] => 5
[93] => 5
[98] => 5
)
CODE
$get = array(
'fields' => array('Upload.type'),
'joins' => array(
array(
'alias' => 't',
'table' => 'temp_files',
'type' => 'LEFT',
'conditions' => array('Upload.id = t.file_id')
),
array(
'alias' => 'o',
'table' => 'orders',
'type' => 'LEFT',
'conditions' => array('o.batch_id = t.batch_id')
)
),
'conditions' => array('OR'=>array('o.user_id '=>$user['id'],'t.user_id '=>$user['id'])),
'group' => 'Upload.id'
);
$files = $this->find('list',$get);
Is there any way in CakePHP for getting data like:
Array
(
[5] => Array
(
[0] => 92
[1] => 93
[2] => 98
)
)
Yes, there is two cakephp ways to retrive results like yours:
Find threaded:
https://book.cakephp.org/2.0/en/models/retrieving-your-data.html#find-threaded
and use Hash utility to extract, combine , etc your results:
https://book.cakephp.org/2.0/en/core-utility-libraries/hash.html
Without knowing how your database looks like I would probably use $this->find('all',$get) instead of 'list'
List will always put the ID of your model as a key, so it will never work as you need.
It would have been much better to understand this situation if db structure would have been mentioned.
Anyways what I would suggest is to to use $this->find('all',$get) instead of list and you can play around your final result array by managing the way you need your final output array.
EX:- if you get the list of the user name with userID then you are using CakePHP collection in CakePHP query.
CakePHP collection here :CakePHP Collection
$userList = $this->find()
->where(['status' => Configure::read('Status.active')])
->order(['first_name' => 'ASC'])
->combine('id','name')
->toArray();
in this result in userID is a key and name as a value display.

CakePHP getting data form an ID which is inside another table?

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

CakePHP (2.4) Deep Relationship

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.

Cakephp - contain (containable behavior) fetches too much

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.

CakePHP retrieve data, associate models, containable behavior

I can't seem to figure out how the containable behavior works, or how to retrieve the data from (and based) on associated models.
I need to put the following MySQL query in the cakephp find('all') function, I know i can use the query builder for the sql syntax but I want to use find function and want to figure out how it works.
SELECT events.id, events.name FROM events, user_roles
WHERE user_roles.event_id = events.id
AND user_roles.user_id = 1
The idea is simple, in the user_roles the user_id, role_id and event_id are stored. Now I want to retrieve all events for a certain user (here user_id = 1). I tried some variations on the following:
$data = $this->Event->find('all',array(
'contain' => array(
'UserRole' => array(
'fields' => array('id'),
'conditions' => array(
'UserRole.user_id' => $this->Auth->user('id')
)
)
)
));
But this keeps returning all events. What I want is an array like (or any other result with the same associated data):
array(
[0] => array(
[Event] => array(
'id' => 1,
'name' => 'Event Name'
),
[UserRole] => array(
'role_id' => 2,
'event_id'=> 1
)
[1] => array(
.... etc ...
)
The following models and associations are defined
class Event extends AppModel {
public $hasMany = array(
'UserRole' => array(
'className' => 'UserRole',
'foreignKey' => 'event_id',
'dependent' => false,
)
)
)
class UserRole extends AppModel {
public $belongsTo = array(
'Event' => array(
'className' => 'Event',
'foreignKey' => 'event_id'
),
)
)
So how does it work ;).
Edit:
I forgot to mention that I've set in AppModel.php:
public $recursive = -1;
Containable finds the associated records for the original query. What you are doing is a find('all') on your Event model with absolutely no conditions, so it will return all Events.
Then Containable will retrieve all the UserRole records that are for the given user_id and are also associated to the Event by the event_id
If you want to only retrieve events for the given user, you could do it one of two ways
Via the Event->UserRole association and chained models:
$data = $this->Event->UserRole->find('first', array(
'conditions' => array('UserRole.user_id' => $this->Auth->user('id'),
'contain' => array('Event')
));
$events = $data['Event'];
or
Via your current code and then using the Hash::extract method to get the Events that match. (Hash is a CakePHP core class)
$events = Hash::extract('/UserRole[user_id='.$this->Auth->user('id').']/..', $data);

Categories