updating related tables in cake - php

I have this:
$this->request->data['Person']['person_id'] = $this->Session->read('insertedPersonID');
$savedPerson = $this->Person->saveAll($this->request->data, array('validate'=>'first'));
which updates the selected person row fine however its related tables like PersonColor and PersonParts are not updating, instead inserting new rows.
I thought cake automatically updates the related tables as well as long as the id of the main table which is the foreign key for the other two tables are provided since doing this:
$savedPerson = $this->Person->saveAll($this->request->data, array('validate'=>'first'));
inserts to the Person table and the other two related two tables fine.
How do I make it update the other two tables as well?
Edit:
For the model relations:
Person Model:
public $hasMany = array(
'PersonParts' => array(
'className' => 'Part',
'foreignKey' => 'part_person_id'
),
'PersonColors' => array(
'className' => 'Color',
'foreignKey' => 'color_person_id'
)
);
Part Model:
public $belongsTo = array(
'PartPerson' => array(
'className' => 'Person',
'foreignKey' => 'part_person_id'
)
);
Color Model:
public $belongsTo = array(
'ColorPerson' => array(
'className' => 'Person',
'foreignKey' => 'color_person_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
edit 2
var_dump of $this->request->data
array(3){
["Person"]=>array(4){
["person_user_id"]=>string(1)"3"
["person_name"]=>string(9)"Britney"
["person_category_id"]=>string(2)"16"
["visibility"]=>string(1)"1"
["person_id"]=>string(1)"71"
}
["PersonParts"]=>array(1){
[0]=>array(3){
["part_name"]=>string(4)"hands"
["quantity"]=>string(1)"2"
["part_part_type_id"]=>string(1)"1"
}
}
["PersonColors"]=>array(2){
[0]=>array(4){
["color_name"]=>string(3)"blue"
["test_field1"]=>string(1)"8"
["test_field2"]=>string(1)"9"
["position"]=>int(1)
}
[1]=>array(2){
["color_name"]=>string(5)"red"
["position"]=>int(2)
}
}
}
Note: This var_dump is only showing ["person_id"]=>string(1)"71" under Person array as the added field to make cake do an update, not insert... person_id is not showing under the PersonParts and PersonColors here since it's not working that way. What should I pass or How should I do an update on the other 2 related tables?

To make Cake do an update on the related tables, you do need to pass in the id of the related record, otherwise Cake won't know which record to update.
You can make this a hidden field in your form, which is how I do things.
The var dump for PersonParts should look like this (note the extra 'part_id' field):
["PersonParts"]=>array(1){
[0]=>array(3){
["part_id"]=>string(1)"7"
["part_name"]=>string(4)"hands"
["quantity"]=>string(1)"2"
["part_part_type_id"]=>string(1)"1"
}
}
If you don't pass the id, then (from memory) Cake will delete the existing related records, and add new ones.

Related

getting data from a HABTM model

I just cant get data from 2 tables in a HABTM relationship and I cant get the answer for these cakephp docs (again). Sorry for asking the question on something that should be explained in the docs.
This should be a simple but I keep getting undefined index and the relationship I set up seems in accordance with the docs. I have another post on a more complicated matter but this issue needs to be isolated .
I dont know how to get related data from the lessons table and the students table via this lessons-students HABTM table.
I want all the lessons a student does. I want the name of the student from the student table and lesson details from the lesson table which has lesson_id as the common link in this lessons-students table. Sounds simple but I cant do it.
public $hasAndBelongsToMany = array(
'Student' => array(
'className' => 'Student',
'joinTable' => 'lessons_students',
'foreignKey' => 'lesson_id',
'associationForeignKey' => 'student_id',
'unique' => 'keepExisting',
)
);
class LessonsController extends AppController {
....
$this->Lesson->recursive = -1;
$options['joins'] = array(
array('table' => 'lessons_students',
'alias' => 'LessonsStudent',
'type' => 'LEFT',
'conditions' => array(
'Lesson.id = LessonsStudent.lesson_id',
)
),
array('table' => 'students',
'alias' => 'Student',
'type' => 'LEFT',
'conditions' => array(
'LessonsStudent.student_id=Student.id',
)
),
);
$options['conditions'] = array('Student.id' => 2);
// $Item->find('all', $options);
$student=$this->set( 'student',$this->Lesson->find('all', $options));
In the view I get the error undefined index Student
<?php
// debug($student);
foreach ($student as $item2):
echo '<td>'. $item2['Lesson']['id'].'</td>';
echo '<td>'. $item2['Student']['id'].'</td>';
http://stackoverflow.com/questions/17250215/cant-get-all-user-data-from-habtm-in-cakephp
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
If you want to use model association you need to remove $this->Lesson->recursive = -1; because it disable any associations defined in model. And than you can remove $options['joins'].
If you have defined many association in your Lesson model and don't want to fetch all of it use unbindModel() or use Containable behavior
$this->Lesson->Behaviors->load('Containable');
$this->Lesson->contain('Student');
If you need to do find many times in many actions it's better to enable Containable in model itself
class Lesson extends AppModel {
public $actsAs = array('Containable');
}
so you can avoid $this->Lesson->Behaviors->load('Containable'); line;

Link one model more than once

I have a function where a user can request a project. The request has 2 fields for other employees to be added.
It's got a field for a person who is responsible for the project (person_responsible) and the employee who is supposed to attend the opening meeting (person_attending).
What I want to know is, since both these fields (person_responsible and person_attending) will be pulling it's data from hr_employees table, how would I set this up in my Project-Model.
At the moment I have the one field set up like this:
public $belongsTo = array(
'HrEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'responsible_person',
'fields' => 'HrEmployee.employeename',
)
);
How would I set up the other field?
What I do in this cases is to make two associations. Since cake allow to customize relations, you can have two relations to the same model with different names.
public $belongsTo = array(
'ResponsibleEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'responsible_person',
'fields' => 'HrEmployee.employeename',
),
'AttendingEmployee' => array(
'className' => 'HrEmployee',
'foreignKey' => 'person_attending',
'fields' => 'HrEmployee.employeename',
)
);
Change the names to adjust your needs. Now, if your model is set as containable and you retrieve the Project model with those models in it, you'll get something like
array('Project' => array(/*data project*/),
'ResponsibleEmployee' => array(/*name*/),
'AttendingEmployee' => array(/*name*/)
)
(or another variation of that array depending on how the query was made).

CakePHP 1.3 - Unknown column in where clause

I'm working on an already existing cakephp 1.3 project and I needed to add a new table to the database. I have this in my controller:
$conditions = array('ShootingPlacement.person_id' => $id, 'Email.person_id' => $id, 'Email.shooting_placement_id' => 'ShootingPlacement.id');
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
And it's giving me this error:
Warning (512): SQL Error: 1054: Unknown column 'Email.person_id' in 'where clause' [CORE/cake/libs/model/datasources/dbo_source.php, line 684]
And ths is the query it's trying to create:
SELECT `ShootingPlacement`.`id`, ... FROM `shooting_placements` AS `ShootingPlacement`
LEFT JOIN `people` AS `Person` ON (`ShootingPlacement`.`person_id` = `Person`.`id`)
LEFT JOIN `shootings` AS `Shooting` ON (`ShootingPlacement`.`shooting_id` = `Shooting`.`id`)
WHERE `ShootingPlacement`.`person_id` = 123688 AND `Email`.`person_id` = 123688 AND `Email`.`shooting_placement_id` = 'ShootingPlacement.id'
ORDER BY `lastname` ASC
Obviously my controller code is wrong, but I'm not sure how to relate the Email table to the ShootingPlacement one. I think my models are correct. So far if I have this:
$conditions = array('ShootingPlacement.person_id' => $id);
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
It will retrieve the rows from Shooting, ShootingPlacement and Person, I want Email to be there too. Email has 2 foreign keys: one from ShootinPlacement and one from Person.
These are the models, the only one I created is Email, the rest where working correctly.
class Email extends AppModel
{
var $name = 'Email';
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id'
),
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'shooting_placement_id'
)
);
}
class ShootingPlacement extends AppModel
{
var $name = 'ShootingPlacement';
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id',
'order' => 'lastname ASC'
),
'Shooting' => array
(
'className' => 'Shooting',
'foreignKey' => 'shooting_id'
)
);
}
class Person extends AppModel
{
var $name = 'Person';
var $belongsTo = array
(
'PersonOrigin' => array
(
'className' => 'PersonOrigin',
'foreignKey' => 'person_origin_id'
)
);
var $hasMany = array
(
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'person_id',
'dependent' => false
)
);
}
class Shooting extends AppModel
{
var $name = 'Shooting';
var $belongsTo = array
(
'ShootingLocation' => array
(
'className' => 'ShootingLocation',
'foreignKey' => 'shooting_location_id'
),
'Emission' => array
(
'className' => 'Emission',
'foreignKey' => 'emission_id'
)
);
}
What I need on the view is to loop through the ShootingPlacement variable and I need it to contain the Email table data for that specific id of ShootingPlacement and Person (As you see in the query, Person and ShootingPlacement are in a relationship already, I only need there to be Email too)
You should be very careful with the relationship you're after. From a quick glance at some of these answers, they seem to suggest you simply add a join to the Email model into your Person model and rely on the conditions of your find to ensure your query doesn't ransack your server's memory.
I'm going to assume that first of all, you want this Email relationship to be implicit in all your queries on Person, otherwise you could simply specify the join on each query you wanted it for. In this case, you definitely want to link it using model relationships.
Your code shows that Shooting and ShootingPlacement (presume this is a model to model mapping relationship) both belong to two models. Incidentally, Shooting belongsTo Emission - which we haven't seen here yet. I assume this isn't applicable to the current scenario.
Now, let's assume off the bad that because your Email table has foreign keys, it will be a hasOne relationship, rather than a hasMany - so that's what you need to link it by. I'm going to link it to the ShootingPlacement model because this is the model you are querying, so it should be the central point at which models are joined around it. Structure wise, because everything seems to originate from your Person model, I would have to suggest you query that model instead. But the way it's set up so far will allow you to query from nearly anywhere and still retrieve mostly the same results bar a few model names and table aliases.
Purely because your foreign key between Email and ShootingPlacement has a different name, and CakePHP 1.3 doesn't handle this very well, I'm also going to suggest you don't use a foreign key, instead putting it into the relationship as conditions.
class ShootingPlacement extends AppModel
{
var $name = 'ShootingPlacement';
var $actsAs = array('Containable');
var $hasOne = array(
'Email' => array(
'className' => 'Email',
'foreignKey' => false,
'conditions' => array(
'Email.shooting_placement_id = ShootingPlacement.id',
'Email.person_id = ShootingPlacement.person_id'
)
)
);
var $belongsTo = array (
'Person' => array (
'className' => 'Person',
'foreignKey' => 'person_id',
'order' => 'lastname ASC'
),
'Shooting' => array (
'className' => 'Shooting',
'foreignKey' => 'shooting_id'
)
);
}
I've also added the containable behaviour in there. This allows you to control from each query which associated models you'd like to return with your primary model results. It will default to all, but can be handy when you only want something specific and/or for memory reasons (these kinds of queries can destroy your server memory pretty quickly if you don't limit them or specify only the field names you want to return).
Now when you create your Email model, I wouldn't suggest complicating this mess of entangled models any further by linking it back to ShootingPlacement again. As you've said, it also has a foreign key to the Person model. So you might want to do exactly the same thing as above for your Person model (changing the conditions to reflect the Person foreign key of course). This way your model is a little more flexible; it will still join to ShootingPlacement and Person, and will also allow you to query it seperately if required without the other associated models.
Documentation
CakePHP 1.3 Model Associations
CakePHP 1.3 Containable Behaviour
See also
This article on Stack
In your model add containable behavior
class Email extends AppModel {
var $name = 'Email';
var $actsAs = array('Containable');
var $belongsTo = array
(
'Person' => array
(
'className' => 'Person',
'foreignKey' => 'person_id'
),
'ShootingPlacement' => array
(
'className' => 'ShootingPlacement',
'foreignKey' => 'shooting_placement_id'
)
);
}
Just write the below code in your controller.
$this->ShootingPlacement->recursive = 2;
$this->ShootingPlacement->contain = array(
'Shooting',
'Person' => array(
'Email'
)
);
$conditions = array(
'ShootingPlacement.person_id' => $id,
'Email.shooting_placement_id' => 'ShootingPlacement.id'
);
$shootingPlacements = $this->ShootingPlacement->find('all', compact('conditions'));
Hope this helps you.
Add a $hasOne relation to Person model with Email like below
var $hasOne = array(
'Email' => array(
'className' => 'Email',
'foreignKey' => 'person_id' // Column defined for person ids in Email table
)
);
Then add
$this->ShootingPlacement->recursive = 2;
OR
you can simply use joins in cakephp to join email model. Refer cakephp joining tables
You need to link your model ShootingPlacement with "Email" with which you call it.
class ShootingPlacement extends AppModel
var $name = 'Shooting';
var $hasMany= array
(
'Email' => array
(
'className' => 'Email',
'foreignKey' => 'yourfk'
),
);
}
And uses it s very powerful ContainableBehavior !
exemple :
$contain=array('Email'=>array('fields'=>array('id','...')));
$conditions=array('ShootingPlacement.id'=>$yourId);
$this->ShootingPlacement->attachBehaviros('Containable');
$this->ShootingPlacement->find('all',$conditions);// your will retrieve yoru SHootingItem + Emails linked
This would provide the required join:
$conditions = array('ShootingPlacement.person_id' => $id, 'Email.person_id' => $id, 'Email.shooting_placement_id' => 'ShootingPlacement.id');
$joins = array(
array(
'table' => 'emails',
'alias' => 'Email',
'type' => 'LEFT',
'conditions' => array('Email.shooting_placement_id = ShootingPlacement.id')
)
);
$shootingPlacements = $this->ShootingPlacement->find('all',
array(
'conditions' => $conditions,
'joins' => $joins
)
);

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' => ''
)
);
}

