I'm having trouble modifying the logic for saving one of my associated table. The two tables are:
In the Clients Controller, the method edit(), classic, baked with cakephp, with the associated table in addition:
$client = $this->Clients->get($id, [
'contain' => ['ClientMetadatas']
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$client = $this->Clients->patchEntity($client, $this->request->data);
if ($this->Clients->save($client, ['associated' => ['ClientMetadatas']])) {
$this->Flash->success(__('The client has been saved.'));
} else {
$this->Flash->error(__('The client could not be saved. Please, try again.'));
$this->response->statusCode(500);
$client=$client->errors();
}
}
$this->set(compact('client'));
$this->set('_serialize', ['client']);
An example of how I edit a client and its metadatas would be, calling '/clients/edit/clientid.json'
{
"firstname": "John",
"firstname": "Smith",
"client_metadatas":
[
{
"name": "test",
"value": "test"
}
]
}
Here's my problem: I'd like to verify if there is no metadata existing with this client_id and metadata's name. If it exists, I'd perform an update rather than an insert.
It seems that the ideal solution would be to do it in the Model directly.
I tried to use the callback methods:
Any advice on how to properly do the verification and the switch between insert/update before saving the entity?
PS: I hope no information in missing. If it the case, don't hesitate, I'll add them...
Thank you !
CakePHPs ORM can do that on its own out of the box. Whether an update or an insert is being performed, is figured based on the persistence status of the entity that is to be saved, ie based on the return value of Entity::isNew()
.
Quote from the Table::save()
API docs:
This method will determine whether the passed entity needs to be inserted or updated in the database. It does that by checking the
isNew
method on the entity.
In order to have this being set up properly automatically, you have to provide the possible existing primary key values of those records in your data, so that the marshaller can prepare the entities properly when patching in the request data.
So where/however you are preparing that data, make sure that you include the primary key value(s) in case they exist, so that you send something like this:
{
"firstname": "John",
"firstname": "Smith",
"client_metadatas":
[
{
"id": 1,
"name": "existing",
"value": "existing"
},
{
"name": "non-existing",
"value": "non-existing"
}
]
}
The first set would be an update (given that a record with such an id
primary key value exists), the second one would be an insert.
Surely it's also possible to handle things on your own, ie without passing the primary key values in the request data, at least as long as there is another unique column. Searching for other, custom criterias, like the name of the record, and then modifying the entity accordingly so that the respective row is being updated, should work fine.
You could do that in the beforeSave
callback/event, or even in the beforeMarshall
callback/event, depending on when you want to this to apply, something along the lines of:
public function beforeSave(
\Cake\Event\Event $event,
\Cake\Datasource\EntityInterface $entity,
\ArrayObject $options
)
{
if ($entity->isNew()) {
$existing = $this
->find()
->where([
'name' => $entity->get('name')
])
->first();
if ($existing) {
$entity->set('id', $existing->get('id'));
$entity->isNew(false);
}
}
return true;
}