Cakephp updating associated inputs creates duplicated records - cakephp

I have two tables that are related strategies and strategy_conditions. Strategies can have many strategy_conditions.
I have updated the strategies add.php with some javascript so that I can add strategy_conditions to the same page etc. All works great.
I want to be able to edit the strategies_conditions at the same time on the strategies edit.php I can loop over and add the correct associated strategies_conditions inputs with the their values but my issue is that rather than update the strategies_conditions it creates new records. Strange because cakephp recognises the details because it has the right values in the inputs.
here is the cakephp code inside strategies edit.php template
<?php foreach ($strategy->strategies_conditions as $key=>$strategiesConditions) : ?>
<tr>
<td>
<?php echo $this->Form->control('strategies_conditions.'.$key.'.name', array( 'label' => false )); ?>
</td>
</tr>
<?php endforeach; ?>

If you want to edit associated records, then you must also supply the primary key for those records.
For the parent the primary key is not necessarily needed, as it is usually being passed in the URL (ie /strategies/edit/1), and used to obtain the parent entity that is being patched with the form data.
For the associated records however there is no such data anywhere else, so you need to explicitly pass it alongside the rest of the record's data, so that the marhshaller can identify and patch the existing entities/records, otherwise you will end up with new entities without primary key, eg new records.
Assuming the primary key is id:
echo $this->Form->hidden('strategies_conditions.' . $key . '.id');
echo $this->Form->control('strategies_conditions.' . $key . '.name', array(
'label' => false
));
See also
Cookbook > Database Access & ORM > Saving Data > Patching HasMany and BelongsToMany
Cookbook > Views > Helpers > Form > Creating Inputs for Associated Data
Cookbook > Views > Helpers > Form > Creating Hidden Inputs

Related

CakePHP relations in Database

I baked two controllers users and moves. Now I want to link the moves the users is linked to (only one). The bake did most of the work for me (thank god).
<td><?= $user->has('move') ? $this->Html->link($user->move->name, ['controller' => 'Moves', 'action' => 'view', $user->move->id]) : '' ?></td>
It actually show nothing. I have a foreign key in my database and the move 1 is correctly linked to the user.
foreign key in users "move_id" - primary key in the moves is "id"
I get no error and also no debug call. Any ideas?
It's showing nothing because in your ternary operator, it's executing this next portion >>>>>>>
: ''
and therefore prints a blank. Your $user object probably doesn't have a field called "move".
You need to check the following:
Are associations defined for these two models?
In your controller, where you're fetching this $user before doing $this->set(.....), did you mention "contain"? Since you need to access the Users as well as the Moves models?
For example:
// If you're trying to find all users records
$users = $this->Users->find('all')
->contain(['Moves']);
// For a single user record
$users = $this->Users->get($this->Auth->user("id"))
->contain(['Moves']);
Hope this helps.
Peace! xD

"Record not found in table with primary key [NULL]" error in cakephp 3 association form

I'm stuck on how to save a BelongsToMany/Through association through the parent form.
I have a ProjectsTable that belongsToMany Characters through ProjectCharacters.
I have a CharactersTable that belongsToMany Projects through ProjectCharacters.
My projects/edit/# page contains a second form after the regular projects/edit form that looks like this:
<?= $this->Form->create($project_character); ?>
<?php
echo $this->Form->input('character_id', ['options' => $character_options]);
echo $this->Form->hidden('project_id', ['value' => $project['id']]);
?>
<?= $this->Form->button(__('Add Character')) ?>
<?= $this->Form->end() ?>
This is just a project id and character id that I want to create a new record for in ProjectCharacters. but when I press save, I get the following error:
Record not found in table "projects" with primary key [NULL]
What do I need to do to save a ProjectCharacter association through this form?
I would say your problem is here:
https://gist.github.com/sarahkeefe/a42d39efade836a675c8#file-projectscontroller-php-L156
You are trying to validate the ownership of a project for an action that does not receive any arguments (the add function). You need to conditionally execute that code or to always allow your add() action to your users.
In the future, you can look at the error page, it has a stack trace, which is a list of functions that got called before your error happened. They usually indicate the place where your error can be found.

