CakePHP 3.2.11: Issue when saving hasMany association

Running into an issue when trying to save a hasMany association.

I have a table called ‘Workflows’ that hasMany ‘WorkflowObjects’:

Workflows Table:

$this->hasMany('WorkflowObjects', [
        'foreignKey' => 'workflow_id'
    ]);

WorkflowObjects table:

$this->belongsTo('Workflows', [
        'foreignKey' => 'workflow_id',
        'joinType' => 'INNER'
    ]);

I have an edit.ctp page where I have a form to edit the selected Workflow. In this form I’m creating a multiselect element where the user can select multiple WorkflowObjects:

echo $this->Form->input('workflow_objects._ids',
                            ['options' => $workflowObjects,
                             'class' => 'form-control height-200']);

My save function in my controller looks like this:

public function save(){
    $workflowId = $this->request->data['id'];

    if ($this->request->is('post')) {
       $workflow = $this->Workflows->get($workflowId, ['contain' => ['WorkflowObjects']]); 
       $this->Workflows->patchEntity($workflow, $this->request->data, ['associated' => ['WorkflowObjects']]);
       $this->Workflows->save($workflow);
    }

    $this->redirect(['action' => 'index', $workflowId]);
}

Every time it saves it creates a new entity of WorkflowObjects (i.e. new row in the database) instead of finding the existing record to associate.

Can anyone see any reason why or am I going about this all wrong?

Thank You

Why creating a save controller while the framework provides a save Table::save() function and besides the data manipulating functions are better declared in the Model class because that is where data logic are done.
From what you are talking about your edit action should be like this :

public Function edit($id) { $workflow = $this->workflows->get($id,['contain'=>['WorkflowObjects]]); if ($this->request->is('post')) { $this->Workflows->patchEntity($workflow, $this->request->data, ['associated' => ['WorkflowObjects']]); if ($this->Workflows->save($workflow)) { $this->Flash->success(__('Work Object where successfully saved')) } $this->redirect(['action' => 'index', $workflowId]); } }

Thanks for the reply but it doesn’t really address the issue. The code you’ve put in the edit function is the same as my save function. I agree putting it in the edit function is cleaner but in terms of what the save is doing, it doesn’t matter which function it’s in, the result is always the same.

In a hasMany/belongsTo relationship if I’m passing it an array of ‘_ids’ for an associated table, it always creates new entitys instead of seeing if that id already exists and retrieving/updating it.

If that’s the intended behavior then I’ll have to resort to a work around.

  1. Check the result after the patchEntity because that determines if the entity is new or not.
  2. Check if the WorkflowObjects model table has id as it primary key $this->primaryKey('id'); .

I will advise you and for others people face to same problem :
When you edit entity with association and you want upsert association and combine delete other entities not used you must prepare response such as :

  • Provide id or primary key in parameters for all entities you would only update value
  • Not pass id in parameters for new entity

Example parameters :

{
“id”: 187,
“name”: “Film A”,
“video_previews”: [ → define association
{
“id”: 33, → update
“link”: “upload/course/video_preview/600a3b06e0992.mp4”,
“name”: “nesting-validators”,
“thumbnail”: “upload/course/video_preview/600a3b06e099e.mp4”
},
{
→ insert
“link”: “upload/course/video_preview/600a3b06e0992.mp4”,
“name”: “nesting-validators”,
“thumbnail”: “upload/course/video_preview/600a3b06e099e.mp4”
}
],
}

Example code

$course = $this->patchEntity(
$this->get($courseId),
$data,
[
‘validate’ => false,
‘associated’ => [
‘VideoPreviews’ => [
‘validate’ => false
]
]
]
);

    $this->save($course, ['atomic' => false]);
1 Like