Hy i am trying to create a follow feature to my website so user can follow each other , I am using Cakephp what kind of relation should i use , what should i name the tables.
NB: I created a user table + follow table containing user_id and follower_id !
If you don't need to save any information about the relation, then hasAndBelongsToMany is the natural relation to use in this case.
Try this:
// User Model
var $hasAndBelonsToMany = array(
'Follower' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'associationForeignKey' => 'follower_id'
'joinTable' => 'followers_users'
)
)
then you must create the users table as normal, and a table 'followers_users' with columns: 'id', 'user_id', 'follower_id' (and 'created' and 'updated' if you need them).
EDIT :
To retrieve your data (read here) you do it as usual:
$this->User->find('all', array('conditions' => array('id' => 1)));
Then you'll get an array like:
Array(
[User] => array(
[id] => 1
[name] => xxxx
)
[Follower] => array(
[0] => array(
[id] => 2
[name] => yyyy
)
[1] => array(
[id] => 3
[name] => zzzz
)
)
)
To save your data (read here and here), you need to create an array like:
array(
[User] => array(
[id] => 1
[name] => xxx
)
[Follower] => array(
[0] => array(
[id] => 2
)
[1] => array(
[id] => 3
)
)
)
Explanation of what you are looking / example is right in the book:
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-relations-to-the-same-model
Similar example per the book:
"It is also possible to create self associations as shown below:"
<?php
class Post extends AppModel {
public $name = 'Post';
public $belongsTo = array(
'Parent' => array(
'className' => 'Post',
'foreignKey' => 'parent_id'
)
);
public $hasMany = array(
'Children' => array(
'className' => 'Post',
'foreignKey' => 'parent_id'
)
);
}
Related
hello I have a table name University_Industry. It has uni_id and ind_id as foriegn keys of University table and Industry table. So meaning university will have many industries and industries will have many universities. I am getting the result successfully by running this query
public function getAllUniv(){
return $this->find('all', array(
'order' => 'uni_id',
));
}
This query returns me universities and their industries.Result look like this
Array
(
[0] => Array
(
[University] => Array
(
[uni_id] => 1
[uni_name] => NYC
)
[Industry] => Array
(
[0] => Array
(
[ind_id] => 1
[ind_name] => Finance
[UniversityIndustry] => Array
(
[id] => 1
[uni_id] => 1
[ind_id] => 1
)
)
[1] => Array
(
[ind_id] => 2
[ind_name] => Accounting
[UniversityIndustry] => Array
(
[id] => 2
[uni_id] => 1
[ind_id] => 2
)
)
)
)
Now I have another table which called users which has uni_id also as a foreign key. So I am looking for a query which can fetch only those universities with the industries which are found in users table. So It should fetch only those universities whose uni_id is exist in users table
I have no idea how can I join users table in my query and get the results.
This is how my University Modal look like at the moment
class University extends AppModel
{
public $useTable = 'university';
public $primaryKey = 'uni_id';
public $hasAndBelongsToMany = array(
'Industry' =>
array(
'className' => 'Industry',
'joinTable' => 'university_industry',
'foreignKey' => 'uni_id',
'associationForeignKey' => 'ind_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
),
);
public function getAllUniv(){
return $this->find('all', array(
'order' => 'uni_id',
));
}
}
You can join data in your find query.
$this->find('all', array(
'order' => 'uni_id',
'joins' => array(
array(
'table' => 'users ',
'alias' => 'UserJoin',
'type' => 'inner',
'conditions' => array(
'UserJoin.uni_id = University.id'
)
),
));
Here is official doc. Hope it will help you
I have a 2 CakePHP models, Articles and Categories, attached using a hasAndBelongsToMany relationship, like so:
$category = new Category();
$category->bindModel(array('hasAndBelongsToMany' => array(
'Article' => array(
'className' => 'Article',
'joinTable' => 'articles_categories',
'foreignKey' => 'category_id',
'associationForeignKey' => 'article_id',
'fields' => 'id,name'
))));
$this->set('categories', $category->find('all',
array(
'fields' => 'id,name'
)));
...but then, when I print out $categories, I get the following:
Array
(
[0] => Array
(
[Category] => Array
(
[id] => 31
[name] => category name
[article_count] => 1
)
[Article] => Array
(
[0] => Array
(
[id] => 1445
[name] => article name
[teaser] =>
[author_id] => 3
[ArticlesCategory] => Array
(
[id] => 6634
[article_id] => 1445
[category_id] => 31
[author_id] => 3
[created] => 2014-03-10 12:27:26
[modified] => 2014-03-10 12:27:26
)
)
)
)
I really don't need the [ArticlesCategory] member of [Article]. This just leads me back to information I already have. I tried limiting recursion but that didn't help.
How would I get rid of this?
You have two options:
[1] Reduce recursive value to 0 (CakePHP default: 1)
$category->recursive = 0;
$category->find('all',
array(
'fields' => 'id,name'
))
[2] Start using ContainableBehaviour (my own preference), which gives you more control over model data retrieved
Add following to AppModel for App-wide coverage:
public $actsAs = array('Containable');
The find method becomes:
$category->find('all',
array(
'fields' => 'id, name',
'contain' => 'Article'
))
That should do it!
I have the following relations:
class Platformuser extends AppModel {
public $hasAndBelongsToMany = array(
'Service'
);
}
class Service extends AppModel {
public $hasAndBelongsToMany = array(
'Platformuser'
);
}
I am doing a action on the PlatformusersController to get the services associated with this user with the following query:
$this->Platformuser->find('all', array(
'conditions' => array('Platformuser.id' => $userId),
));
It returns everything about Platformuser/Service and I ONLY want the data of the service:
array(
(int) => array(
[Platformuser] => array(
[id] => [1]
[name] => [Domingo]
),
[Service] => array(
(int) 0 => array(
[id] => [1]
[name] => [dropbox],
[PlatformusersService] => array(
[id] => [1],
[platformuser_id] => [1],
[service_id] => [1],
[modified] => [2013-10-10 00:00:00],
[created] => [2013-10-10 00:00:00];
)
)
)
)
I want something like:
array(
[Service] => array(
(int) 0 => array(
[id] => [1]
[name] => [dropbox]
)
)
Any ideas?.
You should use the containable behavior. It's easy to use and you'll able to fetch data according to your need like.
$plateFormuser = $this->Platformuser->find('first', array(
'conditions' => array('Platformuser.id' => $userId),
'contain' => array(
'Service' => array(
'fields' => array('id', 'name')
)
)
));
$services = Set::merge(
Set::classicExtract($plateFormuser, 'Service.{n}.{id}'),
Set::classicExtract($plateFormuser, 'Semester.{n}.{name}')
);
Now you can json encode the $services to get result as you've mentioned [{"id":"1", "name":"dropbox"}].
I am in process of chaining multiple models. The goal is to retrieve the category for the keyword when I pass a keyword.
Models
Keyword
columns:
id
keyword
KeywordCategory
columns:
keyword_id
category_id
Category
columns:
id
category
The following are the association definitions that I made.
Keyword
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'keyword_id',
'dependent' => true
)
);
KeywordCategory
var $belongsTo = array(
'Keyword' => array(
'className' => 'Keyword',
'foreignKey' => 'keyword_id',
'dependent' => true
),
'Category' => array(
'className' => 'Category',
'foreignKey' => 'category_id',
'dependent' => true
)
);
Category:
var $hasMany = array(
'KeywordCategory' => array(
'className' => 'KeywordCategory',
'foreignKey' => 'category_id',
'dependent' => true
)
);
My question is when I currently retrieve the Keyword category currently it will return something like following:
Array
(
[Keyword] => Array
(
[id] => 2
[keyword] => rc
[modified] =>
[created] =>
)
[KeywordCategory] => Array
(
[0] => Array
(
[keyword_id] => 2
[category_id] => 2
)
[1] => Array
(
[keyword_id] => 2
[category_id] => 1
)
)
)
I want to also return the Categories signified by category_id in the result. Can anyone suggest the change that I need to make?
Thank you
I had a recent question here regarding Finding records via conditions in habtm. Now, I could search for posts within a category I searched for.
Now, my questions is how to I retrieve the categories of each post. Sometimes a post has one or more categories in this query:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post')
)
));
I'd imagine something like this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array(
'contain' => 'Category'
))
)
));
Here's what my models look like.
// Category Model
class Category extends AppModel {
var $name = 'Category';
var $hasAndBelongsToMany = array(
'Post' => array(
'className' => 'Post'
)
);
var $actsAs = array('Containable');
}
// Post Model
class Post extends AppModel {
var $name = 'Post';
var $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category'
)
);
var $actsAs = array('Containable');
var $virtualFields = array(
'date_posted' => 'DATE_SUB(Post.created, INTERVAL 7 DAY)'
);
}
A sample data would be like this:
categories
id name
1 holidays
2 destinations
posts
id title
1 I am a post
2 I am another post
categories_posts
post_id category_id
1 1
2 2
2 1
I am retrieving posts from holidays.
Array
(
[Category] => Array
(
[id] => 3
[name] => holidays
[uri] => holidays
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 1
[title] => I am a post
),
[1] => Array
(
[id] => 2
[title] => I am a another post
)
)
)
The problem is that 1 of the posts are in two categories. I'd like to have that information also available.
Close. Try this:
$this->set('posts', $this->Category->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
Tangent
Writing Controller code like this has problems:
It will bloat your controller methods and make your controllers less readable.
It prevents you from re-using business logic among your different controllers.
You can satisfactorily correct these problems by moving your find call to a model method:
// Categories Controller
$this->set('posts', $this->Category->get_posts_with_cats($uri));
// Category Model
function get_posts_with_cats($uri) {
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array('Post' => array('Category'))
));
}
This makes your code More Awesome:
The controller is nice and Skinny, and super readable.
Now you can call the same method from any associated model. So, for instance, in your PostsController you could call: $this->Post->Category->get_posts_with_cats($uri);. Your code is now DRY.
Tangent #2
After you learn how to nest containable models like I've shown you, you may be tempted in the future to do something like this:
$this->find('first', array(
'conditions' => array('Category.uri' => $uri),
'contain' => array(
'Post' => array(
'Category' => array(
'Tag' => array('Author')
)
)
)
));
The good news? You will get the desired model data from the above method (as long as you've defined all of these relationships).
The terrible news? CakePHP doesn't know how to optimize the queries for deep associations like this. So, as your database grows, you'll end up with dozens (if not hundreds (if not thousands)) of SELECT queries to the database, effectively bringing your application to a crawl (if not a halt.)
The good folk that design CakePHP are working on a better containable behavior for CakePHP 2.0, but you'll have to wait until then to get this level of precision.
I'm leaving my other answer as-is because it contains a lot of good info for those who may stumble upon it. As far as solving your problem more elegantly? You'll need to flip the find on it's head and search from the Post model with an ad-hoc join, like this:
// From the CategoriesController
$this->Category->Post->find('all', array(
'joins' => array(
array(
'table' => 'categories_posts',
'alias' => 'CategoryFilter',
'type' => 'inner',
'conditions' => array(
'CategoryFilter.post_id = Post.id'
)
),
array(
'table' => 'categories',
'alias' => 'CategoryUriFilter',
'type' => 'inner',
'conditions' => array(
'CategoryUriFilter.id = CategoryFilter.category_id'
)
)
),
'conditions' => array(
'CategoryUriFilter.uri' => $uri
),
'contain' => array('Category')
));
The array returned will be formatted a bit differently, but it should contain all of the data you were looking for.
I actually did this to get what I want:
function getPostsFromCategory($uri) {
return $this->populatePosts($this->find(
'first',
array(
'conditions' => array(
'Category.uri' => $uri
),
'contain' => array('Post' => array('Category'))
)
));
}
function populatePosts($categoryPosts) {
$posts = $categoryPosts['Post'];
$count = count($categoryPosts['Post']);
for ($i = 0; $i < $count; $i++) {
$categories = $this->Post->find('first', array('conditions' => array('Post.id' => $categoryPosts['Post'][$i]['id']),
'contains' => array('Category')));
// unset($categories['Post']);
foreach ($categories['Category'] as $cat) {
$categoryPosts['Post'][$i]['Categories'][] = $cat;
}
unset($categories);
}
return $categoryPosts;
}
I really don't know if this can be done in a more CakePHP-y way though:
I returns something like this:
Array
(
[Category] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[Post] => Array
(
[0] => Array
(
[id] => 28
[title] => A Post
[Categories] => Array
(
[0] => Array
(
[id] => 3
[name] => festivals
[uri] => festivals
[created] => 2010-11-25 20:43:03
[modified] => 2010-11-25 20:43:03
)
[1] => Array
(
[id] => 4
[name] => destinations
[uri] => destinations
[created] => 2010-11-28 13:04:52
[modified] => 2010-11-28 13:04:52
)
)
)
)