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
Related
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.
I have two tables:
users contains (id, name, email) id is the primary key.
users_details contains (user_id, address, city, postcode) user_id is the foreign key to user table.
The users.id=users_details.user_id.
I wrote user model like this in User.php
class User extends AppModel {
public $name = 'User';
public $displayField = 'name';
public $primaryKey = 'id';
var $belongsTo = 'UsersDetail';
public $hasone = array(
'UsersDetail' => array(
'className' => 'UsersDetail',
'conditions' => '',
'fields' => '',
'foreignKey' => 'id',
'order' => '',
'dependent' => true
)
);
}
I wrote the usersdetail model in UsersDetail.php
class UsersDetail extends AppModel {
public $name = 'UsersDetail';
public $displayField = 'name';
public $belongsTo = array( 'User' =>
array( 'className' => 'User','foreignKey' => 'user_id') );
public $hasone = array(
'User' => array(
'className' => 'User',
'conditions' => '',
'fields' => '',
'foreignKey' => 'id',
'order' => '',
'dependent' => true
));
}
I want to get the data from table using the join with condition. The users.id=users_details.user_id.
You have declared two relationships for each model association, when you should only have one going each way.
Like so:
In the userDetail Model:
public $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
And consequently, in the User Model:
public $belongsTo = array(
'UserDetail' => array(
'className' => 'UserDetail',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Check out the documentation for more details.
user has (id, name, email) id is the primary key.
users_details has(user_id, address, city, postcode) user_id is the foreign key to user table. Means.
Point to remember:
one to one relation - hasOne - A user table has one users_detail.
belongto - A userdetails table belongs to User table.
So, the relation between the two tables is hasone and belongsto.
find the right code below.
User model:
public $hasOne = array('UsersDetail' => array(
'className' => 'UsersDetail',
'foreignKey' => 'user_id')
);
UsersDetail model
public $belongsTo = array('User' => array(
'className' => 'Users',
'foreignKey' => 'id',
'conditions' => '',
'fields' => '',
'order' => '')
);
which provide the join precisely. You can add the conditions, order in collections.
The output query
SELECT `User`.`id`, `User`.`first_name`, `User`.`last_name`, `User`.`email`, `User`.`mobile`, `User`.`pswd`, `UsersDetail`.`user_id`, `UsersDetail`.`address`, `UsersDetail`.`city`, `UsersDetail`.`state`, `UsersDetail`.`postcode`, `UsersDetail`.`dob`, `UsersDetail`.`photo` FROM `cakedemo`.`users` AS `User` LEFT JOIN `cakedemo`.`users_details` AS `UsersDetail` ON (`UsersDetail`.`user_id` = `User`.`id`) WHERE 1 = 1
Thank you,
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
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.
Hi,
I need to do the following query using the CakePHP find method:
SELECT *
FROM `messages`
INNER JOIN users ON messages.from = users.id
WHERE messages.to = 4
ORDER BY messages.datetime DESC
Basically I have:
messages table with a Message model
users table with User model
and want to retrieve information from both tables in one query. The users.id field is the same as the messages.from field, so that's what the join is on.
I am doing it in my MessagesController so it would need to be something like:
$this->Message->find();
Thanks
There are two main ways that you can do this. One of them is the standard CakePHP way, and the other is using a custom join.
It's worth pointing out that this advice is for CakePHP 2.x, not 3.x.
The CakePHP Way
You would create a relationship with your User model and Messages Model, and use the containable behavior:
class User extends AppModel {
public $actsAs = array('Containable');
public $hasMany = array('Message');
}
class Message extends AppModel {
public $actsAs = array('Containable');
public $belongsTo = array('User');
}
You need to change the messages.from column to be messages.user_id so that cake can automagically associate the records for you.
Then you can do this from the messages controller:
$this->Message->find('all', array(
'contain' => array('User')
'conditions' => array(
'Message.to' => 4
),
'order' => 'Message.datetime DESC'
));
The (other) CakePHP way
I recommend using the first method, because it will save you a lot of time and work. The first method also does the groundwork of setting up a relationship which can be used for any number of other find calls and conditions besides the one you need now. However, cakePHP does support a syntax for defining your own joins. It would be done like this, from the MessagesController:
$this->Message->find('all', array(
'joins' => array(
array(
'table' => 'users',
'alias' => 'UserJoin',
'type' => 'INNER',
'conditions' => array(
'UserJoin.id = Message.from'
)
)
),
'conditions' => array(
'Message.to' => 4
),
'fields' => array('UserJoin.*', 'Message.*'),
'order' => 'Message.datetime DESC'
));
Note, I've left the field name messages.from the same as your current table in this example.
Using two relationships to the same model
Here is how you can do the first example using two relationships to the same model:
class User extends AppModel {
public $actsAs = array('Containable');
public $hasMany = array(
'MessagesSent' => array(
'className' => 'Message',
'foreignKey' => 'from'
)
);
public $belongsTo = array(
'MessagesReceived' => array(
'className' => 'Message',
'foreignKey' => 'to'
)
);
}
class Message extends AppModel {
public $actsAs = array('Containable');
public $belongsTo = array(
'UserFrom' => array(
'className' => 'User',
'foreignKey' => 'from'
)
);
public $hasMany = array(
'UserTo' => array(
'className' => 'User',
'foreignKey' => 'to'
)
);
}
Now you can do your find call like this:
$this->Message->find('all', array(
'contain' => array('UserFrom')
'conditions' => array(
'Message.to' => 4
),
'order' => 'Message.datetime DESC'
));
Otro example, custom Data Pagination for JOIN
CODE in Controller CakePHP 2.6 is OK:
$this->SenasaPedidosFacturadosSds->recursive = -1;
// Filtro
$where = array(
'joins' => array(
array(
'table' => 'usuarios',
'alias' => 'Usuarios',
'type' => 'INNER',
'conditions' => array(
'Usuarios.usuario_id = SenasaPedidosFacturadosSds.usuarios_id'
)
),
array(
'table' => 'senasa_pedidos',
'alias' => 'SenasaPedidos',
'type' => 'INNER',
'conditions' => array(
'SenasaPedidos.id = SenasaPedidosFacturadosSds.senasa_pedidos_id'
)
),
array(
'table' => 'clientes',
'alias' => 'Clientes',
'type' => 'INNER',
'conditions' => array(
'Clientes.id_cliente = SenasaPedidos.clientes_id'
)
),
),
'fields'=>array(
'SenasaPedidosFacturadosSds.*',
'Usuarios.usuario_id',
'Usuarios.apellido_nombre',
'Usuarios.senasa_establecimientos_id',
'Clientes.id_cliente',
'Clientes.consolida_doc_sanitaria',
'Clientes.requiere_senasa',
'Clientes.razon_social',
'SenasaPedidos.id',
'SenasaPedidos.domicilio_entrega',
'SenasaPedidos.sds',
'SenasaPedidos.pt_ptr'
),
'conditions'=>array(
'Clientes.requiere_senasa'=>1
),
'order' => 'SenasaPedidosFacturadosSds.created DESC',
'limit'=>100
);
$this->paginate = $where;
// Get datos
$data = $this->Paginator->paginate();
exit(debug($data));
OR Example 2, NOT active conditions:
$this->SenasaPedidosFacturadosSds->recursive = -1;
// Filtro
$where = array(
'joins' => array(
array(
'table' => 'usuarios',
'alias' => 'Usuarios',
'type' => 'INNER',
'conditions' => array(
'Usuarios.usuario_id = SenasaPedidosFacturadosSds.usuarios_id'
)
),
array(
'table' => 'senasa_pedidos',
'alias' => 'SenasaPedidos',
'type' => 'INNER',
'conditions' => array(
'SenasaPedidos.id = SenasaPedidosFacturadosSds.senasa_pedidos_id'
)
),
array(
'table' => 'clientes',
'alias' => 'Clientes',
'type' => 'INNER',
'conditions' => array(
'Clientes.id_cliente = SenasaPedidos.clientes_id',
'Clientes.requiere_senasa = 1'
)
),
),
'fields'=>array(
'SenasaPedidosFacturadosSds.*',
'Usuarios.usuario_id',
'Usuarios.apellido_nombre',
'Usuarios.senasa_establecimientos_id',
'Clientes.id_cliente',
'Clientes.consolida_doc_sanitaria',
'Clientes.requiere_senasa',
'Clientes.razon_social',
'SenasaPedidos.id',
'SenasaPedidos.domicilio_entrega',
'SenasaPedidos.sds',
'SenasaPedidos.pt_ptr'
),
//'conditions'=>array(
// 'Clientes.requiere_senasa'=>1
//),
'order' => 'SenasaPedidosFacturadosSds.created DESC',
'limit'=>100
);
$this->paginate = $where;
// Get datos
$data = $this->Paginator->paginate();
exit(debug($data));
$services = $this->Service->find('all', array(
'limit' =>4,
'fields' => array('Service.*','ServiceImage.*'),
'joins' => array(
array(
'table' => 'services_images',
'alias' => 'ServiceImage',
'type' => 'INNER',
'conditions' => array(
'ServiceImage.service_id' =>'Service.id'
)
),
),
)
);
It goges to array is null.