Naming convention and joins in CakePHP - php

Just a few days ago I found out about this miracle called CakePHP so I am pretty green to it.
I need to build a mail application, so I have followed the convention and created:
Database description:
Table of users <user_id (primary key), fname, lname>.
Table of mails <mail_id(primary key), from (foreign key to user_id), to (foreign key to user_id), content, opened>.
My questions:
1) According to the convention, a foreign key should be called related table+'_id'. How should I call the columns if there are two foreign keys that relate to the same table. Like from and to in the mails table.
2) I would like to do an inner JOIN the between the two tables.
Something like:
SELECT user_id, mail_id
FROM users
INNER JOIN mails
ON users.user_id =mails.to AND mails.opened=false.
But I have no clue how to do it.

When you need to do two relations to the same table, you will need to override the default convention. In your example, I would make 2 foreign keys. One named sender_id and one named recipient_id. Then you would join them in the Model like so:
<?php
class Mail extends AppModel {
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'UserSender' => array(
'className' => 'User',
'foreignKey' => 'sender_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'UserRecipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
);
}
?>
Then to do your conditions, you would reference them like so:
<?php
$this->Mail->find(array('conditions'=>array('Mail.opened'=>false)));
?>
...and to filter on the sender and receiver, your conditions would look like:
<?php
$this->Mail->find(array('conditions'=>array('UserSender.some_field'=>$someValue,
'UserRecipient.some_field'=>$someValue)));
?>

I'm not an expert myself, but following info on the CakePHP site will help you further:
Multiple-relations-to-the-same-model

Related

cakephp belongsto custom condition issue