CakePHP/Croogo: A veeery complex association

When I was working on my current project, I ran into a rather complex issue. I'll point out my problem much more detailed right now:
There are three Models: User, UsersExtendedField and UsersExtended.
UsersExtendedField contains custom fields that can be managed manually. All custom fields can be shown in the Users view as well, and be filled out of course. The values are then stored in UsersExtended, which has got two foreignKeys: user_id and field_id.
The relations look like this: User hasMany UsersExtendedField, UsersExtendedField hasMany UsersExtended, UsersExtended belongsTo User, UsersExtendedField.
The problem: When accessing the Users view, a form with user information input is shown. Any UsersExtendedFields are available as well, and since these hasMany UsersExtended, they've got plenty of UsersExtended values. But I want to reduce those to only the value(s) that belong to the User, whose view is shown at the moment. Here are my (desired) relations:
Croogo::hookBehavior('User', 'Crooboard.ExtendedUser', array(
'relationship' => array(
'hasMany' => array(
'UsersExtendedField' => array(
'className' => 'Crooboard.UsersExtendedField',
'foreignKey' => '',
'conditions' => array('status' => 1)
),
),
),
));
class UsersExtendedField extends AppModel {
var $name = 'UsersExtendedField';
var $displayField = 'fieldname';
var $hasMany = array(
'UsersExtended' => array(
'className' => 'Crooboard.UsersExtended',
'foreignKey' => 'field_id',
'conditions' => array(
'UsersExtended.user_id = User.id'
)
),
);
}
This is not the full code, these are the important parts. The problem starts right where I wrote 'UsersExtended.user_id = User.id'. Obviously, this won't work. But I do not have any idea how to access the User.id here. I also could not imagine a HABTM structure to solve this task. Do you have any idea how to get the semantics of this 'UsersExtended.user_id = User.id' to work?
Thank your very much for taking the time to read through this and helping me!
It sounds like you need to set up your HABTM relationship properly.
You already have the join table, UsersExtended, which contains your foreign keys.
Remove all previous relationships and set up HABTM in each of your User and UserExtendedField models.
The relationship code in your User model would look like this:
var $hasAndBelongsToMany = array(
'UsersExtended' => array(
'className' => 'UsersExtended',
'joinTable' => 'UsersExtended', //assuming this is the
//name of that model's table
'foreignKey' => 'user_id',
'associationForeignKey' => 'field_id'
)
);
For more information check out the page in the cakephp book
In addition, this blog post helped me grasp the relationship concepts when I was learning cakephp.

Categories