Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CakePHP 3.1 patchEntity always marking belongsToMany associations as dirty when clean

So I noticed that If I patch an entity (edit method) and whether or not I make any data changes to the record if it has belongsToMany associations it marks them as dirty. I would expect that if I make no changes to the BTM multiple select in the view the data would not be dirty, only if the adding or removing options in the multiple select would it be marked as dirty after patching.

The data does save correctly, it is just dirty, but I need to act on know if it is dirty or clean as I have _join data in my map table. The map table is named users_locations and has id, user_id, location_id and static where static is a tinyint/bool.

What I am trying to do is flag static only for newly created map table entries.

What I notice is that patchEntity is stripping the _joinData as part of the marshaling process.

So looking at the debug output below you can see that the _joinData is stripped after patching for locations and user_occupations both.

This seems undesirable to me to not know if the associated data is clean or dirty. Maybe it was intended to work this way and I am missing something. Thoughts?

In the edit form I have:

<?php echo $this->Form->input('locations._ids', ['options' => $locations]) ?>

In the controller I have:

<?php
    public function edit($id = null)
    {
        $user = $this->Users->get($id, [
            'contain' => ['Locations', 'UserOccupations']
        ]);
        if ($this->request->is(['patch', 'post', 'put'])) {
            $user = $this->Users->patchEntity($user, $this->request->data);
            if ($this->Users->save($user)) {
                $this->Flash->success(__('The user has been saved.'));
                return $this->redirect(['action' => 'index']);
            } else {
                $this->Flash->error(__('The user could not be saved. Please, try again.'));
            }
        }
        $securityGroups = $this->Users->SecurityGroups->find('list');
        $locations = $this->Users->Locations->find('list', [
            'order' => ['Locations.name' => 'ASC'],
            'keyField' => 'id',
            'valueField' => 'name',
            'limit' => 200
        ]);
        $userOccupations = $this->Users->UserOccupations->find('list');
        $this->set(compact('user', 'securityGroups', 'locations', 'userOccupations'));
        $this->set('_serialize', ['user']);
    }
?>

In the model I have this in the initialize function for User:

$this->belongsToMany('Locations', [
    'through' => 'Users.UsersLocations',
    'foreignKey' => 'user_id',
    'targetForeignKey' => 'location_id',
    'className' => 'Locations.Locations'
]);

This is the request data debug output:

[
    'Referer' => [
        'url' => '/login'
    ],
    'security_group_id' => '',
    'username' => 'test',
    'email' => 'test@test.com',
    'prefix' => '',
    'first_name' => 'test',
    'middle_name' => '',
    'last_name' => 'test',
    'suffix' => '',
    'credentials' => '',
    'birthdate' => '',
    'timezone' => 'America/New_York',
    'theme' => '',
    'locations' => [
        '_ids' => [
            (int) 0 => '7',
            (int) 1 => '33'
        ]
    ],
    'user_occupations' => [
        '_ids' => [
            (int) 0 => '1'
        ]
    ]
]

This is the user entity before patching with the request data:

object(Users\Model\Entity\User) {

