how use table that association with itself? - cakephp

how use table that association with itself ?
i use cakephp and table is Section :
create table SECTIONS
(
SECTIONID int(11) not null auto_increment,
TITLE char not null,
CONTROLID int(11) not null,
SECTIONPARENTID int(11),
primary key (SECTIONID)
)
this table have association with itself and i use belong to and has many association and my model is :
class Section extends AppModel {
var $name = 'Section';
var $primaryKey = 'SECTIONID';
var $displayField = 'TITLE';
}
i use belong to and has many association in two table. but i can't use in this example.
thanks for help.

Self referential models are simple in Cake once you know the trick, but you aren't doing yourself any favours by not using Cake naming conventions. I'll assume you're using a datasource that's out of your control :-)
Class Section extends AppModel {
var $belongsTo = array(
'Parent'=>array(
'className'=>'Section',
'foreignKey'=>'SECTIONPARENTID'
)
);
var $hasMany = array(
'Children'=>array(
'className'=>'Section',
'foreignKey'=>'SECTIONPARENTID'
)
);
}
When you run a query such as $this->Section->find('first') you'll get a returned array that looks like this:
section => array(
SECTIONID,
...
'Parent'=>array(
'SECTIONID',
....
),
'Children'=>array(
[0] => array(
[SECTIONID]
),
[1] => array(
[SECTIONID]
),
...
)
)

Related

CakePHP model linking, belongsTo, hasOne

I read the CakePHP book but couldn't make it.
I have a table named frendslists. Every user (owner_user_id) has too many friends and I add the friends to friend_id column. (Model name is Friendslist)
CREATE TABLE IF NOT EXISTS `friendslists` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`owner_user_id` int(20) unsigned NOT NULL,
`friend_id` int(20) NOT NULL COMMENT 'id of the friend',
PRIMARY KEY (`id`),
KEY `friend_id` (`friend_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--id-------owner_user_id-----friend_id
--------------------------------------
--1--------1234--------------9200-----
--2--------1234--------------3210-----
--3--------1234--------------7600-----
I also have a profiles table. Every unique person have a profile there. One person can have only one profile. (Model name is Profile)
CREATE TABLE IF NOT EXISTS `profiles` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`profile_user_id` int(20) NOT NULL,
`name` varchar(50) NOT NULL,
`location` varchar(50) NOT NULL,
PRIMARY KEY (`id`),
KEY `profile_user_id` (`profile_user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--id------profile_user_id---name------location-
-----------------------------------------------
--1-------9200--------------michael----usa-----
--2-------3210--------------john-------uk------
--3-------7600--------------danny------denmark-
I want to link friendslists table to profiles table. Is this one to one (hasOne) or many to one (belongsTo) type relationship ?
When I query friendslists table I want to get profiles data of the friends. What should I do inside CakePHP models and tables?
I created a foreign key like this:
ALTER TABLE `friendslists`
ADD CONSTRAINT `friendslists_ibfk_1`
FOREIGN KEY (`friend_id`)
REFERENCES `profiles` (`profile_user_id`)
ON DELETE CASCADE ON UPDATE CASCADE;
I changed model file to this:
class Friendslist extends AppModel {
var $name = 'Friendslist';
var $useTable = 'friendslists';
public $belongsTo = array(
'Profile' => array(
'className' => 'Profile',
'foreignKey' => 'friend_id'
)
)
function getAll(){
return $this->find('all');
}
}
At last when I do this:
$records=$this->Friendslist->find('all', array('conditions' => array(
'Friendslist.owner_user_id' => 1234)
));
I get these result:
[Friendslist] => Array
(
[id] => 1
[owner_user_id] => 1234
[friend_id] => 9200
)
[Profile] => Array
(
[id] =>
[profile_user_id] =>
[name] =>
[location] =>
)
)
I'm sure that profiles table has a record with profile_user_id=9200. But Profile records comes empty.
I'm a little confused why you're linking friendslists to profiles. Wouldn't it make more sense to link people with people and then get the profile therefrom?
In any case, what you're describing is a HasAndBelongsToMany(HABTM)
So in your Person model you want to specify that they have many other people (or in your case Profiles) that they're associated with and specify the lookup table.
Something like...
public $hasAndBelongsToMany = array(
'Person' => array(
'className' => 'Profile',
'joinTable' => 'friendslists',
'foreignKey' => 'owner_id',
'associationForeignKey' => 'friend_id'
)
);
And then within the friendslists model I'd describe it as a belongsTo listing both the owner and the profile.
Something like:
public $belongsTo = array(
'Person' => array(
'className' => 'Person'
,'foreignKey' => 'owner_user_id'
),
'Profile' => array(
'className' => 'Profile'
,'foreignKey' => 'friend_id'
)
);
You may need to tweak the names as I had a hard time digesting exactly what entities are at play, but that should give you an idea at least.

cakephp model association is not working

