I came across this post that gave an answer on how to do this but it's not quite working for me.
I have a model called SitePage which has many SitePageGroup which in turn has many SitePageContent
// SitePage Model
public $hasMany = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_id',
'dependent' => FALSE,
),
);
// SitePageGroup Model
public $belongsTo = array(
'SitePage' => array(
'className' => 'FoCMS.SitePage',
'foreignKey' => 'site_page_id',
),
);
public $hasMany = array(
'SitePageContent' => array(
'className' => 'FoCMS.SitePageContent',
'foreignKey' => 'site_page_group_id',
'dependent' => FALSE,
),
);
// SitePageContent Model
public $belongsTo = array(
'SitePageGroup' => array(
'className' => 'FoCMS.SitePageGroup',
'foreignKey' => 'site_page_group_id',
),
);
Using the answer in that linked question I am seeing the parent model, SitePage being duplicated, but the associated models are being removed from the original and associated with the new one.
$record = $this->SitePage->find('first', array('condition' => array('SitePage.id' => $id)));
unset($record['SitePage']['id'], $record['SitePageGroup']['id'], $record['SitePageGroup']['SitePageContent']['id'] /* further ids */);
$this->SitePage->create();
$record['SitePage']['name'] = $record['SitePage']['name'].'-copy';
$record['SitePage']['friendly_name'] = $record['SitePage']['friendly_name'].' Copy';
if($this->SitePage->saveAll($record)){
$this->Session->setFlash('The site page has been saved', 'fo_message');
$this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash('The site page could not be saved. Please, try again.', 'fo_message');
}
Update
Debugging the record that I'm trying to reset I see the following
array(
'SitePage' => array(
'name' => 'test',
'friendly_name' => 'Test',
'order' => '82',
'created' => '2015-09-03 19:16:40',
'modified' => '2015-09-03 19:20:27'
),
'SitePageGroup' => array(
(int) 0 => array(
'id' => '55e88087-a4dc-4c37-89dc-f9c172b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-1',
'friendly_name' => 'Group 1',
'order' => '1',
'created' => '2015-09-03 19:16:55',
'modified' => '2015-09-03 19:16:55'
),
(int) 1 => array(
'id' => '55e8809e-d018-4ebe-a4cf-fbef72b40463',
'site_page_id' => '55e88078-16c8-46ce-bf02-fa5372b40463',
'name' => 'group-2',
'friendly_name' => 'Group 2',
'order' => '2',
'created' => '2015-09-03 19:17:18',
'modified' => '2015-09-03 19:17:18'
)
)
)
The way I am getting this result is by doing this
$sitePage = $this->SitePage->find('first', array(
'conditions' => array(
'SitePage.id' => $id,
),
));
unset($sitePage['SitePage']['id'], $sitePage['SitePageGroup']['id'], $sitePage['SitePageGroup']['SitePageContent']['id'], $sitePage['SitePageGroup']['site_page_id'], $sitePage['SitePageGroup']['SitePageContent']['site_page_group_id'] /* further ids */);
debug($sitePage);
die();
But also also as you can see in the debug output the 3rd level of associated models are not being included, each of the SitePageGroup should also have a SitePageContent
I think a simple loop over the array of SitePageGroup should reset the id's and set the foreign keys to null, but I guess I also need to somehow include the SitePageContent that belongs to the SitePageGroup so I can reset those as well.
You need to ensure that all primary and foreign keys are set to null before saving. You only appear to be resetting the primary keys of your models but Cake needs to know that the foreign keys need generating so that they reference the new records.
Before calling $record it might be worth using debug($record); to check that everything in that array has been set/reset appropriately to ensure the copy will work as expected.
Update
Based on the array contents you've posted in your updated question it appears that you are not removing all the primary and foreign keys from your save data. You need to make sure that these are removed from everything you are about to save including the has many associations.
If you look at your array you should be able to see that unset($sitePage['SitePageGroup']['id']) will not remove the primary IDs of your SitePageGroup data as what you are unsetting doesn't correspond to array paths in your $sitePage array.
You can use CakePHP's Hash utility to remove the primary keys from the array like this:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.id');
And similarly for the foreign keys:-
$sitePage = Hash::remove($sitePage, 'SitePageGroup.{n}.site_page_id');
Related
i have two models. Teacher and Subject joined by HABTM defined both ways. A teacher can teach many subjects and a subject can be taught by many teachers.my join table is subjects_teachers and have fields id,teacher_id and subject_id.
Fetching Teacher data from its model, i expect all teachers and their respective subjects ,
Fetching Subject data from its model i expect to also see the teachers teaching that particular subject
problem
on both instances, the associated model returns the correct number of records but the data is absent. i see [maximum depth reached] when i display the respective arrays.
I removed the id field from the join table and that fixed only the Teacher model.The Subject model still has the problem.
i just need to know what [maximum depth reached] means and why removing the id filed from the join table fixed the Teacher problem but not Subject.
also if its important i should mention that my Teacher model primary key field doesnt follow convention
SUBJECT model
public $hasAndBelongsToMany = array(
'Teacher' => array(
'className' => 'Teacher',
'joinTable' => 'subjects_teachers',
'foreignKey' => 'subject_id',
'associationForeignKey' => 'teacher_id',
'unique' => 'keepExisting'
)
);
Teacher Model
public $hasAndBelongsToMany = array(
'Subject' => array(
'className' => 'Subject',
'joinTable' => 'subjects_teachers',
'foreignKey' => 'teacher_id',
'associationForeignKey' => 'subject_id',
'unique' => 'keepExisting'
)
);
Results from subject
array(
(int) 0 => array(
'Subject' => array(
'id' => '1',
'subject_code' => '121',
'subject_name' => 'Mathematics',
'compulsory' => true
),
'Teacher' => array(
(int) 0 => array(
[maximum depth reached]
),
(int) 1 => array(
[maximum depth reached]
),
(int) 2 => array(
[maximum depth reached]
)
)
),
Results from Teacher before removin id field
array(
'Teacher' => array(
'teacher_id' => '6',
'first_name' => 'George',
),
'Subject' => array(
(int) 0 => array(
'id' => '1',
'subject_code' => '121',
'subject_name' => 'Mathematics',
'compulsory' => true,
'SubjectsTeacher' => array(
[maximum depth reached]
)
)
Results after removing id field
'Subject' => array(
(int) 0 => array(
'id' => '1',
'subject_code' => '121',
'subject_name' => 'Mathematics',
'compulsory' => true
),
Your data exists, the debugger just wont display it because the depth option limits it. Use debug() (default depth = 25) or Debugger::dump()/exportVar() with a depth (second argument, defaults to 3) high enough for your deeply nested data.
See also
http://book.cakephp.org/2.0/en/development/debugging.html
http://api.cakephp.org/2.5/class-Debugger.html
It is related to model attribute recursive
http://book.cakephp.org/2.0/en/models/model-attributes.html#recursive
how to get name of (UserTransactionType.name) with Transaction.who_pay_fee_1,2,3 fields.
'user_transaction_type_id' works well but how to get the rest of fields work :(
//Transaction Model
public $belongsTo = array(
'UserTransactionType' => array(
'className' => 'UserTransactionType',
'foreignKey' => 'user_transaction_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//UserTransactionType Model
public $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'user_transaction_type_id',
'dependent' => false,
))
This is the sample code for your controller:
$this->UserTransactionType->find('all',array(
'fields' => array('name'),
'contain' => array('Transaction')
)
);
If Models are associated you can specify in 'contain' which of them you want to get in the result.
If you want to have only some fields of related Model, you can determine them after 'Transaction' in 'contain' just like in the regular find() query:
'contain' => array('Transaction' => array('fields' => array('field_1',
'field_2') ))
But in your case, you don't need to specify fields, because by default you get all fields.
So no matter if you define or not fields "who_pay_fee_1,2,3" because if you use 'contain' by default you will get foreing_key - user_transaction_type_id.
I hope it's helpful
For people they like CakePhp :)
in Controller ->
get the list of 'UserTransactionType'
in View ->
after looping trough all the transactions; in Transaction Status column simply load the 'UserTransactionType'array and assign the number of array to $userTransactionTypes.
$userTransactionTypes[$transaction['Who_pay_fee_1']];
To be honest it was straight forward but needed a bit concentration :)
i have a two tables namely; histories and users. i need to display data like:
id | Username | Lastest created Post | First created Post
the data of id and username is from users table and the last created and first created post data is from histories. i need to view all the users, their lastest created post and their first created post. please help me to make controller and view thanks
Try below.
<?php
$users = $this->User->find('all',array
(
'conditions' => array
(
//conditions goes here
),
'fields' => array
(
'User.id',
'User.username',
'History.Lastest created Post',
'History.First created Post'
)
));
?>
Assume that relation between 'User' and 'History' table is One-to-One and there's a 'user_id' column in History table, you may need to specify relation between them in History model, for example:
var $hasOne = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
Then, you need to perform joins to do this. For example, somewhere in your User model, try something like this:
class User extends AppModel {
....
function getAllUsersHistory{
$allHistories = $this->find('all', array(
'joins' => array(
'table' => 'history',
'alias' => 'HistoryJoin'
'type' => 'INNER',
'conditions' => array(
// your conditions, for example: 'History.user_id' => 'User.id'
)
),
'fields' => array(
'User.id',
'User.username',
'History.lastest_created_post',
'History.first_created_post'
)
));
return $allHistories;
}
.....
}
This is one of those times when I know I'm doing something wrong, but I'm apparently deaf, dumb and blind with respect to seeing it. I'm hoping that another pair of eyes can open my own.
I have a ZipCode model and an Incentive model. There is a glorified join table (glorified because it has its own key) sitting in the middle. The join table has id, incentive_id and zip fields (legacy database). My ZipCode model HABTM Incentive as shown:
public $hasAndBelongsToMany = array(
'Incentive' => array(
'with' => 'ZipCodeIncentive',
'foreignKey' => 'zip',
'associationForeignKey' => 'incentive_id',
),
);
My Incentive model HABTM ZipCode as follows:
public $hasAndBelongsToMany = array(
'ZipCode' => array(
'with' => 'ZipCodeIncentive',
'foreignKey' => 'incentive_id',
'associationForeignKey' => 'zip',
),
I have a method, ZipCode::incentives( $zip ) that wants to pull all of the incentives relevant to the specified zip code:
$incentives = $this->Incentive->find(
'all',
array(
'contain' => array( 'ZipCode' ),
'fields' => array( 'Incentive.id', 'Incentive.name', 'Incentive.it_name', 'Incentive.amount', 'Incentive.state', 'Incentive.entire_state', 'Incentive.excluded', 'Incentive.is_active' ),
'conditions' => array(
'Incentive.excluded' => 0,
'Incentive.is_active' => 1,
'OR' => array(
'Incentive.state' => 'US', # nationwide incentives
array(
# Incentives that apply to the entire state the zip code belongs to
'Incentive.entire_state' => 1,
'Incentive.state' => $state,
),
'ZipCode.zip' => $zip # specific to the building's zip code
)
),
'order' => array( 'Incentive.amount DESC' ),
)
);
What I get for my trouble is the following error:
SQL Error: 1054: Unknown column 'ZipCode.zip' in 'where clause'
The ZipCode model's table isn't getting joined in the SQL, but I haven't grasped why yet. It's worth mentioning that the Incentive model is tied to a MySql view, not a table, via $useTable. I haven't seen anything to suggest that it should be a problem in this scenario, but it's non-standard.
If you see what I'm missing, please call 911 or at least drop an answer.
Rob
Move the condition
'ZipCode.zip' => $zip
To your contain declaration like this
array(
'contain' => array( 'ZipCode'=>
array('conditions'=>
array('ZipCode.zip' => $zip ))),
Then continue with the rest of your statement as usual
I'm trying to use on the fly associations to trim down the data I retrieve, but the model I'm using is associated to other models with a re-named field because I have 2 of the same models associated with it.
So, here's the model, say 'test', that has two 'user' fields, both related to the User model.
In the model:
var $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
),
'User_Watched' => array(
'className' => 'User',
'foreignKey' => 'user_id_watched'
)
);
When I retrieve data related to 'test', I want to only retrieve particular data linked to the 'User' and 'User_Watched' fields without any other nested information.
But when I do:
$this->User->unbindModel(array('hasMany' => array('something1', 'something2')), false);
then something1 and something2 data does not show up for the 'User' field of model 'test', but is still retrieved for the 'User_watched' field.
Can I not retrieve unwanted data for the 'User_watched' field?
Hope this makes sense... :)
KcYxA,
Containable behavior might help a lot in this case, as benjamin mentioned, your "find" queries would look like:
$this->User->find('first', array(
'conditions' => array('User.id' => $id),
'contain' => array('UserWatched')
));
In this case, you won't have to use unbindModel method. In this example, you'll get User and UserWatched data.
If you need only User data from "find", then tell Cake to "$this->User->contain();" so it won't go further then User model.
to use on the fly associations to trim
down the data I retrieve
Good idea.
'foreignKey' => 'user_id_watched'
should possibly be:
'foreignKey' => 'user_watched_id'.
Edit 1: At least this would make sense according to my current understanding. If user_id is a correct foreign key(FK), which cakephp uses to unbind the relations, but user_id_watched isn't, than your described behavior is explained.
Edit 2: The Containable behavior gives you another tool for controlling associated models.
Change $primaryKey in fly, run controller
Sample:
// Models
//....
class PreProductoDescripcion extends AppModel {
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_producto_descripcion';
//....
//....
}
class SenasaPedidosDetalles extends AppModel {
/**
* Display field
*
* #var string
*/
public $displayField = 'cod_tango';
public $belongsTo = array(
'SenasaPedidos' => array(
'className' => 'SenasaPedidos',
'foreignKey' => 'senasa_pedidos_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'PreProductoDescripcion' => array(
'className' => 'PreProductoDescripcion',
'foreignKey' => 'cod_tango',
//'conditions' => array('SenasaPedidosDetalles.cod_tango' => 'PreProductoDescripcion.codigo'),
'fields' => '',
'order' => ''
)
);
//....
#
// Controller Fly
//...
$this->SenasaPedidos->Behaviors->load('Containable');
$this->SenasaPedidos->SenasaPedidosDetalles->PreProductoDescripcion->primaryKey = 'codigo';
$datos = $this->SenasaPedidos->find(
'first', array(
'fields' => array( 'SenasaPedidos.*' ),
'conditions' => array( 'SenasaPedidos.id' => $id ),
'contain' => array(
'Usuarios' => array(
'fields' => array( 'Usuarios.apellido_nombre' )
),
'Clientes' => array(
'fields' => array( 'Clientes.razon_social' )
),
'Provincias' => array(
'fields' => array( 'Provincias.nombre' )
),
'Transportes' => array(
'fields' => array( 'Transportes.razon_social' )
),
'SenasaPedidosDetalles' => array(
'fields' => array( 'SenasaPedidosDetalles.*' ),
'PreProductoDescripcion' => array(
'fields' => array(
'PreProductoDescripcion.id_producto_descripcion',
'PreProductoDescripcion.descripcion'
)
)
),
)
));
//...