I am having problems with the CakePHP belongsTo and hasAndBelongsToMany relationships in CakePHP 2.x
Example situation
Table users
id
organisation_id
Table organisations
id
name
Table user_organisation_permissions
id
user_id
organisation_id
UserModel
hasAndBelongsToMany(Organisation);
belongsTo(Organisation)
A user belongs to one organisation but it has permissions on multiple organisations, resulting in the following conflict:
$aUser = $this->User->findById(1);
print_r($aUser);
// Output
# With the belongsTo relation
array(
'User' => array(
'id' => 1,
'organisation_id' => 1
'name' => 'Test User'
),
'Organisation' => array(
'id' => 1,
'name' => 'Test organisation'
)
);
# With the hasAndBelongsToMany relation
array(
'User' => array(
'id' => 1,
'organisation_id' => 1
'name' => 'Test User'
),
'Organisation' => array(
1 => array(
'id' => 1,
'name' => 'Test organisation'
),
2 => array(
'id' => 1,
'name' => 'Test organisation'
)
)
);
# When both relations are enabled it doesn't work
Does anybody have a solution for this conflict?
Is there a "native" CakePHP solution for this conflict?
The answer is actually in the CakePHP 2.x Cookbook.
Multiple relations to the same model
There are cases where a Model has more than one relation to another Model. For example, you might have a Message model that has two relations to the User model: one relation to the user who sends a message, and a second to the user who receives the message. The messages table will have a field user_id, but also a field recipient_id. Now your Message model can look something like:
class Message extends AppModel {
public $belongsTo = array(
'Sender' => array(
'className' => 'User',
'foreignKey' => 'user_id'
),
'Recipient' => array(
'className' => 'User',
'foreignKey' => 'recipient_id'
)
);
}
Source: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#multiple-relations-to-the-same-model
Related
Each Artikel has exactly one Barcode. For several reasons I want to split the Artikel model and the Barcode model. When I find() something from the artikel table it returns an array which contains the correct barcode section. But when I try to find a barcode, the array's artikel section is nulled.
This is what I mean:
// $this->Artikel->findById(102);
array(
'Artikel' => array(
'id' => '102',
'name' => 'Spätburgunder Spätlese Barrique',
'erzeuger_id' => '679',
'volumen_id' => '44',
'artikelgruppe_id' => '17'
),
'Barcode' => array(
'id' => '1',
'artikel_id' => '102',
'barcode' => '123456'
)
)
// $this->Barcode->findByBarcode(123456);
array(
'Barcode' => array(
'id' => '1',
'artikelnummer' => 'DE51076',
'barcode' => '123456'
),
'Artikel' => array(
'artikelnummer' => null, // this is null
'name' => null, // this is null as well
'erzeuger_id' => null, // also null
'volumen_id' => null, // ……
'artikelgruppe_id' => null // null
)
)
Any ideas what I did wrong?
These are the models
// Barcode.php
public $hasOne = array(
'Artikel' => array(
'className' => 'Artikel',
'foreignKey' => 'artikel_id'
)
);
// Artikel.php
public $hasOne = array(
'Barcode' => array(
'className' => 'Barcode',
'foreignKey' => 'artikel_id'
)
);
Article table - id, name, artikelgruppe_id and Barcode table - id, artikel_id, barcode
The correct way to relate these models is: Article hasOne Barcode and Barcode belongsTo Article
// Artikel.php
public $hasOne = array(
'Barcode' => array(
'className' => 'Barcode',
'foreignKey' => 'artikel_id'
)
);
// Barcode.php
public $belongsTo = array(
'Artikel' => array(
'className' => 'Artikel',
'foreignKey' => 'artikel_id'
)
);
Here article_id is in the Barcode table, so Article hasOne Barcode works as expected. Barcode hasOne Article would have worked if you had a barcode_id in your Article table.
But since you need the article from article_id field in Barcode table, you should use belongsTo relationship.
From what controller are you making those callings? If you are on the same model then is wrong because you must do the call through the references between the models. For example, I asume that you are in the Artikel model, so your first call is right, but the second one should be $this->Artikel->Barcode->findById(1)
It's probably a very basic problem, but I can't wrap my head around it how to solve this properly.
In my application, I've got users who have multiple addresses with different purposes. For example, a user can have an Address and an InvoiceAddress. I want to store both addresses in the same table (the Address model) and then assign them to one User.
The respective models look something like:
Address:
public $belongsTo = array(
'Address' => array(
'className' => 'Address',
),
'InvoiceAddress' => array(
'className' => 'Address',
),
);
And the User:
public $hasOne = array(
'Address' => array(
'className' => 'Address',
),
'InvoiceAddress' => array(
'className' => 'Address',
),
);
When a user would have one address, I'd add a user_id as foreign key to the Address model and be done with it. But obviously, something has to be done differently for CakePhp to be able to distinguish between the address and the invoice address.
In my mind the solution is something like adding a address_id and invoice_address_id to the User table, but I can't seem to get it working in any way.
If you want to add in your user table the related fields then it is better to add them in the following form address_user_id and the invoice_address_user_id. Then you must have a belongsTo relation in your user model that looks like:
public $belongsTo = array(
'Address' => array(
'className' => 'Address',
'foreignKey' => 'address_user_id',
),
'InvoiceAddress' => array(
'className' => 'Address',
'foreignKey' => 'invoice_address_user_id',
),
);
So when you execute the following command $this->User->find('all'); the result will be
$result = array(0 =>
array('User' => array(...),
'Address' => array(...), // it will bring only one address row
'InvoiceAddress' => array(...) // it will bring only one address row
),
1 => ...
2 => ...
);
Then in your address Model the relationships you must add are:
public $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'address_user_id',
),
'InvoiceUser' => array(
'className' => 'User',
'foreignKey' => 'invoice_address_user_id',
)
);
I am trying to display a field from a 3rd table in a relationship, and after checking posts here and the docs, I am still stuck.I have 3 models all related in some way. I have found similar posts here but I am still not getting it to work. I am learning so sorry if I have missed this somewhere in the docs but I have read a fair bit and have tried a lot. I am guessing too much now on this so I need help.
1)Tutorsession - belongsto teachers,
2) Teacher -has 1 user,hasmany tutorsessions
3) User- has 1 teacher, //////I want to display a field from this table given I display tutorsessions
In controller
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => 'User.username'
)
)
); //////////no error but no results
from view echo '<td>'. $item['User']['username'].'</td>'; ///////error user undefined
The tutorsession automatically gets rows from teacher table but not user table witht he model setup on a findall.
I want to display a username from the user table. I display the tutorsession table , I then can display the teacher table with the model association but I cant go from the teacher table to the user table to get a user name from the user id as the common field. I have checked the docs below and I am not sure why my code isnt ble to display a username from users table.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
update: here are the models
class User extends AppModel {
public $hasOne = array(
'Teacher' => array(
'className' => 'Teacher',
'dependent' => true
)
class Tutorsession extends AppModel
{
public $name='Tutorsession';
public $belongsTo = array(
'Teacher' => array(
'className' => 'Teacher',
'foreignKey' => 'teacher_id'
)
class Teacher extends AppModel
{
public $name='Teacher';
public $hasMany = array('Tutorsession');
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
Add containable behavior in your Model
public $actsAs = array('Containable');
Execute following query In controller
$contain = array('Teacher' => array('User'));
$this->set('tutor', $this->Tutorsession->find('first',array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => $contain
)));
Try this:
$this->Tutorsession->recursive = 2;
$this->Tutorsession->Teacher->contain('User');
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
)
);
$tutor=$this->Teacher->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
);
debug($tutor); exit();
Your result similar:
'Teacher' => array(
'id' => '1',
'name' => 'abc',
'created' => '1397040385',
'updated' => '1397725860'
),
'User' => array(
'id' => '4',
'name' => 'administrators',
'created' => '1397032953',
'modified' => '1397032953'
),
'Tutorsession' => array(
0=>array(
'id' => '3',
'name'=>'abc',
'created' => '1400729137'
),
1=>array(
'id' => '4',
'name'=>'abc',
'created' => '1400729137'
)
)
Ensure your models are correct
I am having a bit of a problem with saveAll, however I have a feeling what I am trying to do isn't possible but would be interested to see what people think.
The models in question are
League
Team
User
The relationships are as follows:
LEAGUE
One league can have many teams, however the league is owned by one user (the admin).
var $belongsTo = 'User';
var $hasMany = 'Team';
TEAM
Each team can be in one league and be owned by one user.
var $belongsTo = array('User', 'League' => array('counterCache' => true));
USER
Each user can be admin for multiple leagues and also have multiple teams
var $hasMany = array('League', 'Team');
I have a form where by a user can create a new league, create a team to add to the league and also register. The data that gets posted back to CakePHP is as follows:
array(
'League' => array(
'name' => 'Valid League Name',
'max_teams' => '10'
),
'Team' => array(
array(
'name' => 'Valid Team Name'
)
),
'User' => array(
'password' => 'password1',
'firstname' => 'Firstname',
'lastname' => 'Lastname',
'email' => 'test#example.com',
'confirm_password' => 'password1'
)
);
Now assuming this all passes validation and this is the first data entered in to the database I would expect the following to be returned from the saveAll operation that I am using:
array(
'League' => array(
'id' => 1,
'name' => 'Valid League Name',
'max_teams' => '10',
'user_id' => 1
),
'Team' => array(
array(
'id' => 1,
'name' => 'Valid Team Name',
'league_id' => 1,
'user_id' => 1
)
),
'User' => array(
'id' => 1,
'password' => 'password1',
'firstname' => 'Firstname',
'lastname' => 'Lastname',
'email' => 'test#example.com',
'confirm_password' => 'password1'
)
);
What is happening however is that the user_id field in the Team array isn't getting populated and it just coming back instead as null. What I am guessing is that CakePHP can't be sure that the team and the user that have just been created are necessarily related to one another so haven't created the association between the two.
At the moment, within the controller where the save is being made (I am saving through the League model $this->League->saveAll()) I am manually getting the user_id once everything has been saved and resaving the team with the new user_id. Is there anything I can do to get Cake to realise that the two are associated to the saveAll function does everything I need? I have tried using saveAll() with the deep option enabled and this also has no affect.
I have a database:
Companies | Users | Files
- ID - ID - ID
- company_id - user_id
So for my models:
Company hasmany User
User belongsto Company & hasmany File
File belongsto User
How can I list al the files belonging to a company? Without the user and company data? So a single array listing files.
I could fetch all the user IDs followed by a query in File searching with these IDs. But I was wondering, is there a best practice cakePHP way?
I'm fairly new to cake.
Thanks
As far as I know it's not possible to get company files without any user or company data, but you can limit the data by using the Containable behaviour. For example, if you want to get files that belong to a company in the company controller you could use this:
$users = $this->Company->User->find('all', array(
'conditions' => array('User.company_id' => $id),
'fields' => array('id', 'company_id'),
'contain' => array('File'),
));
The result ($users) would look something like this:
array(
0 => array(
'User' => array(
'company_id' => '75',
'id' => '51'
),
'File' => array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
)
...
)
)
1 => array(
'User' => array(
'company_id' => '75',
'id' => '65'
),
'File' => array(
0 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
)
)
You can then get an array of files using the Hash utility.
$files = Hash::extract($users, '{n}.File.{n}');
Which will give you something like this:
array(
0 => array(
'id' => '399',
'user_id' => '51',
...
),
1 => array(
'id' => '337',
'user_id' => '51',
...
),
3 => array(
'id' => '450',
'user_id' => '65',
...
),
...
)
Don't forget to enable the behaviour. I suggest you do it in your app model by adding the following line:
public $actsAs = array('Containable');
CakePHP will not help you on this and even if want to write mysql query you cant do this by using files table only until and unless you will add company_id in files table.