cakephp saving data for a related model's related model (not typo)

I've run into a bit of a problem saving data in cake php.
here are the models/relationships.
District hasMany Departments
Department hasMany Groups
I am in a view for creating new district, in which I've allowed the user to create multiple new departments. while creating each department, the user may create multiple groups for that dept. Now the trouble is I'm unsure of how to save the group data.
for each department that is created on the fly, im using the multiple index method for the inputs (i.e. "Department.0.name", Department.0.type) so this will be a cinch to save using the saveAll method. However, for each group that is created, i will need a department_id, and since none of the District's departments have yet been saved, they don't have an id. how can i save this new district's data, saving the new departments, and their associated new created groups? is there a way that i can address the name attribute of the group inputs that will create the proper association, something like "Department.0.Group.0.name", for instance?
Thanks in advance!!! if anything is unclear, please don't hesitate to say so, I'll be glad to rephrase.
What does your POST data array look like?
<?php
debug($this->data);
?>
If it is not in the correct format, the associated models won't get saved.. Cake knows to grab the "lastInsertId()" of the models which haven't been saved yet, so you don't have to worry about those... What i'm not sure about, and the docs don't really go into, is how deep the save goes. The example provided is as follows:
$this->data =
Array
(
[Article] => Array
(
[title] => My first article
)
[Comment] => Array
(
[0] => Array
(
[comment] => Comment 1
[user_id] => 1
)
[1] => Array
(
[comment] => Comment 2
[user_id] => 2
)
)
)
$this->Article->saveAll($this->data);
This is the correct structure (cakephp 1.3) for saving associated models of a 'hasMany' relationship, but i'm not sure if it goes any deeper than one child.
One thing that comes to my mind is to build the array according to the format above, but leave the parent model out. Then manually save the parent model data, grab the ::getLastInsertId(); then do a saveAll on departments and groups.
[UPDATE]
I just tested your theory and it will work the way you intend.
<?php
echo $this->Form->input('Department.0.Group.0.name');
?>
Will produce:
<input name="data[Department][0][Group][0][name]" type="text" id="Department0Group0name">
[UPDATE 2]
I did some exploring in lib/Cake/Model/Model.php and found this:
<?php
...
public function saveAssociated($data = null, $options = array()) {
...
... // code omitted.
...
if ($options['deep']) { // This will recurse infinitely through all associations
$saved = $this->{$association}->saveAssociated($values, array_merge($options, array('atomic' => false)));
}
...
...
... // code omitted.
...
?>

CakePHP: Retrieve multiple records from a single model to be edited in one form

Good day,
I have a model called ProjectRequirement which belongsTo Project, so Project hasMany ProjectRequirements
When I created the ProjectRequirement entries, I made use of this method:
<?php
echo $this->Form->inputs(array(
'legend => false,
'fieldset' => false,
'ProjectRequirement.1.description' => ...
'ProjectRequirement.2.description' => ...
'ProjectRequirement.3.description' => ...
));
?>
I did this so that I could make use of the saveMany() method to save multiple records at the same time. However, when I want to edit these records again on the same form, I cannot see, to be able to do it. I have kept the same field naming structure and tried to set the data as follows:
<?php
$this->request->data = $this->ProjectRequirement->find('all', array('conditions' => ...));
?>
A pr(); shows that the records are being returned, but they are not populating the form fields. If I remove the numbers and just have a single field like this:
<?php
echo $this->Form->inputs(array(
'legend => false,
'fieldset' => false,
'ProjectRequirement.description' => ...
));
?>
It works fine. How can I set the data so that multiple records from ProjectRequirement are set on multiple inout fields? Or can't I?
To reiterate: I do NOT have a problem saving multiple records, I have a problem retrieving multiple records to display.
Regards,
Simon
When you create the form to SAVE many fields, you name the fields like this:
ProjectRequirement.1.name...
ProjectRequirement.2.name...
ProjectRequirement.3.name...
...
However, when retrieving data, it is not the same scenario, and the general rules of arrays and how indexing works are applied here. So simply changing my fields to be like this:
ProjectRequirement.0.name...
ProjectRequirement.1.name...
ProjectRequirement.2.name...
...
Worked because I was only testing with one record in the database, and that row would have been at index 0, not 1.

CakePHP HABTM: Editing one item casuses HABTM row to get recreated, destroys extra data

I'm having trouble with my HABTM relationship in CakePHP.
I have two models like so: Department HABTM Location. One large company has many buildings, and each building provides a limited number of services. Each building also has its own webpage, so in addition to the HABTM relationship itself, each HABTM row also has a url field where the user can visit to find additional information about the service they're interested and how it operates at the building they're interested in.
I've set up the models like so:
<?php
class Location extends AppModel {
var $name = 'Location';
var $hasAndBelongsToMany = array(
'Department' => array(
'with' => 'DepartmentsLocation',
'unique' => true
)
);
}
?>
<?php
class Department extends AppModel {
var $name = 'Department';
var $hasAndBelongsToMany = array(
'Location' => array(
'with' => 'DepartmentsLocation',
'unique' => true
)
);
}
?>
<?php
class DepartmentsLocation extends AppModel {
var $name = 'DepartmentsLocation';
var $belongsTo = array(
'Department',
'Location'
);
// I'm pretty sure this method is unrelated. It's not being called when this error
// occurs. Its purpose is to prevent having two HABTM rows with the same location
// and department.
function beforeSave() {
// kill any existing rows with same associations
$this->log(__FILE__ . ": killing existing HABTM rows", LOG_DEBUG);
$result = $this->find('all', array("conditions" =>
array("location_id" => $this->data['DepartmentsLocation']['location_id'],
"department_id" => $this->data['DepartmentsLocation']['department_id'])));
foreach($result as $row) {
$this->delete($row['DepartmentsLocation']['id']);
}
return true;
}
}
?>
The controllers are completely uninteresting.
The problem:
If I edit the name of a Location, all of the DepartmentsLocations that were linked to that Location are re-created with empty URLs. Since the models specify that unique is true, this also causes all of the newer rows to overwrite the older rows, which essentially destroys all of the URLs.
I would like to know two things:
Can I stop this? If so, how?
And, on a less technical and more whiney note: Why does this even happen? It seems bizarre to me that editing a field through Cake should cause so much trouble, when I can easily go through phpMyAdmin, edit the Location name there, and get exactly the result I would expect. Why does CakePHP touch the HABTM data when I'm just editing a field on a row? It's not even a foreign key!
From the CookBook the 1st problem is:
By default when saving a
HasAndBelongsToMany relationship, Cake
will delete all rows on the join table
before saving new ones.
I am not quite sure why Cake is trying to save the HABTM data even though you don't have a foreign key in your data, but there is an easy solution for that. Simply destroy the association for the save call:
$this->Location->unbindModel(
array('hasAndBelongsToMany' => array('Department'))
);
I'm thinking of one reason why this might be happening. When you retrieve Location, you also retrieve locations_departments data. And when you do a save($this->data) it looks for models in the array and saves them.
A way to solve this is setting the recursive attribute (of a model) to -1 or 0 (try, I'm not sure, just print out the data to see what comes out). You can set it in the model: var $recursive = -1; or in the controller method (action): $this->ModelName->recursive = -1;
More about recursive: http://book.cakephp.org/view/439/recursive
It's really similar to what harpax suggested, just if you don't need that data, tell it to Cake, so that it won't fetch it.
Trouble is that when saving your Location, you gave the save method an array containing all the DepartmentsLocations too. Thus CakePHP destroys everything and try to recreate it.
This is a common mistake with cake since it will often pull far too many results for you.
Be sure to pass only the data that needs to be saved, or better to fetch only the datas you need.

Resources