    'id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
    'identifier' => (int) 5,
    'security_group_id' => null,
    'sex_id' => null,
    'username' => 'test',
    'email' => 'test@test.com',
    'prefix' => '',
    'first_name' => 'test',
    'middle_name' => '',
    'last_name' => 'test',
    'suffix' => '',
    'credentials' => '',
    'birthdate' => null,
    'timezone' => 'America/New_York',
    'theme' => '',
    'ip' => '0.0.0.0',
    'last_login' => null,
    'created' => object(Cake\I18n\Time) {

        'time' => '2015-09-16T16:17:57+0000',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'modified' => object(Cake\I18n\Time) {

        'time' => '2015-12-16T22:22:49+0000',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'user_occupations' => [
        (int) 0 => object(Users\Model\Entity\UserOccupation) {

            'id' => (int) 1,
            'name' => 'Test',
            '_joinData' => object(Cake\ORM\Entity) {

                'user_occupation_id' => (int) 1,
                'user_id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
                '[new]' => false,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[repository]' => 'UsersUserOccupations'

            },
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Users.UserOccupations'

        }
    ],
    'locations' => [
        (int) 0 => object(Locations\Model\Entity\Location) {

            'id' => (int) 7,
            'ldap_name' => 'Test',
            'name' => 'Test',
            'address' => null,
            'address_2' => null,
            'city' => 'Test',
            'state' => 'MD',
            'zip' => null,
            'phone' => null,
            'fax' => null,
            'active' => true,
            'created' => object(Cake\I18n\Time) {

                'time' => '2015-09-11T19:35:34+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            'modified' => object(Cake\I18n\Time) {

                'time' => '2015-12-16T21:47:29+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            '_joinData' => object(Users\Model\Entity\UsersLocation) {

                'location_id' => (int) 7,
                'id' => (int) 304,
                'user_id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
                'static' => false,
                '[new]' => false,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[repository]' => 'Users.UsersLocations'

            },
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Locations.Locations'

        },
        (int) 1 => object(Locations\Model\Entity\Location) {

            'id' => (int) 33,
            'ldap_name' => 'Test2',
            'name' => 'Test2',
            'address' => null,
            'address_2' => null,
            'city' => 'Test',
            'state' => 'MD',
            'zip' => null,
            'phone' => null,
            'fax' => null,
            'active' => true,
            'created' => object(Cake\I18n\Time) {

                'time' => '2015-09-15T21:03:46+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            'modified' => object(Cake\I18n\Time) {

                'time' => '2015-12-16T21:47:29+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            '_joinData' => object(Users\Model\Entity\UsersLocation) {

                'location_id' => (int) 33,
                'id' => (int) 305,
                'user_id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
                'static' => false,
                '[new]' => false,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[repository]' => 'Users.UsersLocations'

            },
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Locations.Locations'

        },
    ],
    '[new]' => false,
    '[accessible]' => [
        '*' => true
    ],
    '[dirty]' => [],
    '[original]' => [],
    '[virtual]' => [
        (int) 0 => 'full_name',
        (int) 1 => 'name_last_first'
    ],
    '[errors]' => [],
    '[repository]' => 'Users.Users'

}

This the what the debug output look like after patching with the request data:

object(Users\Model\Entity\User) {

    'id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
    'identifier' => (int) 5,
    'security_group_id' => null,
    'sex_id' => null,
    'username' => 'test',
    'email' => 'test@test.com',
    'prefix' => '',
    'first_name' => 'test',
    'middle_name' => '',
    'last_name' => 'test',
    'suffix' => '',
    'credentials' => '',
    'birthdate' => null,
    'timezone' => 'America/New_York',
    'theme' => '',
    'ip' => '0.0.0.0',
    'last_login' => null,
    'created' => object(Cake\I18n\Time) {

        'time' => '2015-09-16T16:17:57+0000',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'modified' => object(Cake\I18n\Time) {

        'time' => '2015-12-16T22:22:49+0000',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'user_occupations' => [
        (int) 0 => object(Users\Model\Entity\UserOccupation) {

            'id' => (int) 1,
            'name' => 'Test ',
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Users.UserOccupations'

        }
    ],
    'locations' => [
        (int) 0 => object(Locations\Model\Entity\Location) {

            'id' => (int) 7,
            'ldap_name' => 'Test',
            'name' => 'Test',
            'address' => null,
            'address_2' => null,
            'city' => 'Test',
            'state' => 'MD',
            'zip' => null,
            'phone' => null,
            'fax' => null,
            'active' => true,
            'created' => object(Cake\I18n\Time) {

                'time' => '2015-09-11T19:35:34+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            'modified' => object(Cake\I18n\Time) {

                'time' => '2015-12-16T21:47:29+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Locations.Locations'

        },
        (int) 1 => object(Locations\Model\Entity\Location) {

            'id' => (int) 33,
            'ldap_name' => 'Test2',
            'name' => 'Test2',
            'address' => null,
            'address_2' => null,
            'city' => 'Test',
            'state' => 'MD',
            'zip' => null,
            'phone' => null,
            'fax' => null,
            'active' => true,
            'created' => object(Cake\I18n\Time) {

                'time' => '2015-09-15T21:03:46+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            'modified' => object(Cake\I18n\Time) {

                'time' => '2015-12-16T21:47:29+0000',
                'timezone' => 'UTC',
                'fixedNowTime' => false

            },
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Locations.Locations'

        }
    ],
    '[new]' => false,
    '[accessible]' => [
        '*' => true
    ],
    '[dirty]' => [
        'locations' => true,
        'user_occupations' => true
    ],
    '[original]' => [
        'locations' => [
            (int) 0 => object(Locations\Model\Entity\Location) {

                'id' => (int) 7,
                'ldap_name' => 'Test',
                'name' => 'Test',
                'address' => null,
                'address_2' => null,
                'city' => 'Test',
                'state' => 'MD',
                'zip' => null,
                'phone' => null,
                'fax' => null,
                'active' => true,
                'created' => object(Cake\I18n\Time) {

                    'time' => '2015-09-11T19:35:34+0000',
                    'timezone' => 'UTC',
                    'fixedNowTime' => false

                },
                'modified' => object(Cake\I18n\Time) {

                    'time' => '2015-12-16T21:47:29+0000',
                    'timezone' => 'UTC',
                    'fixedNowTime' => false

                },
                '_joinData' => object(Users\Model\Entity\UsersLocation) {

                    'location_id' => (int) 7,
                    'id' => (int) 304,
                    'user_id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
                    'static' => false,
                    '[new]' => false,
                    '[accessible]' => [
                        '*' => true
                    ],
                    '[dirty]' => [],
                    '[original]' => [],
                    '[virtual]' => [],
                    '[errors]' => [],
                    '[repository]' => 'Users.UsersLocations'

                },
                '[new]' => false,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[repository]' => 'Locations.Locations'

            },
            (int) 1 => object(Locations\Model\Entity\Location) {

                'id' => (int) 33,
                'ldap_name' => 'Test2',
                'name' => 'Test2',
                'address' => null,
                'address_2' => null,
                'city' => 'Test',
                'state' => 'MD',
                'zip' => null,
                'phone' => null,
                'fax' => null,
                'active' => true,
                'created' => object(Cake\I18n\Time) {

                    'time' => '2015-09-15T21:03:46+0000',
                    'timezone' => 'UTC',
                    'fixedNowTime' => false

                },
                'modified' => object(Cake\I18n\Time) {

                    'time' => '2015-12-16T21:47:29+0000',
                    'timezone' => 'UTC',
                    'fixedNowTime' => false

                },
                '_joinData' => object(Users\Model\Entity\UsersLocation) {

                    'location_id' => (int) 33,
                    'id' => (int) 305,
                    'user_id' => '8b7197a4-5633-4bda-a6c7-a6e16f7cad64',
                    'static' => false,
                    '[new]' => false,
                    '[accessible]' => [
                        '*' => true
                    ],
                    '[dirty]' => [],
                    '[original]' => [],
                    '[virtual]' => [],
                    '[errors]' => [],
                    '[repository]' => 'Users.UsersLocations'

                },
                '[new]' => false,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[repository]' => 'Locations.Locations'

            },
        ]
    ],
    '[virtual]' => [
        (int) 0 => 'full_name',
        (int) 1 => 'name_last_first'
    ],
    '[errors]' => [],
    '[repository]' => 'Users.Users'

}
like image 786
darensipes Avatar asked Nov 08 '22 23:11

darensipes


1 Answers

Please open an issue in https://github.com/cakephp/cakephp/issues.

Thanks!

like image 106
Megs Lalk Avatar answered Nov 14 '22 22:11

Megs Lalk