I'm having the hardest time with saving multiple records. I've tried a million things, but I end up with the same problem: my records are not saved and I can't see any errors. Bear in mind that I'm new to cakephp and a novice coder.
Am I missing something obvious and crucial?
Table:
$this->table('splits');
$this->displayField('id');
$this->primaryKey('id');
$this->belongsTo('Transactions', [
'foreignKey' => 'transaction_id',
'joinType' => 'INNER'
]);
$this->belongsTo('Accounts', [
'foreignKey' => 'account_credit_id',
'joinType' => 'INNER'
]);
Controller:
$splits = $this->Splits->newEntity();
if ($this->request->is('post')) {
$splits = $this->Splits->newEntities($this->request->data());
debug($splits);
foreach ($splits as $split){
$this->Splits->save($split);
}
}
$transactions = $this->Splits->Transactions->find('list', ['limit' => 200]);
$accounts = $this->Splits->Accounts->find('list', ['limit' => 200]);
$this->set(compact('split', 'transactions', 'accounts'));
$this->set('_serialize', ['split']);
Template:
echo $this->Form->input('Splits.1.transaction_id', ['options' => $transactions]);
echo $this->Form->input('Splits.1.amount', ['type' => 'float']);
echo $this->Form->input('Splits.1.account_id', ['options' => $accounts]);
echo $this->Form->input('Splits.2.transaction_id', ['options' => $transactions]);
echo $this->Form->input('Splits.2.amount', ['type' => 'float']);
echo $this->Form->input('Splits.2.account_id', ['options' => $accounts]);
echo $this->Form->input('Splits.3.transaction_id', ['options' => $transactions]);
echo $this->Form->input('Splits.3.amount', ['type' => 'float']);
echo $this->Form->input('Splits.3.account_id', ['options' => $accounts]);
Debug on $splits:
[
(int) 0 => object(App\Model\Entity\Split) {
(int) 1 => [
'transaction_id' => '108',
'amount' => '100.33',
'account_id' => '2'
],
(int) 2 => [
'transaction_id' => '108',
'amount' => '50.22',
'account_id' => '4'
],
(int) 3 => [
'transaction_id' => '108',
'amount' => '65.22',
'account_id' => '5'
],
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
(int) 1 => true,
(int) 2 => true,
(int) 3 => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[repository]' => 'Splits'
}
]
Did you somewhere saw this Table.index.field style being used, or did you just tried something and hoped it would work?
When saving many records respectively creating many entities, the expected format is a numerically indexed array that holds the data for the individual records, just as shown in the docs
Cookbook > Database Access & ORM > Saving Data > Converting Multiple Records
When creating forms that create/update multiple records at once you
can use newEntities():
[...]
In this situation, the request data for multiple articles should look
like:
$data = [
[
'title' => 'First post',
'published' => 1
],
[
'title' => 'Second post',
'published' => 1
],
];
So your inputs should not use the table name, but just the index and the field name, like
echo $this->Form->input('0.transaction_id', /* ... */);
echo $this->Form->input('0.amount', /* ... */);
echo $this->Form->input('0.account_id', /* ... */);
echo $this->Form->input('1.transaction_id', /* ... */);
echo $this->Form->input('1.amount', /* ... */);
echo $this->Form->input('1.account_id', /* ... */);
echo $this->Form->input('2.transaction_id', /* ... */);
echo $this->Form->input('2.amount', /* ... */);
echo $this->Form->input('3.account_id', /* ... */);
Try this:
$splits = TableRegistry::get('splits');
$entities = $splits->newEntities($this->request->data());
foreach ($entities as $entity) {
$splits->save($entity);
}
Try this example to insert multiple recored in cakphp 3.x
$passwords = $this->request->data('password_id');
foreach ($passwords as $password) {
$data = [
'event_id' => $this->request->data('event_id'),
'password_id' => $password
];
$eventPasswordAll = $this->EventPasswordAll->newEntity();
$this->EventPasswordAll->patchEntity($eventPasswordAll, $data);
$this->EventPasswordAll->save($eventPasswordAll );}
I hope it is useful to your question
please reviwe it, Thanks !
If you’d like to process all the entities as a single transaction you can use transactional():
Although using a loop, "transations" avoids errors if the "save" does not work.
// In a controller.
$articles->getConnection()->transactional(function () use ($articles, $entities) {
foreach ($entities as $entity) {
$articles->save($entity, ['atomic' => false]);
}
});
Converting Multiple Records - CakePHP 3
Related
In a edit-template, I created 2 forms. The first to edit a record from 'Table1' and a second form to add an record to the associated (belongsTo) table 'Table2'. I don't want to edit the associated record but add a new one in Table2 and change the bindingkey of the record from 'Table1'.
In the controller I use patchEntity(), but this way the original linked record in 'Table2' is edited.
The code for the second form look like this.
<?= $this->Form->create($machine, ['url' => ['action' => 'edit_foto']]);?>
<?= $this->Form->controls([
'id' => ['type' => 'hidden'],
'foto.omschrijving' => [
'type' => 'text',
'label' => __('naam van de machine'),
'value' => $result->type_machine
], []);?>
<?= $this->Form->button(__('save'), ['type' => 'submit']); ?>
<?= $this->Form->end(); ?>
In the controller for 'Table1' I use something like this.
public function editFoto($id) {
$data = $this->getRequest()->getData();
$machine = $this->Machines->get($id, [
'contain' => [
'Foto'
]
]);
if ($this->request->is([
'patch',
'post',
'put'
])) {
$machine = $this->Machines->patchEntity($machine, $data);
if ($this->Machines->save($machine)) {
$this->Flash->success(__('Machine {naam} has been saved.', [
'naam' => $machine['naam']
]));
return $this->redirect(['action' => 'edit', $id]);
}
}
}
How can I prevent the controller from editing the original record and instead force to add a new record in 'Table2' and change the bindingkey in 'Table1'?
Help is much appreciated.
added information:
the data send by the form looks like:
[
'id' => '87',
'foto' => [
'omschrijving' => 'WAKER 135 - ter - testje',
'foto' => [
'tmp_name' => 'C:\xampp\tmp\php6CFD.tmp',
'error' => (int) 0,
'name' => 'MAKITA DTD153RTJ accuschroevendraaier.jpg',
'type' => 'image/jpeg',
'size' => (int) 188971
],
'dir_id' => '5c9dd428cc4ef'
]
]
Instead of
$machine = $this->Machines->patchEntity($machine, $data);
$this->Machines->save($machine);
you should use
$machine = $this->Machines->newEntity($data, [
'associated' => ['Fotos']
]);
$this->Machines->save($machine);
to save the data. If the associations between Table1 and Table2 are correct in the models, this will also update the binding keys.
Make sure, the format of the ids in FormHelper is correct. See the docs, how to handle Saving With Associations. You can see this in $data. When submitting the form, $data should be in the following format:
$data = [
'id' => '...',
'foto' => [
// ... model data
],
];
I have a table that looks like this:
,====,==============,============,==========,
| id | contact_from | contact_to | message |
|====|==============|============|==========|
| 1 | 1 | 2 | some msg |
| 2 | 2 | 1 | reply |
'----'--------------'------------'----------'
I create a new row, doing this:
public function add()
{
$message = $this->Messages->newEntity();
if ($this->request->is('post') && $this->request->is('ajax')) {
$data = $this->request->getData();
$data['contact_to'] = (int)$data['contact_to'];
$data['contact_from'] = (int)$this->Auth->user('id');
$message = $this->Messages->patchEntity($message, $data);
if ($this->Messages->save($message)) {
echo json_encode(['status' => 'success']);
exit;
}
echo json_encode(['status' => 'error']);
exit;
}
}
And this is my hasOne association:
$this->hasOne('ContactFrom', [
'className' => 'Contacts',
'foreignKey' => 'id',
'bindingKey' => 'contact_from',
'joinType' => 'INNER',
'propertyName' => 'contact_from'
]);
$this->hasOne('ContactTo', [
'className' => 'Contacts',
'foreignKey' => 'id',
'bindingKey' => 'contact_to',
'joinType' => 'INNER',
'propertyName' => 'contact_to'
]);
As you can see, I pass an ID to a new row, however it saves everything, except the id's. When I debug($message) after the patchEntity call, it comes back like this:
object(App\Model\Entity\Message) {
'message' => 'asdfasdf',
'date_sent' => object(Carbon\Carbon) {},
'[new]' => true,
'[accessible]' => [
'contact_to' => true,
'contact_from' => true,
'message' => true,
],
'[dirty]' => [
'message' => true,
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[invalid]' => [],
'[repository]' => 'Messages'
}
It drops my ID's. I assume it's because I need to pass the Entity to it, but to save on db calls, how can I make it save the contact_to and contact_from id's to the table?
The names that you've choosen are causing a clash in the marshaller.
You cannot use the same name for the binding/foreign key and the property name, these two need to be different, as the former are ment to hold an identifier, and the latter is ment to hold either an entity, or an array that can be marshalled into an entity - neither of that applies to the value that you are passing, hence it will be discard.
You should ideally follow the CakePHP naming conventions, and append _id to your columns, ie name them contact_from_id and contact_to_id.
See also
Cookbook > CakePHP at a Glance > CakePHP Conventions > Database Conventions
I have a set of checkboxes with a belongs to many relationship. I couldnt use a multiple checkbox option for various reasons. The table Tutors has a belongsToMany relationship with subjects table. I get the correct checkboxes checked when it loads and when I update the checboxes I get the correct checkbox data returned but how do i save this? The docs say i need to put the id's in an array for the correct Subjects to be checked . I tried a few things but I couldnt save the data. I dont get an error, just not saving.
//view
foreach ($subjects as $key => $item) {
$sub=$item->name;
$val=0;
foreach ($tutor->subjects as $key2 => $item2) {
$sub2=$item2->name;
if ($sub==$sub2){
$val=1;
break;
}
}
echo $this->Form->hidden('subjects.'.$key.'.id', ['value'=>$item->id]);
echo $this->Form->input('subjects.'.$key.'.checkedvalue', ['label'=>$sub,
'checked'=> $val,'type'=>'checkbox']);
}
//controller
public function edittest5($id = null)
{
$tutor = $this->Tutors->get($id, [
'contain' => ['AvailabilityForTutors','Subjects']
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$tutor = $this->Tutors->patchEntity($tutor, $this->request->data(),['associated' => ['AvailabilityForTutors','Subjects']] );
// $tutor->dirty('availability_for_tutors', true);
debug($tutor);
$this->Tutors->save($tutor,[
// 'atomic'=>false,
'validate' => false,
'associated' => ['Subjects']
]);
return $this->redirect(['action' => 'edittest5',$id]);
}
$subjects = $this->Tutors->Subjects->find('all', ['limit' => 200]);
returned data
'first_name' => 'drew',
'subjects' => [
(int) 0 => [
'id' => '1',
'checkedvalue' => '1'
],
(int) 1 => [
'checkedvalue' => '0'
],
(int) 2 => [
'checkedvalue' => '0'
],
(int) 3 => [
'checkedvalue' => '0'
],
(int) 4 => [
'id' => '5',
'checkedvalue' => '1'
I need to create the upload process where the user can upload multiple files at once, using the file field with html5 multiple attr. Name of the file must be saved in the associated model.
I can run successfully upload one file and save the file name in the photos table, across the field:
echo $this->Form->file('photos.name');
But if I want to enable upload more photos with
echo $this->Form->input('title'); // post title
echo $this->Form->input('maintext'); // post main text,
... etc
echo $this->Form->file('photos[].name',['multiple'=>true]);
I get into the problem, and try to understand where I make mistakes, but without success.
PostsController:
public function add()
{
$post = $this->Posts->newEntity();
if ($this->request->is('post')) {
$post = $this->Posts->patchEntity($post, $this->request->data);
if ($this->Posts->save($post)) {
$this->Flash->success(__('The post has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The post could not be saved. Please, try again.'));
}
}
$this->set(compact('post'));
$this->set('_serialize', ['post']);
}
PostsTable:
$this->addBehavior('Upload');
$this->hasMany('Photos', [
'foreignKey' => 'post_id'
]);
UploadBehavior
All standard callbacks where I currently perform debug $data / $entity, but only in beforeMarshal i use:
$data = Hash::get($data,'name');
debug($data);
// debug output
[
'name' => 'HPIM3869.JPG',
'type' => 'image/jpeg',
'tmp_name' => 'C:\xampp\tmp\phpF02D.tmp',
'error' => (int) 0,
'size' => (int) 1295448
],
...
In beforeSave and afterSave
My form is OK, the data properly come in before Marshal method, if I upload 3 files, I also see the same number of debug outputs, but in beforSave and afterSave debug only show the first file like this:
debug($entity);
object(App\Model\Entity\Photos) {
'name' => [
'name' => 'HPIM3435.JPG',
'type' => 'image/jpeg',
'tmp_name' => 'C:\xampp\tmp\php5839.tmp',
'error' => (int) 0,
'size' => (int) 1517410
],
'post_id' => (int) 469,
'created' => object(Cake\I18n\Time) {
'time' => '2015-10-07T09:22:44+0200',
'timezone' => 'Europe/Berlin',
'fixedNowTime' => false
},
'modified' => object(Cake\I18n\Time) {
'time' => '2015-10-07T09:22:44+0200',
'timezone' => 'Europe/Berlin',
'fixedNowTime' => false
},
'[new]' => true,
'[accessible]' => [
'*' => true
],
'[dirty]' => [
'name' => true,
'post_id' => true,
'created' => true,
'modified' => true
],
'[original]' => [],
'[virtual]' => [],
'[errors]' => [],
'[repository]' => 'Photos'
}
Edit:
In the purpose of the test, I create such a form:
echo $this->Form->input('name',['value'=>'zzz']);
echo $this->Form->input('photos.0.name',['value'=>'zzz']);
echo $this->Form->input('photos.1.name',['value'=>'hhh']);
echo $this->Form->input('photos.2.name',['value'=>'fff']);
Also it only be saved the first result.
I need help to understand how to save multiple form data. Where I go wrong?
I think your field should be like this
echo $this->Form->file('photos.name.',['multiple'=>true]); //note dot notation at end of name. It will generate input name as array
help me to fix isPut or isPost for save logic, in the following code i can view the data in the from, but when i am trying to save it its not working, i have tried ispost and isput logic both are not working. i think problem is with controller sections not with view
here is view of my form,
<?php
echo $this->Form->create('Role',array('url'=>array('controller'=>'Organisations','action' => 'edit_profile'),'id' => 'role'));
echo $this->Form->input('RoleLanguage.rolename',array('label'=>'Profile Name:','id'=>'rolename'));
$options = array('A' => 'Approve', 'P' => 'Pending', 'D' => 'Delete');
echo $this->Form->input('Role.status', array(
'options'=>$options,
'empty' => false,
'label'=>'Status',
'style'=>'width:100px',
'id'=>'status'
));
$id= array('value' => $id);
//print_r($id);die();
echo $this->Form->hidden('rle_id', $id);
echo "<br>";
$options = array('R' => 'Role', 'P' => 'Position', 'T' => 'Team','C'=>'Core Strategic Profile');
echo $this->Form->input('Role.type', array(
'options'=>$options,
'empty' => false,
'label'=>'Type of Job Profile:',
'style'=>'width:100px',
'id'=>'type'
));
echo "<br>";
echo $this->Form->input('RoleLanguage.external_document_URL',array('label'=>'External Document URL:','id'=>'external_document_URL','type'=>'text'));
echo "<br>";
echo $this->Form->input('RoleLanguage.description', array('style'=>'width:420px','rows' => '5', 'cols' => '5','label'=>'Description','id'=>'description'));
?>
here is controller logic
function edit_profile($id=NULL)
{
$this->layout='Ajax';
//print_r($id);die();
$this->set('id',$id);
$this->Role->recursive = 0;
$this->Role->id = $id;
$language = $this->getLanguage('content');
$this->Role->unBindModel(array("hasMany" => array('RoleLanguage')));
$this->Role->bindModel(array("hasOne" => array('RoleLanguage'=> array('foreignKey' => 'rle_id', 'className' => 'RoleLanguage', 'type' => 'INNER', 'conditions' => array('RoleLanguage.language' => $language)))));
$this->data = $this->Role->read();
//print_r($this->data);die();
if ($this->RequestHandler->isPut())
{
$this->data=array(null);
$this->autoRender = false;
$acc_id = $this->activeUser['User']['acc_id'];
$this->data['Role']['acc_id'] = $acc_id;
unset($this->Role->RoleLanguage->validate['rle_id']);
print_r($this->data);die();
$this->Role->saveAll($this->data);
}
}
i am serializing data in another view from where i am calling the qbove view code for that is
$.ajax({
type: 'Put',
url: $('#role').attr('action'),
data: $('#role').serialize()
It could be that the data is failing the model validation test that occurs when you call saveAll.
Have you tried printing $this->Role->invalidFields() to see if there is anything there?