saving hasMany always add record instead of updating

286 Views Asked by At

I have a hasMany relationship (let's say Post hasMany Comments)

I want to edit both the Post and an existing Comment at the same time

My code

in my comment edit.ctp file I did

<?= $this->Form->create($post); ?>
<?= $this->Form->input('title'); ?> 
<?= $this->Form->input('title');  ?>
<?= $this->Form->input('text'); ?>
<?= $this->Form->input('comments.0.id'); ?>
<?= $this->Form->input('comments.0.text'); ?>

and in my PostsController

$post= $this->Posts->get($id);
$post= $this->Posts->patchEntity($post, $this->request->data, ['associated' => ['Comments']]);

My problem

now I expect that the comment will be updated, instead cake adds a new comment every time.

What did I do

I tried to debug $this->request->data and I got

[
    'text' => 'This is a test',
    'comments' => [
        (int) 0 => [
            'id' => '168',
            'text' => 'Comment test',

        ]
    ]
]

but if I debug $post I get

object(App\Model\Entity\Post) {

    /* ... */
    'comments' => [
        (int) 0 => object(App\Model\Entity\Comment) {

            'text' => 'Comment test',
            '[new]' => true,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [
                'text' => true
            ],
            /* ... */

        }
    ],
    /* ... */
    '[dirty]' => [
        'comments' => true,
    ],

}   

So why comment is marked as 'new' when I pass its id to the controller?

Of course this is an over simplified version of my actual situation. Maybe the problem it's not in the above code and i have to look elsewhere in my other code.

My question here is if I'm doing some basic approach error.

1

There are 1 best solutions below

4
On BEST ANSWER

You need to read the associated data into your entity in order to be able to patch it, otherwise the data won't be merged, but just marshalled, and thus ends up being handled as "new".

Automatically loading associated data only happens for the special _ids key, and when there is at least one entry in the association property, ie actually you don't need to have the associated data loaded that you want to patch, but there must be something in order for the marshaller to reach the point where data is being read and merged.

To be exact, without any data in the comments property, the marshaller will step out here

https://github.com/cakephp/cakephp/blob/3.2.8/src/ORM/Marshaller.php#L653-L655

I can't really tell whether there might be a bug, at the very least I guess the docs would need to be updated around this, as they would be a little misleading. While they do try to explain what happens when associated data is missing in the source entity, the shown example currently doesn't work, and they say that new entities would only be created for belongsTo and hasOne associations, which is incorrect.

Cookbook > Database Access & ORM > Saving Data > Patching HasMany and BelongsToMany

You may want to file an issue over at GitHub for clarification.

tl;dr

Long story short, contain the comments and you should be good.

$post = $this->Posts->get($id, [
    'contain' => ['Comments']
]);
// ...