Saving with Associations

I understand that by default, the save() method will save one level of associations
https://book.cakephp.org/4/en/orm/saving-data.html#saving-with-associations

However, I have a situation where associated data is updated but no saved.

My model Form28s has these associations

        $this->hasMany('Form28Instructors', [
            'dependent' => true,
            'foreignKey' => 'form28_id'
        ]);

        $this->hasMany('MemberBadges', [
            'dependent' => true,
            'foreignKey' => 'form28_id'
        ]);

When a MemberBadge is saved, in the afterSave I call a function that updates the Form28 and associated data

$this->Form28s->updatePoints($entity->form28_id);

The updatePoints function will load the Form28 associated with the badge

        $form28 = $this->get($id, [
            'contain' => [
                'Badges',
                'MemberBadges',
                'Form28Instructors'
            ]
        ]);
        {LOGIC CUT OUT TO CALCULATE AND UPDATE POINTS}
       
        // Save Form 28 and Associated Tables
        $this->save($form28);

The points field in the Form28 updates correctly, however the Form28Instructors do not have their assigned points updated. I’ve checked and the entity is updated, but not saved. I see the fields in the entity are updated, just not saved

  form28_instructors: array:1 [â–Ľ
    0 => &5 App\Model\Entity\Form28Instructor {#1136 â–Ľ
      #_accessible: array:9 [â–Ľ
        "created" => true
        "modified" => true
        "created_by" => true
        "modified_by" => true
        "form28_id" => true
        "member_id" => true
        "instructor_efficiency_points" => true
        "form28" => true
        "member" => true
      ]
      #_fields: array:8 [â–Ľ
        "id" => 22
        "created" => Cake\I18n\FrozenTime @1622330265 {#1117 â–¶}
        "modified" => Cake\I18n\FrozenTime @1622575642 {#1103 â–¶}
        "created_by" => 2
        "modified_by" => 2
        "form28_id" => 10
        "member_id" => 3
        "instructor_efficiency_points" => 5.0
      ]
      #_original: array:1 [â–Ľ
        "instructor_efficiency_points" => 0
      ]
      #_hidden: []
      #_virtual: []
      #_dirty: array:1 [â–Ľ
        "instructor_efficiency_points" => true
      ]
      #_new: false
      #_errors: []
      #_invalid: []
      #_registryAlias: "Form28Instructors"

You should show all the debug output for $form28, it might hold important information. The cut code might have valuable info too, as not every way of assigning data is equal. Also, evaluate the return value of the save() call, and additionally debug $form28 again after that call.

Also make sure that the schema cache is up to date, ie clear it (bin/cake schema_cache clear).

Thanks, I’ve checked the schema but no change Debug output is below

