I am working on a followers / following system with CakePHP 2.
I have setup my database with a users table, and a user_users table. The users table is the main table containing every user on the system, whilst the user_users table contains the records of followers.
I then have a UsersController, User model and Follower model.
I can successful output a button to either say Follow or Following dependent on whether the currently logged in user is following the user of which the profile they are viewing belongs to, however what I am unable to understand how to do, is create new following relationships in the table. In other words, I do not know how to create records in the user_users table.
I am not sure where the logic for this should go, and thus what my "Follow" button should point to.
This is probably a very simple question, but I am totally stumped. I have tried adding a "follow" action to the UsersController but I cannot get that to work.
Any help much appreciated,
Duncan
HATBM isn't a good fit in this situation. From the cookbook:
HABTM data is treated like a complete set, each time a new data
association is added the complete set of associated rows in database
is dropped and created again so you will always need to pass the whole
data set for saving. For an alternative to using HABTM see hasMany
through (The Join Model)
For this reason, HABTM is mainly good for pretty 'dumb' relationships. I've used it in cases such as where a User has to select many Interests - and they just get a list of checkboxes, where they can click multiple Interests, and save them all in one hit.
In your case, it'll be easier to have a separate table with it's own model. I'd call it Relationships or something similar. It would have an id, followed_by_id, following_id, and any other fields you may need.
I've dug up some code from an old cake 1.3 app, but it should help you out. Your Relationships model would look something like this:
<?php
class Relationship extends AppModel {
var $name = 'Relationship';
var $belongsTo = array(
'FollowedBy' => array(
'className' => 'User',
'foreignKey' => 'followed_by_id'
),
'Following' => array(
'className' => 'User',
'foreignKey' => 'following_id'
)
);
}
?>
Your User's model would have to have relationships like this:
var $hasMany = array(
'Followers' => array(
'className' => 'Relationship',
'foreignKey' => 'following_id',
'dependent'=> true
),
'FollowingUsers' => array(
'className' => 'Relationship',
'foreignKey' => 'followed_by_id',
'dependent'=> true
),
);
Then in your relationships controller, you'd have methods something like this:
function add($following_id = null) {
$this->Relationship->create();
$this->Relationship->set('followed_by_id',$this->Auth->User('id'));
$this->Relationship->set('following_id',$following_id);
$this->Relationship->save();
$this->redirect($this->referer());
}
function delete($id = null) {
$this->Relationship->delete($id);
$this->redirect($this->referer());
}
Note that in that code, I'm modifying the database with a GET request - which I really shouldn't be doing (it's old code, from years ago). You'll want to enforce a POST request for both the add and delete methods, since they're modifying the database.
But still, that code should set you on the right track.
Related
I have two different tables as Post and Post_Info.
I want to get all the details using post_id in cake php from both the tables. The Post_Info table having post_id as foreign key and other related details.
I am new to Cakephp and I don't know how to get data from multiple tables in Cakephp.
I can get data from Post table using following query in Cakephp.
Following code is getting data from Post table.
$userdata = $this->Post->find('first', array(
'conditions' => array('Post.post_id' => $post_id)
));
To achieve this you have to do two things.
Make a relation of belongsTo in PostInfoTable.php in your model folder. you can read more about it here.
Once you have made the relationship all you have to do is use "contain" in your query along with the Model name. Your query will be modified as below
$userdata = $this->Post->find('first', array(
'conditions' => array('Post.post_id' => $post_id),
'contain' => ['PostInfo']
));
And you are good to go.
Moreover, I will recommend you to go through the documentation at least once it will be a great help to you.
I have a model User and a model Role in a CakePHP application. The association between the two models is the following:
User $belongsTo Role
Role $hasMany User
I want to make a query on the User model to find all users with a specific role (let's say the role Supervisor). I did my query like this:
$supervisors = $this->User->find('all', array(
'contain' => array(
'Role' => array(
'conditions' => array(
'Role.name' => 'Supervisor'
)
)
)
));
But the above query returns me all the users in my users table. It does not return only the users with role Supervisor. I know that if I do two queries, one on the Role model to find the id of the role type 'Supervisor' and then do another query on the User model and pass the id of the supervisor role record in the conditions on my User model like this:
$supervisor_role_id = $this->Role->field('id', array('Role.name' => 'Supervisor'));
$supervisors = $this->User->find('all', array(
'conditions' => array(
'User.role_id' => $supervisor_role_id
)
));
The above queries will give me the desired result. But I don't wanna do 2 queries to do this. Why doesn't the first approach work. Any idea please?
Thank you
The reason your attempt didn't work
CakePHP's Containble Behavior creates separate queries for each model. So - what you did was basically described like this: "Find all Users. Also find any Roles with the name of 'Supervisor". As you can see, there is no condition that crosses between the two.
So, you can do one of the following:
1) [easy way] Query the other way around
Query from the Role model and contain it's user(s). This pulls the role you want (based on your provided conditions) then contains any/all of it's users.
Note - if you've already loaded the 'User' model (or it's been loaded by default because you're in the UsersController), you can run your find like this: $this->User->Role->find(..... - so you don't have to load the Role model separately.
2) Use JOINs (see CakePHP Book on Joining Tables)
This allows you to limit the result of a parent model based on it's associated data.
I will do my best to explain my situation in the hopes it will be clear enough for someone to help me out:
I currently have a 'Requests' model. This consists of a web form and a controller that saves one or more records based on the users selection on the form (for example, if they make a bunch of selections in a multi-select box, multiple records are generated and saved).
This works fine, but I want all of these 'Requests' to be grouped together for easy reference, since they are related. I created a new model called 'Groups.' In my 'Requests' model, I put:
var $belongsTo = array('Group' => array('className' => 'Group', 'foreignKey' => 'group_id'));
and created the group_id column in the 'requests' database table.
In the 'Groups' controller, I put
var $hasMany = array('Requests' => array('className' => 'Request'));
Now, in my 'Requests' controller, where I do all of the processing of the form input and create the records, I tried creating a new 'Group' with
$this->Request->Group->create();
This works, and the 'Group' database record is made (along with the 'Requests' as appropriate), but the 'group_id' field in the 'Requests' record is not set properly. When I try to do it manually, like setting it to $this->Request->data['Group']['id'] or $this->Request->Group->id, CakePHP tells me this value is NULL. So I have no way to know if these associations are even working at all.
Clearly I'm doing something wrong - do I have to move all of the creating and saving logic from my 'Requests' controller to my 'Groups' controller? Seems like this would be a pain. Is there a problem with creating a "Parent" and a bunch of "Children" at once from the controller of the "Children?"
For the record - I'm using CakePHP 1.1, and unfortunately cannot upgrade.
try setting the 'group_id' field in the 'Requests' record manually to $this->Request->Group->getLastInsertID();
that should give you the ID of the last group created.
I've been searching around for a definitive answer to this issue but have been unable to resolve it. I've recently been working with (and learning) CakePHP and have run across an obstacle. To simplify my database, let's say we simply have two tables:
persons
relationships (or persons_persons)
Persons has a many-to-many relationship with itself - and indeed, two people can have more than one relationship with each other:
persons
-------
*person_id*
name
relationships
-------------
*relationship_id*
person_1_id
person_2_id
start_date
end_date
Now, if I want to say Person1 (id=1) is married to Person2, I would have one entry in the relationships table. person_1_id would be 1, and person_2_id would be two.
I can create this relationship in CakePHP, and show the lists of relationships (and persons) per Person record. However, it's a unidirectional relationship: for Person 1, the query will only pull Relationship objects where person_1_id matches. If I want to query the relationships for Person 2, I'd have to have a second identical row, with the person_1_id and person_2_id swapped.
Here's the models:
class Member extends AppModel {
public $hasMany = array(
'Relationships' => array(
'className' => 'Relationship',
'foreignKey' => 'person_1_id'
)
);
}
class Relationship extends AppModel {
public $belongsTo = array(
'Person1' => array(
'className' => 'Person',
'foreignKey' => 'person_1_id'
),
'Person2' => array(
'className' => 'Person',
'foreignKey' => 'person_2_id'
)
);
}
Any suggestions? It doesn't make logical sense to duplicate Relationship entities when person_1_id is actually no separate than person_2_id.
I think you've got two options:
1. Duplicate the Relationship Record
Look at it from a different perspective; Consider the concept of directed vs. an un-directed graph. Your current schema supports a directed edge leaving one object (Person) and arriving at another. Agreed from your application's perspective, a person can't have a relationship with another, without them knowing.
The amount of space within the database would be negligible if you did choose to go down this route and would be less complex to implement than option 2. Bare in mind that you should put in place measures to ensure the relationship remains symmetrical at all times.
2. Handle the Relationship Asymmetry in Your Application
Create another relationship so your model looks like the following:
class Member extends AppModel {
public $hasMany = array(
'RelationshipsA' => array(
'className' => 'Relationship',
'foreignKey' => 'person_1_id'
),
'RelationshipsB' => array(
'className' => 'Relationship',
'foreignKey' => 'person_2_id'
)
);
}
You then have separate bins for your relationships depending on which side of the relationship your person exists. You then have to put extra logic everywhere so that you can handle the relationship bins as one homogeneous collection.
I'm sorry I couldn't be any more helpful but I can't think off the top my head, how to implement an un-directed graph without building it on top of a directed one.
Update 05/05/13
To summarize the comment thread, setting the finderQuery on the hasMany association can mask the need to duplicate the lookup for the association with person_2_id (Documentation):
'Relationship' => array(
'finderQuery' => 'SELECT Relationship.* FROM relationships AS Relationship WHERE Relationship.person_2_id = {$__cakeID__$} OR Relationship.person_1_id = {$__cakeID__$};'
)
This hides the complex retrieval mechanism but storing relationships may require further thought.
What I need:
A facebook-like friendship system.
User (A) sends a requests for a friendship with User (B)
User (B) confirmes the request
User (A) and User (B) are friends now
My problem:
I'm confused how to work this out. I read a lot on the internet but it did not really helped me...
My Questions:
What kind of link is it in CakePHP? Is it hasAndBelongsToMany? Or hasMany? Or ...?
How do I realise it in the datebase correctly?
How do I link it in the model?
What I already did:
Table: users
Name: id, username, password, ...
users_users table: id, user_id, friend_id, approved
Model:
'User' => array(
'className' => 'User',
'joinTable' => 'users_users',
'foreignKey' => 'user_id',
'associationForeignKey' => 'friend_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
);
"#tereško Thank you! But I get an error: Error: An Internal Error Has Occurred"
First to answer your comment about the "internal error" you getting:
Try setting debug to 2 in config.php you will realize that you will start getting much more understandable errors.
Regarding your first question:
1. Your relation basically looks to like hasMAny since every user has friends.
HABTM will also work here, but it is much more complicated. This decision (what relations to use) also depends on other parts of your system - i.e. for what and how you'd like to use this data in other parts of it.
2. Read here
3. Read there again
A bit more on HABTM and hasMany through the jon model (if you need to store any additional data in the join table while using the same idea as HABTM).
What to do when HABTM becomes complicated?
By default when saving a HasAndBelongsToMany relationship, Cake will delete all rows
on the join table before saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2 children. The Club will only have 2 Children, not 12.
Also note that if you want to add more fields to the join (when it was created or meta information) this is possible with HABTM join tables, but it is important to understand that you have an easy option.
HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo
association.
Your current DB structure is awful. I didn't get why you need a table called table - or maybe I got this wrong.If you intend to use HABTM you do not need to create the join model at all - cake will automatically create and populate it for you.
Some more info for HATBM:
Here
There
Here
There