I have the table 'posts' and 'users':
CREATE TABLE `posts` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(255) default NULL,
`date` datetime default NULL,
`content` text,
`user_id` int(11) default NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(100) default NULL,
`email` varchar(150) default NULL,
`firstname` varchar(60) default NULL,
`lastname` varchar(60) default NULL,
PRIMARY KEY (`id`)
);
and the model classes:
<?php
class Post extends AppModel {
var $name = 'Post';
var $belongsTo = array(
'User'=>array(
'className'=>'User',
'foreignKey'=>'user_id',
'conditions'=>null,
'fields'=>null)
);
}
?>
<?php
class User extends AppModel {
var $name = 'User';
var $hasMany = array('Post');
}
?>
I am testing using var $scaffold.
However, after I add some users, I can only see an empty select menu in the add post page, which means the association is not working. I don't know what is wrong with my code. Please help me out.
Thank you very much!
See the below url:-
http://book.cakephp.org/1.3/view/1042/belongsTo
http://book.cakephp.org/1.3/view/1043/hasMany
//Try This for belongsTo :--
<?php
class Profile extends AppModel {
var $name = 'Profile';
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
}
?>
Or
<?php
class Profile extends AppModel {
var $name = 'Profile';
var $belongsTo = array('User');
}
?>
//For hasmany
<?php
class User extends AppModel {
var $name = 'User';
var $hasMany = array(
'Post' => array(
'className' => 'Post',
'foreignKey' => 'user_id',
'conditions' => array('Comment.status' => '1'),
'order' => 'Comment.created DESC',
'limit' => '5',
'dependent'=> true
)
);
}
?>
I am starting with cakePHP too and banged my head for some time before I realized that the "conventions" require the Model filename to be CamelCased.
Try using:
Post.php
User.php
otherwise it seems the models are totally ignored.

CakePHP containable order condition not working

