Cakephp hasManyThrough relationship with user_id being used twice - php

I have created hasManyThrough table like below:
id user_id friend_id
I also have users table like below:
id username password
I want to have both user_id and friend_id belongsTo relation with user's id column. So I have written below two models:
Friend model
class Friend extends AppModel {
var $name = 'Friend';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Friend' => array(
'className' => 'User',
'foreignKey' => 'friend_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
User model
class User extends AppModel {
var $name = 'User';
var $displayField = 'username';
var $hasMany = array(
'User' => array(
'className' => 'Friend',
'foreignKey' => 'user_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
),
'Friend' => array(
'className' => 'Friend',
'foreignKey' => 'friend_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
I'm not sure why but when I try to view this via controller in scaffolding, it comes up with 500 server error?? Is there something wrong with the model config that is creating wrong SQL?
OK it seems like error is due to Zend Optimizer according to some forums. This is creating Segmentation Fault when baking!
I cannot turn off this in the third party hosted server so I might have to move to my local server and test so I can see Cake errors.
OK below is the error (it's very long so I'm not going to put all but below gives a bit of an idea):
Fatal error: Maximum function nesting level of '100' reached, aborting! in /usr/share/php/cake/libs/debugger.php on line 248 Call Stack: 0.0011 352048 1. {main}()

Most probably problem is occurring due to nesting of Friend Model construct and which is occurring due to use of same alias of Friend for User and Friend table has well. Here is what happening i guess:
Friend Model being initialized, which in its constructs during model binding initialize User Model.
Now when User Model is initialized, during its binding Process, It Tries to bind Friend Model with alias as User, but due to use of Singleton pattern in Models, Instead of Friend Model instance ClassRegistry returns instance of User, resulting User model binding to itself.
Similarly is the case when User is Initialized first.Change Alias of relations like Friend_2,
or something else, ie Dont repeat Alias in Model relations.

Look at my answer in CakePHP Twitter-clone: Can't get follow-system to work. It`s the same but with friends rather then followers.
You just make a user_users table with a user_id (User ID) field and child_user_id (Friend ID), you can use the user_users table again to make similar relations eg user HABTM followers, user HABTM followers
Sorry for the late response, I thought I posted the answer a day ago but clearly it didn't post.

Related

Foreign Key in 2 tables (CakePHP)

I have 3 tables: staffs, users and threads.
Both the staffs and users can create threads and a thread belongs to a user.
In thread, there is a user_id (FK - BelongsTo). The question is, How can I identify who created the thread. If I create a field called creator_id I still don't know If it's a staff or a user who created the thread.
I also tried a different approach by creating a field called creator (enum('staff', 'user)). The limitation with this method is, when I am using Thread->find(), I couldn't figure out a way to retrieve the information of the creator.
What options do I have to achieve what I want?
Do the Model Association as the following in Model for Thread. Create a field called creator_id and store the id of the user/staff who created the thread,
public $belongsTo = array(
'Client' => array(
'className' => 'Staff',
'foreignKey' => 'creator_id',
'conditions' => array('Thread.creator' => 'staff'),
'fields' => '',
'order' => ''
),
'User' => array(
'className' => 'User',
'foreignKey' => 'creator_id',
'conditions' => array('Thread.creator' => 'user'),
'fields' => '',
'order' => ''
)
);

belongsTo relationship & filtering in cakePHP

I'm a newbie to cakePHP and I've taken on creating a very small "status reporting" project, that would allow a user to report their current status on a project that they were assigned on.
I'm currently using the acl, auth, and session components to allow for multiple tier users to manage one another with an admin creating users, projects, and assigning them to one another. I have also fixed it so that when a user is logged in and goes to add a "status" that their login session automatically takes care of the "user_id" for the status:
$this->data['Status']['user_id'] = $this->Auth->user('id');
What I need help doing now is filter the options for the project that the user can pick to add a "status" to.
I currently have a setup with a table that holds the relationships between users and projects named *projects_users* with *id, project_id, user_id* as fields. But, after baking this setup, when the user is adding a "status" the drop down menu provides all projects rather than strictly the ones they are "assigned to."
I would like to filter the user's options to the projects that they are assigned to. Is there considerable more relationships I should be setting up or is this a pretty easy little function to write? Any help would be greatly appreciated and if I have left out information, I'd be glad to post anything else.
Here is the Status Model setup:
class Status extends AppModel {
var $name = 'Status';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Project' => array(
'className' => 'Project',
'foreignKey' => 'project_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
My User Model looks like this:
var $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasMany = array(
'Status' => array(
'className' => 'Status',
'foreignKey' => 'user_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
var $hasAndBelongsToMany = array(
'Project' => array(
'className' => 'Project',
'joinTable' => 'projects_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'project_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
In the database, You should have a column called project_id in the user table that is associated with the project table's id column.
Then, in your User model, you should create a hasMany relationship with the Project model.
class User extends AppModel {
var $name = 'User';
var $hasMany = 'Project';
}
Now, when you fetch a particular user, you will get all projects that the user is associated with.

CakePHP: Retrieving records based on field in related model

I'm trying to search based on a field in a related model. I can do so with the belongsTo model, but not the hasMany model. I'm sure this is something simple I'm overlooking but I can't see it. Could someone point me in the right direction (or even the cakebook page I need - searched, but didn't find anything that looked like this). Thanks
In groups controller: (this doesn't work)
$group = $this->Group->find('all', array('conditions' => array('Voucher.id' => '55')));
This does:
$group = $this->Group->find('all', array('conditions' => array('TourOperator.id' => '3')));
Background file snippets:
Group model:
var $belongsTo = array(
'TourOperator' => array(
'className' => 'TourOperator',
'foreignKey' => 'tour_operator_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasMany = array(
'Voucher' => array(
'className' => 'Voucher',
'foreignKey' => 'group_id',
'dependent' => true,
'conditions' => '',
'fields' => '',
'order' => 'Voucher.date, Voucher.meal_type_id'
)
);
Voucher model:
var $belongsTo = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'group_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Tour Operator model:
var $hasMany = array(
'Group' => array(
'className' => 'Group',
'foreignKey' => 'tour_operator_id',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
Update (as posted in comment below, but clearer to read here)
I've ended up using this.
$groups = $this->Group->Voucher->find('all', array('fields' => 'DISTINCT group_id', 'conditions' => array('Voucher.status' => 'pending')));
$group_ids = Set::extract($groups, '/Voucher/group_id');
$data = $this->Group->find('all', array('conditions' => array('Group.id' => $group_ids)));
I get a distinct list of group IDs matching my criteria, create an array of just the IDs and then use that to pull the groups so that the arrays are ordered as I expect them in my view.
Can you recast your query to $this->Voucher->find, and then use the associated Group data returned from that query? As you say, there's no problem finding groups by their TourOperator id, and the Groups/Voucher relationship looks like it's the inverse of that.
With CakePHP's Containable behaviour you can't use conditions on related data in hasMany and hasAndBelongsToMany associations. This is because it uses seperate queries to get those associations.
With the Linkable behaviour, however, you can use data from hasMany associations to filter records. The behaviour can be downloaded here: https://github.com/Terr/linkable
Linkable uses standard JOINs to, well, join related data. This means it doesn't pull in all the related data (ie, all the Vouchers related to your Group(s)), just like a normal SELECT. However, it can work together with Containable to do both.
You can find examples of how Linkable works in the README.
(disclaimer: I currently maintain Linkable, but that is not the reason I point to it)

CakePHP: COUNT function on a hasmany association

I'm trying to join a model through a hasmany association and then count the number of records. Basically I have users and projects. I want to display the number of associated projects on the user index action.
var $hasMany = array('Project' =>
array('className' => 'Project',
'conditions' => '',
'order' => '',
'limit' => '',
'foreignKey' => 'user_id',
'dependent' => true,
'exclusive' => false,
'finderQuery' => '',
'fields' => '',
'offset' => '',
'counterQuery' => '',
'counterCache' => true
)
);
So I have joined to Project model on user_id and have set the counterCache to true. The question is how do I access this in the user index view?
I've tried
<?php echo $user['Project']['project_count']; ?>
and
<?php echo $user['User']['project_count']; ?>
Can someone help me?
Jonesy
counterCache need to be in the belongsTo association. In your example you need to have project_count in the users table while in the project model's association belongsTo you need to activate the counterCache to true. Check the manual
If you've done all that and the project_count is still not updating, try turning off caching in app/config/core.php
I ran into that issue when I updated the database schema but Cake was caching the old schema and thus no counter field was found.

How do I query data in CakePHP using HABTM relationships?

I'm working on a CakePHP 1.2 application. I have a model "User" defined with a few HABTM relationships with other tables through a join table.
I'm now tasked with finding User information based on the data stored in one of these HABTM tables. Unfortunately, when the query executes, my condition is rejected with an error about a missing table. Upon inspection it seems that CakePHP is not including any of the HABTM tables in the select statement.
My User HABTM relationship is as follows:
var $hasAndBelongsToMany = array(
'Course' => array(
'className' => 'Course',
'joinTable' => 'course_members',
'foreignKey' => 'user_id',
'associationForeignKey' => 'course_id',
'conditions' => '',
'order' => '',
'limit' => '',
'uniq' => false,
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'School' => array(
'className' => 'School',
'joinTable' => 'school_members',
'foreignKey' => 'user_id',
'associationForeignKey' => 'school_id',
'conditions' => '',
'order' => '',
'limit' => '',
'uniq' => false,
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'Team' => array(
'className' => 'Team',
'joinTable' => 'team_members',
'foreignKey' => 'user_id',
'associationForeignKey' => 'team_id',
'conditions' => '',
'order' => '',
'limit' => '',
'uniq' => false,
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
The error is:
SQL Error: 1054: Unknown column
'School.name' in 'where clause'
And finally, the query it is trying to execute
SELECT
`User`.`id`, `User`.`username`, `User`.`password`, `User`.`firstName`, `User`.`lastName`, `User`.`email
`, `User`.`phone`, `User`.`streetAddress`, `User`.`city`, `User`.`province`, `User`.`country`, `User
`.`postal`, `User`.`userlevel`, `User`.`modified`, `User`.`created`, `User`.`deleted`, `User`.`deleted_date
` FROM `users` AS `User` WHERE `User`.`id` = 6 AND `School`.`name` LIKE '%Test%' LIMIT 1
Turn your debug level up to 2 and look at the SQL output. Find the query that your code is generating and you'll notice there are several. The ORM layer in CakePHP doesn't join HABTM related tables in the first query. It gets the results from the first select, then separately fetches the HABTM data for each item. Because the join table is not in the first query, your condition, which is intended for use on a joined table, results in the error you are seeing.
The cook book has a section on HABTM associations and fetching data based on conditions in the HABTM table that will fit your requirements.
From the Cookbook: http://book.cakephp.org/view/83/hasAndBelongsToMany-HABTM
One option is to search the Tag model (instead of Recipe), which will also give us all of the associated Recipes.
$this->Recipe->Tag->find('all', array(
'conditions' => array('Tag.name' => 'Dessert')));
We could also use the join table model (which CakePHP provides for us), to search for a given ID.
$this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
$this->Recipe->find('all', array(
'fields' => array('Recipe.*'),
'conditions' => array('RecipesTag.tag_id' => 124) // id of Dessert
));
It's also possible to create an exotic association for the purpose of creating as many joins as necessary to allow filtering, for example:
$this->Recipe->bindModel(array('hasOne' => array('RecipesTag',
'FilterTag' => array(
'className' => 'Tag',
'foreignKey' => false,
'conditions' => array('FilterTag.id = RecipesTag.tag_id')
))));
$this->Recipe->find('all', array(
'fields' => array('Recipe.*'),
'conditions' => array('FilterTag.name' => 'Dessert')
));
Your table should be called "schools_users" and not "school_members" because it's many-to-many, thus using the plural form in the table name on both sides is appropiate.
You also set the Model ClassName "School" as Alias for the HABTM. You should change that to something more generic like "Schools" as in "User is in and has many SchoolS" to avoid conflicts.
And another hint: Try to find the user "via" the School Model rather than the User Model.
$this->User->Schools->find()
Hope this helps.
FWIW, your join tables do appear to be "oddly named" insofar as they don't follow the convention described here:
http://book.cakephp.org/view/83/hasAndBelongsToMany-HABTM
In any case, good luck, I remember HABTM being a butt-pain in CakePHP.

Categories