I am creating a simple API with CakePHP 4, and I am having some issues saving some POST data. This is a new project, so the models are newly baked, based on my 3 tables: elaborations, details, jobs
The ElaborationTable table has these relations:
$this->hasOne('Details', [ // this (and the next) is the only thing that I've manually changed here: hasOne instead of hasMany
'foreignKey' => 'elaboration_id',
]);
$this->hasOne('Jobs', [
'foreignKey' => 'elaboration_id',
]);
While both DetailsTable and JobsTable have this:
$this->belongsTo('Elaborations', [
'foreignKey' => 'elaboration_id',
'joinType' => 'INNER',
]);
In my controller, this is what I'm using to retrieve the POST data and save it to each database table:
$data = $this->request->getData();
$data['user_id'] = $this->Auth->user('id');
$elaboration = $this->Elaborations->newEntity($data, [
'associated' => [
'Details',
'Jobs'
]
]);
Finally, my POST data is something like this:
APP/Controller/ElaborationsController.php (line 85)
[
'detail' => [
'regione' => 'Lazio',
'provincia' => 'RM',
'comune' => 'Roma',
],
'job' => [
'serramenti' => '1',
],
'user_id' => (int) 2,
]
Now, the user_id field gets saved in my elaborations table, no errors are issued, but still the details and jobs tables remains empty, no data is saved.
What am I doing wrong?
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 created a table called Delegates, and I created a UsersTable and User Entity. And I have used $this->setTable('delegates'); in UsersTable to be able to access Delegates from $this->Users; (I just want to say I have created a User Model with delegates table)
So far so good...
In my application I am trying to access deep associations. Every thing is fine with this query but when I contain the User model I get The Users association is not defined on Comments.
I can confirm the associations are set correctly.
...
// There are more associations up there.
...
'Comments', [
'className' => 'App\Model\Table\CommentsTable',
'foreignKey' => 'delegate_assessment_criteria_id',
'belongsTo' => [
'Assessors' => [
'className' => 'App\Model\Table\AssessorsTable',
'foreignKey' => 'assessor_id',
],
'Users' => [
'className' => 'App\Model\Table\UsersTable',
'foreignKey' => 'delegate_id',
]
]
]
Here is deep association.
...
// There are more associations up there.
...
'Comments' => function($q) {
return $q
->select([
'id'
])
->order(['Comments.created DESC'])
->contain([
'Assessors' => function($q) {
return $q
->select([
'id'
])
->enableAutoFields();
}
])
->contain([
'Users' => function($q) {
return $q
->select([
'id',
'delegate_id'
])
->enableAutoFields();
}
])
->enableAutoFields();
}
2 Notes:
If I contain the User model in very first in hierarchy of my query I
can access the User fields but in deep association this doesn't
work.
If I contain Delegates it works.
I believe there is a problem with Cakephp query builder.
Alright finally I figured it out. I have done it before I don't know why I forgot. Probably because I was in deep association I drained into it.
The deep association contain was creating conflict with very first contain. if I would set different propertyName in deep associations then it does the purpose.
Bear in mind you must set this associations on your Table Models.
'Comments', [
'className' => 'App\Model\Table\CommentsTable',
'foreignKey' => 'delegate_assessment_criteria_id',
'belongsTo' => [
'Assessors' => [
'className' => 'App\Model\Table\AssessorsTable',
'foreignKey' => 'assessor_id',
],
'Delegates' => [ // <= Here set the name of Assossiation you want to be shown in array
'className' => 'App\Model\Table\UsersTable',
'propertyName' => 'delegates', // <= Add propertyName and set another name here
'foreignKey' => 'delegate_id',
]
]
]
And on association
'Comments' => function($q) {
return $q
->select([
'id'
])
->order(['Comments.created DESC'])
->contain([
'Assessors' => function($q) {
return $q
->select([
'id'
])
->enableAutoFields();
}
])
->contain([
'Users' => function($q) {
return $q
->select([
'id',
// 'delegate_id' // <= Remove this - We have already done it when we set the association on above code.
])
->enableAutoFields();
}
])
->enableAutoFields();
}
I have a Shops table which can have Products. Other Shops can have the same Products so I used a belongsToMany relation table ShopsProducts.
I can add an infinite number of Products to a Shop and remove them by saving the Shop entity including the relation.
All works fine, but if I want to unlink all Products from a Shop in my form and save, the relation is of cause empty so the Shop will always have 1 Product that I can't delete over the relation but only directly.
This is what the request looks like from the Shops form with a Product
data => [
'name' => 'some',
'is_active' => '1',
'slug' => 'some',
'product_id' => '',
'products' => [
(int) 5 => [
'id' => '5',
'_joinData' => [
'priority' => '0'
]
]
],
]
And this is the request without
data => [
'name' => 'some',
'is_active' => '1',
'slug' => 'some',
'product_id' => '',
]
What is the cake way to handle this issue?
What's your save strategy?
https://book.cakephp.org/3.0/en/orm/saving-data.html#saving-belongstomany-associations
Try replace instead of append.
If that doesn't work for you, check in the before() or afterSave() if products->get('products') is empty, if it is manually call a deleteAll() on the join table for that product an shop.
You will need to set your association to [] to blank it out.
$shops->products = [];
$shops->setDirty('products ', true);
I have a table name countries , countries has relation with users, users has relation with MoneyTransferTransactions
In MoneyTransferTransactions view I need fetch country name.
I already join Users table in MoneyTransferTransactionsTable by below code
$this->belongsTo('Users', [
'foreignKey' => 'user_id',
'joinType' => 'INNER'
]);
In user table I also used inner join like below code
$this->belongsTo('Countries', [
'foreignKey' => 'country_id',
'joinType' => 'INNER'
]);
In MoneyTransferTransactions controller I have used below code to fetch all data with associated data.
$this->paginate = [
'contain' => ['Users','TransferOptions'],
'conditions'=> ['MoneyTransferTransactions.status'=>1],
'order' =>['MoneyTransferTransactions.id'=>'DESC']
];
I have used var_dump in index.ctp , I gotted country id from users table but not got country name from countries table. How can I get country name from MoneyTransferTransactions>index.ctp ?
Add association between Users and Countries in 'contain'
$this->paginate = [
'contain' => ['TransferOptions', 'Users' => ['Countries']],
'conditions' => ['MoneyTransferTransactions.status' => 1],
'order' => ['MoneyTransferTransactions.id' => 'DESC']
];
I use BlueImp jQuery plugin to upload multiple images in follow scenario:
User open add or edit Albums page, select images and procces upload via ajax method. This work fine, image data (name, size, model, field,..) are stored in related images table without foreign key.
Ajax return id of recent uploaded image, and js create (captions, position) inputs for that file.
User fill other form fields, and click submit.
Problem, if user create new album or edit/update exist one wich not contain images, application does not update foriegn key for new images. But if album contain images, and user add new one to album, after save post cakephp add / update foreign key to new images records.
Debug output:
// first time add images, does not update FK
[
'name' => 'A4',
'images' => [
(int) 116 => [
'caption' => '',
'position' => '1',
'id' => '116'
]
],
'id' => '4',
'user_id' => '1',
'active' => '1'
]
// album has one image, we add new one, CakePHP Update FK for new images
[
'name' => 'A4',
'images' => [
(int) 117 => [
'caption' => '',
'position' => '1',
'id' => '117'
],
(int) 118 => [
'caption' => '',
'position' => '2',
'id' => '118'
]
],
'id' => '4',
'user_id' => '1',
'active' => '1'
]
AlbumsController add method:
public function add()
{
$album = $this->Albums->newEntity();
if ($this->request->is('post')) {
$album = $this->Albums->patchEntity($album, $this->request->data,['associated' => ['Images']]);
if ($this->Albums->save($album)) {
$this->Flash->success(__('The album has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The album could not be saved. Please, try again.'));
}
}
$users = $this->Albums->Users->find('list', ['limit' => 200]);
$this->set(compact('album', 'users'));
$this->set('_serialize', ['album']);
}
Albums Table:
$this->hasMany('Images', [
'foreignKey' => 'foreign_key',
'conditions' => [
'Images.model' => 'Albums',
'Images.field' => 'upload'
],
'sort' => ['Images.position' => 'ASC']
]);
Images Table:
$this->belongsTo('Albums', [
'foreignKey' => 'foreign_key',
'joinType' => 'INNER'
]);
I use same approach in the cakephp 2 apps, and work without problems.
Video: http://screencast-o-matic.com/watch/cDhYYOinCX
Q: How to update foreignKey?
I'm not sure is this correct way, but it's work for me.
AlbumsTable
public function afterSave(Event $event, Entity $entity, ArrayObject $options)
{
if (isset($entity)) {
$images = TableRegistry::get('Images');
$images->query()
->update()
->set(['foreign_key' => $entity->id])
->where([
'foreign_key IS NULL',
'model' => 'Albums'
])
->execute();// conditions
}
}