App\Model\Entity\Form28 Object
(
    [id] => 10
    [created] => Cake\I18n\FrozenTime Object
        (
            [date] => 2017-09-02 14:45:24.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [modified] => Cake\I18n\FrozenTime Object
        (
            [date] => 2023-04-23 04:31:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [created_by] => 
    [modified_by] => 2
    [file_number] => TE145
    [unit_id] => 198
    [badge_id] => 1
    [workbook_reference] => 
    [planned_exam_date] => 
    [actual_exam_date] => 
    [submit_date] => Cake\I18n\FrozenDate Object
        (
            [date] => 2017-09-02 00:00:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [dispatch_date] => 
    [complete_date] => Cake\I18n\FrozenDate Object
        (
            [date] => 2017-11-06 00:00:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [examiner_id] => 3
    [supervisor_id] => 
    [form28_status_id] => 2
    [instructor_efficiency_points] => 5
    [squadron_trophy_points] => 1
    [notes] => 
    [form28_instructor_count] => 2
    [member_badge_count] => 2
    [member_badge_count_pass] => 1
    [member_badge_count_fail] => 0
    [form28_instructors] => Array
        (
            [0] => App\Model\Entity\Form28Instructor Object
                (
                    [id] => 22
                    [created] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2021-05-29 23:17:45.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [modified] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2023-04-23 01:56:59.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [created_by] => 2
                    [modified_by] => 2
                    [form28_id] => 10
                    [member_id] => 3
                    [instructor_efficiency_points] => 3
                    [[new]] => 
                    [[accessible]] => Array
                        (
                            [created] => 1
                            [modified] => 1
                            [created_by] => 1
                            [modified_by] => 1
                            [form28_id] => 1
                            [member_id] => 1
                            [instructor_efficiency_points] => 1
                            [form28] => 1
                            [member] => 1
                        )

                    [[dirty]] => Array
                        (
                            [instructor_efficiency_points] => 1
                        )

                    [[original]] => Array
                        (
                            [instructor_efficiency_points] => 5
                        )

                    [[virtual]] => Array
                        (
                        )

                    [[hasErrors]] => 
                    [[errors]] => Array
                        (
                        )

                    [[invalid]] => Array
                        (
                        )

                    [[repository]] => Form28Instructors
                )

            [1] => App\Model\Entity\Form28Instructor Object
                (
                    [id] => 25
                    [created] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2023-04-23 03:52:59.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [modified] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2023-04-23 03:59:07.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [created_by] => 2
                    [modified_by] => 2
                    [form28_id] => 10
                    [member_id] => 6
                    [instructor_efficiency_points] => 3
                    [[new]] => 
                    [[accessible]] => Array
                        (
                            [created] => 1
                            [modified] => 1
                            [created_by] => 1
                            [modified_by] => 1
                            [form28_id] => 1
                            [member_id] => 1
                            [instructor_efficiency_points] => 1
                            [form28] => 1
                            [member] => 1
                        )

                    [[dirty]] => Array
                        (
                            [instructor_efficiency_points] => 1
                        )

                    [[original]] => Array
                        (
                            [instructor_efficiency_points] => 1
                        )

                    [[virtual]] => Array
                        (
                        )

                    [[hasErrors]] => 
                    [[errors]] => Array
                        (
                        )

                    [[invalid]] => Array
                        (
                        )

                    [[repository]] => Form28Instructors
                )

        )

    [member_badges] => Array
        (
            [0] => App\Model\Entity\MemberBadge Object
                (
                    [id] => 21
                    [created] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2017-11-06 12:21:18.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [modified] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2023-04-23 04:31:28.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [created_by] => 3
                    [modified_by] => 2
                    [member_id] => 11
                    [badge_id] => 1
                    [form28_id] => 10
                    [practical_book_mark] => 
                    [scrap_book_mark] => 
                    [questionnaire_mark] => 
                    [exam_mark] => 100
                    [honours] => 1
                    [rpl_awarded] => 
                    [issuance_status_id] => 1
                    [date_of_issue] => Cake\I18n\FrozenDate Object
                        (
                            [date] => 2017-11-06 00:00:00.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [authority_card_number] => TASE178/17
                    [eligible_for_points] => 1
                    [missing_badge_id] => 
                    [notes] => My Notes.
                    [total_marks] => 100
                    [age_at_issue] => 5.45
                    [[new]] => 
                    [[accessible]] => Array
                        (
                            [created] => 1
                            [modified] => 1
                            [created_by] => 1
                            [modified_by] => 1
                            [member_id] => 1
                            [badge_id] => 1
                            [form28_id] => 1
                            [practical_book_mark] => 1
                            [scrap_book_mark] => 1
                            [questionnaire_mark] => 1
                            [exam_mark] => 1
                            [honours] => 1
                            [rpl_awarded] => 1
                            [issuance_status_id] => 1
                            [date_of_issue] => 1
                            [authority_card_number] => 1
                            [eligible_for_points] => 1
                            [missing_badge_id] => 1
                            [notes] => 1
                            [member] => 1
                            [badge] => 1
                            [form28] => 1
                            [issuance_status] => 1
                        )

                    [[dirty]] => Array
                        (
                        )

                    [[original]] => Array
                        (
                        )

                    [[virtual]] => Array
                        (
                            [0] => total_marks
                            [1] => age_at_issue
                        )

                    [[hasErrors]] => 
                    [[errors]] => Array
                        (
                        )

                    [[invalid]] => Array
                        (
                        )

                    [[repository]] => MemberBadges
                )

            [1] => App\Model\Entity\MemberBadge Object
                (
                    [id] => 22
                    [created] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2017-11-06 12:21:42.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [modified] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2021-06-01 19:27:22.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [created_by] => 3
                    [modified_by] => 3
                    [member_id] => 3
                    [badge_id] => 1
                    [form28_id] => 10
                    [practical_book_mark] => 
                    [scrap_book_mark] => 
                    [questionnaire_mark] => 
                    [exam_mark] => 99
                    [honours] => 1
                    [rpl_awarded] => 
                    [issuance_status_id] => 1
                    [date_of_issue] => Cake\I18n\FrozenDate Object
                        (
                            [date] => 2017-11-06 00:00:00.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [authority_card_number] => TASE179/17
                    [eligible_for_points] => 
                    [missing_badge_id] => 
                    [notes] => 
                    [total_marks] => 99
                    [age_at_issue] => 5.45
                    [[new]] => 
                    [[accessible]] => Array
                        (
                            [created] => 1
                            [modified] => 1
                            [created_by] => 1
                            [modified_by] => 1
                            [member_id] => 1
                            [badge_id] => 1
                            [form28_id] => 1
                            [practical_book_mark] => 1
                            [scrap_book_mark] => 1
                            [questionnaire_mark] => 1
                            [exam_mark] => 1
                            [honours] => 1
                            [rpl_awarded] => 1
                            [issuance_status_id] => 1
                            [date_of_issue] => 1
                            [authority_card_number] => 1
                            [eligible_for_points] => 1
                            [missing_badge_id] => 1
                            [notes] => 1
                            [member] => 1
                            [badge] => 1
                            [form28] => 1
                            [issuance_status] => 1
                        )

                    [[dirty]] => Array
                        (
                        )

                    [[original]] => Array
                        (
                        )

                    [[virtual]] => Array
                        (
                            [0] => total_marks
                            [1] => age_at_issue
                        )

                    [[hasErrors]] => 
                    [[errors]] => Array
                        (
                        )

                    [[invalid]] => Array
                        (
                        )

                    [[repository]] => MemberBadges
                )

        )

    [badge] => App\Model\Entity\Badge Object
        (
            [id] => 1
            [created] => 
            [modified] => Cake\I18n\FrozenTime Object
                (
                    [date] => 2017-06-03 12:53:17.000000
                    [timezone_type] => 3
                    [timezone] => UTC
                )

            [created_by] => 2
            [modified_by] => 2
            [name] => General Proficiency Class 6
            [abbreviation] => GP6
            [badge_group_id] => 1
            [badge_section_id] => 1
            [badge_subject_id] => 1
            [badge_type_id] => 1
            [no_assessment] => 
            [rpl_eligible] => 
            [not_available] => 
            [pre_requisite_badge_id] => 
            [pass_mark] => 50
            [instructor_efficiency_points] => 5
            [squadron_trophy_points] => 1
            [[new]] => 
            [[accessible]] => Array
                (
                    [created] => 1
                    [modified] => 1
                    [created_by] => 1
                    [modified_by] => 1
                    [name] => 1
                    [abbreviation] => 1
                    [badge_group_id] => 1
                    [badge_section_id] => 1
                    [badge_subject_id] => 1
                    [badge_type_id] => 1
                    [no_assessment] => 1
                    [rpl_eligible] => 1
                    [not_available] => 1
                    [pre_requisite_badge_id] => 1
                    [pass_mark] => 1
                    [instructor_efficiency_points] => 1
                    [squadron_trophy_points] => 1
                    [badge_group] => 1
                    [badge_section] => 1
                    [badge_subject] => 1
                    [badge_type] => 1
                    [badge] => 1
                    [aviation_experience_classes] => 1
                    [diploma_badges] => 1
                    [form28s] => 1
                    [member_badges] => 1
                )

            [[dirty]] => Array
                (
                )

            [[original]] => Array
                (
                )

            [[virtual]] => Array
                (
                )

            [[hasErrors]] => 
            [[errors]] => Array
                (
                )

            [[invalid]] => Array
                (
                )

            [[repository]] => Badges
        )

    [[new]] => 
    [[accessible]] => Array
        (
            [created] => 1
            [modified] => 1
            [created_by] => 1
            [modified_by] => 1
            [file_number] => 1
            [unit_id] => 1
            [badge_id] => 1
            [workbook_reference] => 1
            [planned_exam_date] => 1
            [actual_exam_date] => 1
            [submit_date] => 1
            [dispatch_date] => 1
            [complete_date] => 1
            [examiner_id] => 1
            [supervisor_id] => 1
            [form28_status_id] => 1
            [instructor_efficiency_points] => 1
            [squadron_trophy_points] => 1
            [notes] => 1
            [form28_instructor_count] => 1
            [member_badge_count] => 1
            [member_badge_count_pass] => 1
            [member_badge_count_fail] => 1
            [unit] => 1
            [badge] => 1
            [member] => 1
            [form28_status] => 1
            [form28_instructors] => 1
            [member_badges] => 1
        )

    [[dirty]] => Array
        (
            [instructor_efficiency_points] => 1
            [squadron_trophy_points] => 1
            [member_badge_count] => 1
            [member_badge_count_pass] => 1
            [member_badge_count_fail] => 1
        )

    [[original]] => Array
        (
            [member_badge_count_pass] => 2
            [member_badge_count_fail] => 2
        )

    [[virtual]] => Array
        (
        )

    [[hasErrors]] => 
    [[errors]] => Array
        (
        )

    [[invalid]] => Array
        (
        )

    [[repository]] => Form28s
)
App\Model\Entity\Form28 Object
(
    [id] => 20
    [created] => Cake\I18n\FrozenTime Object
        (
            [date] => 2019-05-21 21:35:55.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [modified] => Cake\I18n\FrozenTime Object
        (
            [date] => 2023-04-23 04:31:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [created_by] => 6
    [modified_by] => 2
    [file_number] => TE151
    [unit_id] => 198
    [badge_id] => 129
    [workbook_reference] => 
    [planned_exam_date] => Cake\I18n\FrozenDate Object
        (
            [date] => 2019-05-31 00:00:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [actual_exam_date] => 
    [submit_date] => Cake\I18n\FrozenDate Object
        (
            [date] => 2019-08-10 00:00:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [dispatch_date] => 
    [complete_date] => Cake\I18n\FrozenDate Object
        (
            [date] => 2019-08-10 00:00:00.000000
            [timezone_type] => 3
            [timezone] => UTC
        )

    [examiner_id] => 3
    [supervisor_id] => 
    [form28_status_id] => 2
    [instructor_efficiency_points] => 20
    [squadron_trophy_points] => 15
    [notes] => 
    [form28_instructor_count] => 
    [member_badge_count] => 1
    [member_badge_count_pass] => 1
    [member_badge_count_fail] => 0
    [form28_instructors] => Array
        (
        )

    [member_badges] => Array
        (
            [0] => App\Model\Entity\MemberBadge Object
                (
                    [id] => 39
                    [created] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2019-08-10 19:18:57.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [modified] => Cake\I18n\FrozenTime Object
                        (
                            [date] => 2023-04-23 04:31:29.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [created_by] => 2
                    [modified_by] => 2
                    [member_id] => 11
                    [badge_id] => 129
                    [form28_id] => 20
                    [practical_book_mark] => 
                    [scrap_book_mark] => 
                    [questionnaire_mark] => 
                    [exam_mark] => 100
                    [honours] => 
                    [rpl_awarded] => 
                    [issuance_status_id] => 1
                    [date_of_issue] => Cake\I18n\FrozenDate Object
                        (
                            [date] => 2019-08-10 00:00:00.000000
                            [timezone_type] => 3
                            [timezone] => UTC
                        )

                    [authority_card_number] => TASE192/23
                    [eligible_for_points] => 1
                    [missing_badge_id] => 
                    [notes] => 
                    [total_marks] => 100
                    [age_at_issue] => 3.7
                    [[new]] => 
                    [[accessible]] => Array
                        (
                            [created] => 1
                            [modified] => 1
                            [created_by] => 1
                            [modified_by] => 1
                            [member_id] => 1
                            [badge_id] => 1
                            [form28_id] => 1
                            [practical_book_mark] => 1
                            [scrap_book_mark] => 1
                            [questionnaire_mark] => 1
                            [exam_mark] => 1
                            [honours] => 1
                            [rpl_awarded] => 1
                            [issuance_status_id] => 1
                            [date_of_issue] => 1
                            [authority_card_number] => 1
                            [eligible_for_points] => 1
                            [missing_badge_id] => 1
                            [notes] => 1
                            [member] => 1
                            [badge] => 1
                            [form28] => 1
                            [issuance_status] => 1
                        )

                    [[dirty]] => Array
                        (
                        )

                    [[original]] => Array
                        (
                        )

                    [[virtual]] => Array
                        (
                            [0] => total_marks
                            [1] => age_at_issue
                        )

                    [[hasErrors]] => 
                    [[errors]] => Array
                        (
                        )

                    [[invalid]] => Array
                        (
                        )

                    [[repository]] => MemberBadges
                )

        )

    [badge] => App\Model\Entity\Badge Object
        (
            [id] => 129
            [created] => 
            [modified] => Cake\I18n\FrozenTime Object
                (
                    [date] => 2017-06-03 13:05:54.000000
                    [timezone_type] => 3
                    [timezone] => UTC
                )

            [created_by] => 2
            [modified_by] => 2
            [name] => Aircraft Radio Class 1
            [abbreviation] => AR1
            [badge_group_id] => 6
            [badge_section_id] => 1
            [badge_subject_id] => 3
            [badge_type_id] => 1
            [no_assessment] => 
            [rpl_eligible] => 1
            [not_available] => 
            [pre_requisite_badge_id] => 28
            [pass_mark] => 70
            [instructor_efficiency_points] => 20
            [squadron_trophy_points] => 15
            [[new]] => 
            [[accessible]] => Array
                (
                    [created] => 1
                    [modified] => 1
                    [created_by] => 1
                    [modified_by] => 1
                    [name] => 1
                    [abbreviation] => 1
                    [badge_group_id] => 1
                    [badge_section_id] => 1
                    [badge_subject_id] => 1
                    [badge_type_id] => 1
                    [no_assessment] => 1
                    [rpl_eligible] => 1
                    [not_available] => 1
                    [pre_requisite_badge_id] => 1
                    [pass_mark] => 1
                    [instructor_efficiency_points] => 1
                    [squadron_trophy_points] => 1
                    [badge_group] => 1
                    [badge_section] => 1
                    [badge_subject] => 1
                    [badge_type] => 1
                    [badge] => 1
                    [aviation_experience_classes] => 1
                    [diploma_badges] => 1
                    [form28s] => 1
                    [member_badges] => 1
                )

            [[dirty]] => Array
                (
                )

            [[original]] => Array
                (
                )

            [[virtual]] => Array
                (
                )

            [[hasErrors]] => 
            [[errors]] => Array
                (
                )

            [[invalid]] => Array
                (
                )

            [[repository]] => Badges
        )

    [[new]] => 
    [[accessible]] => Array
        (
            [created] => 1
            [modified] => 1
            [created_by] => 1
            [modified_by] => 1
            [file_number] => 1
            [unit_id] => 1
            [badge_id] => 1
            [workbook_reference] => 1
            [planned_exam_date] => 1
            [actual_exam_date] => 1
            [submit_date] => 1
            [dispatch_date] => 1
            [complete_date] => 1
            [examiner_id] => 1
            [supervisor_id] => 1
            [form28_status_id] => 1
            [instructor_efficiency_points] => 1
            [squadron_trophy_points] => 1
            [notes] => 1
            [form28_instructor_count] => 1
            [member_badge_count] => 1
            [member_badge_count_pass] => 1
            [member_badge_count_fail] => 1
            [unit] => 1
            [badge] => 1
            [member] => 1
            [form28_status] => 1
            [form28_instructors] => 1
            [member_badges] => 1
        )

    [[dirty]] => Array
        (
            [instructor_efficiency_points] => 1
            [squadron_trophy_points] => 1
            [member_badge_count] => 1
            [member_badge_count_pass] => 1
            [member_badge_count_fail] => 1
        )

    [[original]] => Array
        (
            [member_badge_count_fail] => 1
        )

    [[virtual]] => Array
        (
        )

    [[hasErrors]] => 
    [[errors]] => Array
        (
        )

    [[invalid]] => Array
        (
        )

    [[repository]] => Form28s
)

Well there you have it, the form28_instructors field is not marked as dirty, hence it will not be saved. So it comes down to how you’re assigned the data.

1 Like

Basically, I’m assigning the data by running a foreach loop on $form28->form28_instructors and updating the value to the new assigned.

Whilst the field in the associated form28_instructor entity is updated and “dirty”, I’m guessing the form28 doesn’t know the associated record is changed, and to save it. I assumed it would check (don’t assume :slight_smile: )

                    [[dirty]] => Array
                        (
                            [instructor_efficiency_points] => 1
                        )

After adding a setDirty on the associated entity it’s working now.

            $form28->setDirty('form28_instructors');

Thanks - have the basic down pat and this was a more complex change generated in the afterSave