I’m using cakephp 1.3 with mysql database and engine is MYISAM, I’m getting problem in relationships. I have two tables name Organization and OrgClasses, these two are associated with mbo_studio_id which is common in both table.
organization
Id(PK), name, mbo_studio_id
org_classes
id(PK), name,date, mbo_studio_id
I’m trying to fetch data on the condition of mbo_studio_id, but it adds another condition by primary key of OrgClasses.
var $belongsTo = array(
'OrgClass' => array(
'className' => 'OrgClass',
'foreignKey' => 'mbo_studio_id',
'conditions' => array('OrgClass.mbo_studio_id' => 'Organization.mbo_studio_id'),
'order' => 'OrgClass.date DESC',
'dependent' => false,
)
);
I get following query
SELECT `Organization`.`id`, `Organization`.`name, `Organization`.`mbo_studio_id`, `OrgClass`.` id`,`OrgClass`.`mbo_studio_id`, `OrgClass`.`name`, `OrgClass`.`date`
FROM `organizations` AS `Organization`
LEFT JOIN `org_classes` AS `OrgClass` ON (`Organization`.`mbo_studio_id` = `OrgClass`.`id` AND `OrgClass`.`mbo_studio_id` = Organization.mbo_studio_id)
Here I don’t want Organization.mbo_studio_id = OrgClass.id condition in query.
Thanks
Then you need to set foreignKey to false:
'foreignKey' => false,
This way it will only use your custom conditions.

CakePHP find list of associated items where id is

I want to create in cake php application with users, games and games platforms (for ex PS3)
I got tables:
userGames (game have one platform)
id, name, platform_id
users (users can have many platforms)
id, username
platforms
id, name
users_platforms
id, platform_id, user_id
And now i want to select all user id=1 platforms, and list it for select tag.
Here is sql query:
SELECT platforms.name, platforms.id FROM platforms LEFT JOIN platforms_users ON platforms_users.platform_id=platforms.id WHERE platforms_users.user_id=1
But i dont know what to list this by find('list') function in cakePHP
I try type in users controller:
$this->User->Platform->find('list', array('conditions', array('User.id'=>'1')));
But this returns sql problem (undefinded User.id)
Anyone can help me?
please try this
$this->loadModel('UserPlatform');
$this->UserPlatform->bindModel(array(
'belongsTo' => array('Platform')
));
$this->UserPlatform->find('list', array(
'fields' => array('Platform.id','Platform.name'),
'conditions' => array('UserPlatform.user_id'=>'1'),
'recursive' => 1
));
you must apply find function on join table
Your find must same as this:
$this->PlatformUser->find('list',array('fields'=>
array('Platform.name','Platform.id'),'conditions'=> array('PlatformUser.id' => 1)));
Your PlatformUser Model must have:
public $belongsTo = array(
'Platform' => array(
'className' => 'Platform',
'foreignKey' => 'platform_id',
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
),
);
What you are trying to do is actually a HasAndBelongsToMany Association.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
hasAndBelongsToMany (HABTM)
We’ll need to set up an extra table in the database to handle HABTM associations. This new join table’s name needs to include the names of both models involved, in alphabetical order, and separated with an underscore ( _ ). The contents of the table should be two fields that are foreign keys (which should be integers) pointing to the primary keys of the involved models. To avoid any issues, don’t define a combined primary key for these two fields. If your application requires a unique index, you can define one. If you plan to add any extra information to this table, or use a ‘with’ model, you should add an additional primary key field (by convention ‘id’).
HABTM requires a separate join table that includes both model names.
(This would be the UserPlatform-table if I got that right.)
Make sure primary keys in tables cakes and recipes have “id” fields as assumed by convention. If they’re different than assumed, they must be changed in model’s primaryKey.
class Recipe extends AppModel {
public $hasAndBelongsToMany = array(
'Ingredient' =>
array(
'className' => 'Ingredient',
'joinTable' => 'ingredients_recipes',
'foreignKey' => 'recipe_id',
'associationForeignKey' => 'ingredient_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'with' => ''
)
);
}

Associations not using foreign key

I have a model (AccountAgentDetail) that has 2 associations. One is a belongsTo (AccountUser) and the other is a hasOne(AccountProfile). The table for AccountAgent only has a FK relation to AccountUser. This model and the associated models are part of a plugin.
The issue I am seeing is that when the query is executed the join from AccountProfile to AccountAgentDetail is using the wrong association. It is using the id field of the AccountAgentDetail table instead of the fk field that I have defined in the AccountAgentDetail model.
This is the model that I am working with:
<?php
class AccountAgentDetail extends AccountModuleAppModel {
var $name = 'AccountAgentDetail';
var $primaryKey = 'agent_detail_id';
var $belongsTo = array(
'AccountUser' => array(
'className' => 'AccountModule.AccountUser',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
var $hasOne = array(
'AccountProfile' => array(
'className' => 'AccountModule.AccountProfile',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public function getProspectiveAgents($count = 10)
{
return $this->find('all',
array(
'conditions'=>array('AccountAgentDetail.is_prospect'=>1),
'order'=>array('AccountAgentDetail.created_date DESC')
)
);
}
}
?>
This is the query that is executed when I call the method getProspectiveAgents. The issue I am seeing is in the second left join it is using AccountAgentDetail.agent_detail_id instead of AccountAgentDetail.user_id
SELECT
`AccountAgentDetail`.`agent_detail_id`,
`AccountAgentDetail`.`user_id`,
`AccountAgentDetail`.`is_prospect`,
`AccountAgentDetail`.`mls_id`,
`AccountAgentDetail`.`primary_office`,
`AccountAgentDetail`.`primary_board`,
`AccountAgentDetail`.`commission_plan`,
`AccountAgentDetail`.`referred_by`,
`AccountAgentDetail`.`referral_source`,
`AccountAgentDetail`.`previous_brokerage`,
`AccountAgentDetail`.`created_date`,
`AccountAgentDetail`.`last_modify_date`,
`AccountAgentDetail`.`created_by`,
`AccountAgentDetail`.`last_modifed_by`,
`AccountUser`.`user_id`,
`AccountUser`.`user_name`,
`AccountUser`.`user_pass`,
`AccountUser`.`user_status`,
`AccountUser`.`user_group`,
`AccountUser`.`instance_id`,
`AccountUser`.`is_logged_in`,
`AccountUser`.`is_visible`,
`AccountUser`.`created_by`,
`AccountUser`.`last_modified_by`,
`AccountUser`.`created_date`,
`AccountUser`.`last_modified_date`,
`AccountProfile`.`profile_id`,
`AccountProfile`.`user_id`,
`AccountProfile`.`first_name`,
`AccountProfile`.`middle_name`,
`AccountProfile`.`last_name`,
`AccountProfile`.`birth_date`,
`AccountProfile`.`ssn`,
`AccountProfile`.`employee_id`,
`AccountProfile`.`hire_date`,
`AccountProfile`.`sever_date`,
`AccountProfile`.`rehire_date`,
`AccountProfile`.`created_by`,
`AccountProfile`.`last_modified_by`,
`AccountProfile`.`created_date`,
`AccountProfile`.`last_modify_date`
FROM
`account_agent_details` AS `AccountAgentDetail`
LEFT JOIN `account_users` AS `AccountUser` ON(
`AccountAgentDetail`.`user_id` = `AccountUser`.`user_id`
)
LEFT JOIN `account_profiles` AS `AccountProfile` ON(
`AccountProfile`.`user_id` = `AccountAgentDetail`.`agent_detail_id`
)
WHERE
`AccountAgentDetail`.`is_prospect` = 1
ORDER BY
`AccountAgentDetail`.`created_date` DESC
You have decalared 'agent_detail_id' as primary key, it seems to me logical to select this as primary key for a join instead of user_id which is foreign key! Although I don't know exactly how this class works in the background I think you should work your model.
In your place I would work it in my mind out of the mvc-class-model context, thinking only under the E-Relational "frame".
Not sure about that...maybe you business requirements are such that you can share the primary key user_id, practically speaking having it both as foreign key and primary key in AccountAgentDetail if it is a ono-to-one.

PHP/CakePHP : Relation HasOneBelongToOne

I'm trying to figured out what's the best way to make a "Has One Belong to One" relation in CakePHP. Unfortunatly I didn't found anything for helping me on the internet.
I've tried to proceed like that:
Company model :
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
User model :
var $hasOne = array(
'Company' => array(
'className' => 'Company',
//'foreignKey' => 'user_id',
'dependent' => true
)
);
But still cakePHP allow me to create two companies for one user.
And here my database schema :
Company : id, name, ..., user_id
User : id, name, ...
Many thanks
CakePHP can handle the following relationships only:
one to one
one to many
many to one
many to many
Read more here
If you want to do "Has One Belong to One" then use the hasOne feature in both objects.
If each user has only one company and each company belongs to one and only one user, shouldn't they be on the same table?
like:
USER
Id
Name
...
Company
Sorry if I shouldn't ask questions back here but seemed pertinent.
In your database, company table, user_id should be set unique.

DataMapper ORM for Codeigniter Relations

I have a table ...
CREATE TABLE IF NOT EXISTS `messages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`to` int(11) NOT NULL,
`from` int(11) NOT NULL,
`subject` varchar(50) NOT NULL,
`message` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
To and From is the primary key id from Users Table.
How can I get the user details when I get each message with CodeIgniter DataMapper.
You are missing a couple key points to using the DataMapper for CodeIgniter. First off you need to do some pretty simple and important things. DataMapper (DM) uses normalized db naming to find relationships. What this means is if you query your db. Now it's a little harder to use DM for two columns and I think that you really don't need it.
First if you don't use DM you really only need two queries
SELECT u.*, m.* FROM messages AS m, users AS u WHERE m.from = u.id AND m.id = SOME_ID.
This query will get you all user details and message details for some message ID.
Now this is semi-simple case because I am assuming a message can only be from one user.
For the to field on the other hand I will assume you should use a relational table. To use DM for it you have to name the table something like users_messages but again why do you need to use DM when it really is overkill.
Now for the from field you have a many to many relation because a message can have many users that it was to and a user can have many messages that they sent.
So create a table like this
CREATE TABLE message_to (
user_id BIGINT UNSIGNED NOT NULL,
message_to_id BIGING UNSIGNED NOT NULL,
PRIMARY KEY (user_id, message_to_id),
);
If you want to do it right you will also use foreign keys but that depends on your DB
Now you can query really easily and get all the users a message was sent to.
SELECT u.*, m.* FROM users AS u, m AS messages JOIN messages_to AS m_t ON (u.id = m_t.user_id)
And querying the other way around is just as easy, getting all the messages a user has sent.
Remember just because a tool like DM exists doesn't mean it is the best tool for the job and actually using DM in this case incurs a pretty decent overhead when it is not necessary.
Doing this with DM would require the same things you just cannot name your tables/columns as you see fit and you need a class for every table creating it's relationship with other tables.
Meaning you have a lot of extra work to use DM, and you need to learn their syntax.
What you're looking for is a self-relationship.
If this is a 'has_one' relation, you can do that with in-table foreign keys. You do have to follow the naming convention for keys (to_id and from_id instead of to and from).
Currently (v1.8.0) you can only have one relation between any two models:
$has_one = array(
'to' => array(
'class' => 'messages',
'other_field' => 'messages'
),
'messages' => array(
'other_field' => 'to'
)
);
}
See http://datamapper.wanwizard.eu/pages/advancedrelations.html for more information.
You have to make your models [models/users.php] and
[models/messages.php] like this:
class User extends DataMapper {
var $has_many = array(
'sent_message' => array(
'class' => 'Message',
'other_field' => 'sender',
),
'received_message' => array(
'class' => 'Message',
'other_field' => 'receiver',
),
);
}
class Message extends Datamapper {
var $has_one = array(
'sender' => array(
'class' => 'User',
'other_field' => 'sent_message',
),
'receiver' => array(
'class' => 'User',
'other_field' => 'received_message'
),
);
}
I Only have proviede $has_one and $has_many and you have to include the rest of models.
you have to deifne your tables like this:
Table Name: users fields: id, email, ....
Table Name: messages [this is the important table in this case] field:
id, sender_id, receiver_id, subject, message, created, ....
Now have to fill your database with example messages and then you can test like this:
for example User X is logged in and is now an object. You get users last 10 Messages like this:
$messages = new Message();
$messages->where_related_user('id', $user->id);
$messages->limit(10);
$messages->get();
you can get the reciever and sender of each message like this:
$messages = new Message();
$messages->include_related('sender');
$messages->include_related('receiver');
$messages->get();
Now print the name of each sender and receiver:
foreach($messages as $message):
echo $message->sender->name;
echo $message->receiver->name;
endforeach;

Categories