I'm having problems with a very simple ordering query. I have a Post model and a Tag model with a HABTM relationship and am trying to return a list of all posts with a particular tag assigned to them, ordered by the date the post is created.
$this->set('data', $this->Post->Tag->find('all', array(
'conditions' => array('Tag.id' => 1),
'contain' => array('Post' => array(
'order' => 'Post.created_date desc'
))
)));
While this returns the list of posts, it is not sorted by date.
With debugging on, it looks like the following query is being used:
SELECT `Post`.`id`, `Post`.`title`, `Post`.`created_date`, `PostsTag`.`post_id`, `PostsTag`.`tag_id`
FROM `database`.`posts` AS `Post`
JOIN `database`.`posts_tags` AS `PostsTag` ON (`PotsTag`.`tag_id` = 1 AND `PostsTag`.`post_id` = `Post`.`id`)
Code for posts model:
class Post extends AppModel {
public $name = 'Post';
public $hasAndBelongsToMany = array('Tag');
}
Code for tags model:
class Tag extends AppModel {
public $name = 'Tag';
public $hasAndBelongsToMany = array('Post');
}
Any help on the issue would be much appreciated - I'm using CakePHP 2.1. if it makes any difference.
What about defining the order attribute in your Tag Model?
Like e.g.
var $hasAndBelongsToMany = array(
'Post' => array(
'order' => 'Post.created_date'
)
);
I don't think that the "order" should be inside of "contain".
Try with:
$this->set('data', $this->Post->Tag->find('all', array(
'conditions' => array('Tag.id' => 1),
'contain' => array('Post'),
'order' => 'Post.created_date desc'
)));
or just:
$this->set('data', $this->Post->Tag->find('all', array(
'conditions' => array('Tag.id' => 1),
'order' => 'Post.created_date desc'
)));
Read this:-
http://www.jamesfairhurst.co.uk/posts/view/adding_tags_to_a_cakephp_app_hasAndBelongsToMany/
http://edivad.wordpress.com/2007/04/19/cakephp-hasandbelongstomany-habtm/
//try this
CREATE TABLE `tags` (
`id` int(11) NOT NULL auto_increment,
`tag` varchar(100) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `posts_tags` (
`post_id` int(11) NOT NULL,
`tag_id` int(11) NOT NULL
);
//Creating the Models and Relationships
class Tag extends AppModel {
var $name = 'Tag';
var $hasAndBelongsToMany = array('Post'=>array('className'=>'Post'));
}
class Post extends AppModel {
var $name = 'Post';
var $hasMany = array('Comment'=>array('className'=>'Comment'));
var $hasAndBelongsToMany = array('Tag'=>array('className'=>'Tag'));
}

Selecting the newest record of a hasMany relationship

I have 2 basic models in my CakePHP application: User and Login. A user has a hasMany relation with Logins (i.e., a new login record is created everytime the user logs in).
Now, I want to make 2 relations from the User model to the Login model:
User hasMany Login
and
User hasOne lastLogin
This last relation should only include the last record of the Login model for the selected user.
I tried this as follows:
var $belongsTo = array
(
'LastLogin' => array
(
'className' => 'Login',
'order' => 'LastLogin.created DESC',
'limit' => 1
)
);
However, this doesn't work. Any ideas on how to get this working?
UPDATED ANSWER IN RESPONSE TO COMMENTS
With a belongsTo relationship, the foreign key should be in the current model.
This means that if you want to have a relationship where User belongsTo LastLogin, the users table should have a last_login_id field.
In your case you probably want to use a hasOne relationship instead, and you're going to have to use the MAX() SQL function in the fields key. Note that getting the last_login works completely independently of your User hasMany Login relationship. So if all you want is the last login you can remove the hasMany relationship and just leave the hasOne.
With the example code below you'll get this:
Output of /users/index:
Array
(
[User] => Array
(
[id] => 1
[name] => user1
[last_login] => 2011-05-01 14:00:00
)
[Login] => Array
(
[0] => Array
(
[id] => 1
[user_id] => 1
[created] => 2011-05-01 12:00:00
)
[1] => Array
(
[id] => 2
[user_id] => 1
[created] => 2011-05-01 13:00:00
)
[2] => Array
(
[id] => 3
[user_id] => 1
[created] => 2011-05-01 14:00:00
)
)
)
If you don't use the Model::afterFind() callback your results will look more like this (Login array snipped to save space):
Array
(
[User] => Array
(
[id] => 1
[name] => user1
)
[0] => Array
(
[last_login] => 2011-05-01 14:00:00
)
)
Example code:
users table:
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
logins table:
CREATE TABLE `logins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
)
User model:
class User extends AppModel {
var $name = 'User';
var $hasMany = array('Login');
var $hasOne = array(
'LastLogin' => array(
'className' => 'Login',
'fields' => array('MAX(LastLogin.created) as last_login')
)
);
// This takes the last_login field from the [0] keyed array and puts it into
// [User]. You could also put this into your AppModel and it would work for
// all find operations where you use an SQL function in the 'fields' key.
function afterFind($results, $primary=false) {
if (!empty($results)) {
foreach ($results as $i => $result) {
if (!empty($result[0])) { // If the [0] key exists in a result...
foreach ($result[0] as $key => $value) { // ...cycle through all its fields...
$results[$i][$this->alias][$key] = $value; // ...move them to the main result...
}
unset($results[$i][0]); // ...and finally remove the [0] array
}
}
}
return parent::afterFind($results, $primary=false); // Don't forget to call the parent::afterFind()
}
}
Users controller:
class UsersController extends AppController {
var $name = 'Users';
function index() {
$this->autoRender = false;
pr($this->User->find('all'));
}
}
I had a similar situation but mine was
Product hasMany Price
I wanted a Product hasOne CurrentPrice with the CurrentPrice defined as the top most record found if sorted by created in desc order.
I solved mine this way. But I am going to use your User and LastLogin instead.
class User extends AppModel {
var $name = 'User';
var $hasMany = array('Login');
var $hasOne = array(
'LastLogin' => array(
'className' => 'Login',
'order' => 'LastLogin.created DESC'
)
);
If you think about it, created and id has the same meaning. so you could also use id in descending order.
I am assuming that your table schema is similar to the one suggested by mtnorthrop.
i.e.
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
logins table:
CREATE TABLE `logins` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
)

how to define two foreign keys in cakePHP

I got a table with a few columns.
The table has a primary key (replyid), and two foreigne keys (userid and postid).
Here is the structure of the table:
{replyid, content, userid, postid}
The Reply table got two foreigne keys.
I am not sure if this is a correct to set the foreign keys like this:
class Post extends AppModel{
var $name = 'Post';
var $useTable = 'post';
var $primaryKey = 'postid';
var $belongsTo = 'User';
var $hasMany = array(
'Reply' => array(
'className' => 'Reply',
'foreignKey' => 'postid',
'foreignKey' => 'userid'
)
);
}
?>
Could you helpplease?
Two foreign keys are fine,but you should define the relationshions in right models.
In your post model,
var $hasMany = array(
'Reply' => array(
'className' => 'Reply',
'foreignKey' => 'postid',
//'foreignKey' => 'userid'
)
);
In your user model,
var $hasMany = array(
'Reply' => array(
'className' => 'Reply',
//'foreignKey' => 'postid',
'foreignKey' => 'userid'
)
);
Unfortunately CakePHP does not support composite keys or partial primary keys. As recommended by the CakePHP manual, you can either use direct query calls such as:
CREATE TABLE posts_tags (
id INT(10) NOT NULL AUTO_INCREMENT,
post_id INT(10) NOT NULL,
tag_id INT(10) NOT NULL,
PRIMARY KEY(id));
...Or, you can create a surrogate key, perhaps an auto-increment value. Rather than using an auto-increment key as the primary key, you may also use char(36). CakePHP will then use a unique 36 character uuid (String::uuid) whenever you save a new record using the Model::save method. (From CakePHP Manual)
Build your tables in the database and use cake bake to build the models - this way you'll see how Cake handles it natively. I recommend that you name your db columns like foreign_id. It'll make life a lot easier.

Resources