Trouble linking models together in CakePHP2 - php

I'm using CakePHP 2.6.7.
Here is my database (simplified) :
***Users***
id
username
***Cars***
id
CarMake
CarModel
NumberPlate
user_id
***Addresses***
id
address
postalcode
city
country
***Addresses_Users***
user_id
address_id
This is what I want :
One user can have multiple cars and one car is owned by only one user.
One user can have multiple addresses and one address can by owned by multiple users.
One car can have multiple addresses and one address can be owned by multiple cars.
So right now I have a model User.php :
<?php
class User extends AppModel {
public $hasMany = array(
'Car' => array(
'classname' => 'Car')
);
public $hasAndBelongsToMany = array(
'Address' => array(
'classname' => 'Address')
);
?>
And a model Car.php :
<?php
class Car extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'User'
)
);
public $hasMany = array(
'Address' => array(
'classname' => 'Address')
);
}
?>
And a model Address.php :
<?php
class Address extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'User'
),
'Car' => array(
'classname' => 'Car')
);
}
?>
I tried to link cars with addresses but I can't get it to work...
If I do a debug($this->User->find()); I have this result :
array(
'User' => array(
'id' => '1',
'username' => 'wblondel',
),
'Car' => array(
(int) 0 => array(
'id' => '2',
'CarMake' => 'Allard',
'CarModel' => '2005',
'NumberPlate' => '56QDS1',
'user_id' => '1',
),
(int) 1 => array(
'id' => '5',
'CarMake' => 'Alvis',
'CarModel' => '25',
'NumberPlate' => '5lDS1',
'user_id' => '1',
)
),
'Address' => array(
(int) 0 => array(
'id' => '2',
'address' => 'fghfghgf',
'postalcode' => '78200',
'city' => 'buchelay',
'country' => '40'
),
(int) 1 => array(
'id' => '3',
'address' => 'jkjkjk',
'postalcode' => '69100',
'city' => 'villeurbanne',
'country' => '59'
)
)
)
What I would firstly like is that in each car there is an array "address"...
Thanks!

From what you've described your associations look wrong. They should be something like:-
User
<?php
class User extends AppModel {
public $hasMany = array(
'Car'
);
public $hasAndBelongsToMany = array(
'Address'
);
}
Car
<?php
class Car extends AppModel {
public $belongsTo = array(
'User'
);
public $hasAndBelongsToMany = array(
'Address'
);
}
Address
<?php
class Address extends AppModel {
public $hasAndBelongsToMany = array(
'Car',
'User'
);
}
You don't need to use className unless the alias you want to use is different from the model name or the model belongs to a plugin.
Then if you use the Containable behavior you should be able to get everything back using something like:-
$this->User->find(
'all',
array(
'contain' => array(
'Address',
'Car' => array(
'Address'
)
)
)
);

To control what exactly is retrieved from a find() query, you can use the ContainableBehavior.
By the way I think you have an error in your Address model as an address doesn't "belong to" a user, but can have many ones. It should be an HABTM relation as you have done in the User model.

Related

Cakephp join query Belongs to

hello I have three tables in my database. Country, State,and City. In state table there is a country_id as foreign key and in city table there is state_id as foreign key. The problem is I want to get the country name when I am querying in the city table. I'll share the code so you can fully understand
Country.php
class Country extends AppModel{
public $useTable = 'countries';
}
State.php
class City extends AppModel{
public $useTable = 'cities';
public $belongsTo = array(
'State' => array(
'className' => 'State',
'foreignKey' => 'state_id',
'fields' => array('state.id','state.name')
)
);
}
City
class State extends AppModel{
public $useTable = 'states';
public $belongsTo = array(
'Country' => array(
'className' => 'Country',
'foreignKey' => 'country_id',
'fields' => array('Country.id','Country.name','Country.short_name')
)
);
okay here is the code. If I use this query
$this->State->find('all', array(
'conditions' => array(
'state.country_id' => $country_id
),
I can get the country name from country table. same is the case If I do this in city table like this
$this->City->find('all', array(
'conditions' => array(
'city.state_id' => $state_id
),
I can get all the state names city names here. So Now in this same table in one query how I can get the country name as well ?
Try using the recursive property.
Setting $this->City->recursive = 2 gets associated data for the table and associated data on the associated tables.
$this->City->recursive = 2;
$this->City->find('all', array(
'conditions' => array(
'city.state_id' => $state_id
),
//other stuff you use in this find
));
You can also using the "joins" flag.
$this->City->find('all', array(
'conditions' => array(
'City.state_id' => $state_id
),
'joins' => array(
array(
'table' => 'states',
'alias' => 'State',
'type' => 'left',
'conditions' => 'State.id = City.state_id'
),
array(
'table' => 'countries',
'alias' => 'Country',
'type' => 'left',
'conditions' => 'Country.id = State.country_id'
)
),
'fields' => array('City.id', 'State.name', 'Country.name',
//include all the fields you need
)
));
See: http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive
In addition to the first answer, make sure to contain the foreign key for the Country.
class City extends AppModel{
public $belongsTo = array(
'State' => array(
'className' => 'State',
'foreignKey' => 'state_id',
'fields' => array('State.id','State.name', 'State.country_id'), // Make sure to contain the foreign key for the `Country`.
)
);
}
For your information, you can also use ContainableBehavior instead of recursive.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html

Display a field from a 3rd table

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

Cakephp - contain (containable behavior) fetches too much

As I understood from the cakephp-documentation, one of the advantages of the 'containable'-Behavior is being able to fetch fewer data if you need fewer data...
But that doesn't seem to work in my case of a connection between users and usergroups.
My associations look like:
Group
hasMany: Membership
User
hasMany: Membership
Membership
belongsTo: User, Group
(I'm not using HABTM, instead use the Model 'Membership' in between to join users and groups).
All Models implement the 'Containable'-Behavior.
Now I want to get all the members of a group with a certain id, only their ids and mail-addresses. My query is built like that:
$members = $this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
),
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
));
But the resulting array looks like:
array(
(int) 0 => array(
'Membership' => array(
'id' => '1',
'group_id' => '1',
'user_id' => '1',
'role' => 'member'
),
'Group' => array(
'id' => '1',
'name' => 'Foo Group'
),
'User' => array(
'password' => '*****',
'id' => '1',
'name' => 'Dr. Foo'
'mail' => 'foo#bar.baz',
'role' => 'admin'
)
)
)
So there are definietely more fields fetched than I wanted to... (it's the same thing btw wenn I set the 'contain'-key to:
'contain' => array(
'User.fullName', 'User.id'
)
Am I using the containable-behavior wrong?
Your models don't seem to be acting containabl-y at all. Have you set your models to act as containable?
class Post extends AppModel {
public $actsAs = array('Containable');
}
If so, maybe the problem is with the recursion (to avoid getting the Group array with the query). Containable behavior should handle the recursion level on its own, but try setting it on the AppModel just to be sure
class AppModel extends Model {
public $actsAs = array('Containable');
public $recursive = -1;
Your first attempt
'contain' => array(
'User' => array(
'fields' => array('id', 'fullName')
),
)
looks good in terms of syntax, so it probably the actAs thing.
Also, for debugging also, try
$this->Membership->contain('User');
$this->Membership->find('all', array(
'conditions' => array(
'group_id' => $id
));
and see if you get the expected results that way.

Cannot save associated data with hasMany through (Join Model)

Hi, I am new to cakephp and doing a project on cakephp 2.3.4.
I have to associate product metal class through has many through association . But it doesn't seem to be working.
Model code
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
App::uses('AppModel', 'Model');
class MetalProduct extends AppModel {
public $belongsTo = array(
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id'
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id'
)
);}
My database table names are metal, products and metal_products
I have multiple select option for selecting more than one metal type.
This is how I get the the list of metals
$metals=$this->Metal->find('list');
$this->set(compact('metals'));
FormHelper code for listbox is
<?php echo $this->Form->input('Metal',array('type' => 'select',
'multiple' => true)); ?>
The product is getting saved successfully but the associations are not.
The debug array give me this
$message = array(
'Product' => array(
'category_id' => '517a514b-0eb0-4ec9-b018-0b948620d4f0',
'name' => 'mangalsutra Diamond',
'slug' => 'mangalsutra_diamond',
'description' => '1212',
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
'image' => '121212',
'price' => '12121',
'weight' => '12',
'active' => '1',
'category' => 'Mangalsutra'
)
)
I had put my head through walls but no clue why the associations are not getting saved.
The way they say in tutorials it seems easy, but why its not working?
I have doubts that its not saving because the metal array is passed like this
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
It should mention 'id''rather than (int) 0 or something.
Also, my database table for metal_products which I have created manually has
id(primary key)
metal_id(foreign key to Metal.id)
product_id(foreign key to Product.id)
Am I doing something wrong with naming conventions or the way database is created?
Please give me correct ans cause anything I tried from others answer is not working
I am saving it via
$this->Product->saveAll($this->request->data, array('deep' => true))
In your model, is all of that in your MetalProduct Model? If so you need to move
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Metal model and
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Product Model
Also add your belongsTo to each Model
I see you have a join table. Join tables are usually for HABTM associations. This is a HasMany. You need to use the other options available to define the foreign key relationships in each model as outlined above.
Please see the examples on the Cake Documentation.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
Also, if you are unsure of how to set up the models correctly, you can always use the Bake feature of Cakephp to generate the models and code for you in that regard.
book.cakephp.org/2.0/en/console-and-shells/code-generation-with-bake.html
If you are more of a visual learner this video tut should help you through the basics
http://www.youtube.com/watch?v=kJAMifqF5s8
The Relation i generated using Bake looks something like this
class Product extends AppModel {
/**
* hasAndBelongsToMany associations
*/
public $hasAndBelongsToMany = array(
'Metal' => array(
'className' => 'Metal',
'joinTable' => 'metals_products',
'foreignKey' => 'product_id',
'associationForeignKey' => 'metal_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
class Metal extends AppModel {
/** hasAndBelongsToMany associations */
public $hasAndBelongsToMany = array(
'Product' => array(
'className' => 'Product',
'joinTable' => 'metals_products',
'foreignKey' => 'metal_id',
'associationForeignKey' => 'product_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
/**
* MetalsProduct Model
* #property Product $Product
* #property Metal $Metal
*/
class MetalsProduct extends AppModel {
/* belongsTo associations */
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id',
'conditions' => '',
'fields' => '',
'order' => ''
));
}
This worked for me flawlessly.
Hope it works for all.

Not getting all the data I need for an association in CakePHP app

I have built an app in CakePHP where Users can be friends with other Users. A user has a profile and the friendships are linked between then user ids in a friends table (models further down)
In my FriendsController I pass a username and then use the following method to get Friends for a User: Note this method is in my User Model and called like: $this->User->getFriends($username); in my FriendsController!
function getFriends($username) {
//Start by getting User's ID
$user = $this->find(
'first',
array(
'conditions' => array(
'User.username' => $username
)
)
);
$profileAndFriends = $this->Profile->find(
'first', //Because User hasOne Profile, so we'll fetch this one profile.
array(
'conditions' => array(
'Profile.user_id' => $user['User']['id']
),
'contain' => array(
'UserFrom' => array(
'conditions' => array(
'status' => 1
)
),
'UserTo' => array(
'conditions' => array(
'status' => 1
)
)
)
)
);
return $profileAndFriends;
}
My User Model:
class User extends AppModel
{
public $name = 'User';
public $hasOne = 'Profile';
public $hasMany = array(
'UserFrom'=>array(
'className'=>'Friend',
'foreignKey'=>'user_from'
),
'UserTo'=>array(
'className'=>'Friend',
'foreignKey'=>'user_to'
)
);
}
and my Profile model:
class Profile extends AppModel
{
public $name = 'Profile';
public $belongsTo = 'User';
}
and the Friend model:
class Friend extends AppModel
{
var $name = 'Friend';
public $belongsTo = array(
'UserFrom'=>array(
'className'=>'User',
'foreignKey'=>'user_from'
),
'UserTo'=>array(
'className'=>'User',
'foreignKey'=>'user_to'
)
);
}
So in my view I have:
<?php foreach ($friends as $friend) : ?>
<?php echo $friend['UserTo']['firstname']; ?>
<?php endforeach; ?>
However it can't understand the firstname part! So...
If I do a debug on $friend in the view I get:
array(
'Friend' => array(
'id' => '1',
'user_from' => '6',
'user_to' => '8',
'created' => '0000-00-00 00:00:00',
'status' => '1'
),
'UserFrom' => array(
'password' => '*****',
'id' => '6',
'username' => 'driz',
'email' => 'cameron#driz.co.uk',
'status' => '1',
'code' => '6d446abefc2f1214e561da6f93470621',
'lastlogin' => '2012-04-23 15:22:04',
'created' => '0000-00-00 00:00:00'
),
'UserTo' => array(
'password' => '*****',
'id' => '8',
'username' => 'testperson',
'email' => 'test#testperson.com',
'status' => '1',
'code' => '7a794859b3a16dfc005dd7f80b9ac030',
'lastlogin' => '2012-03-18 09:28:24',
'created' => '0000-00-00 00:00:00'
)
)
So it's clearly an issue of the Linked Users I need to also contain there profiles... But how do I do that as If I put a contain inside the UserTo and UserFrom it throws an error saying they're not associated...
class User extends AppModel {
public $name = 'User';
public $hasOne = 'Profile';
public $belongsTo = array(
'Friend'=>array(
'foreignKey' => 'user_to'
)
);
}
class Profile extends AppModel {
public $name = 'Profile';
public $belongsTo = 'User';
}
class Friend extends AppModel {
public $name = 'Friend';
public $hasOne = 'User';
}
Now its possible to take user friends by:
$this->Friend->find("all", array(
'conditions'=>array(
'Friend.user_from'=>$user_id,
)
'contain'=>array(
'User'=>array(
'contain'=>'Profile'
)
)
));

Categories