Сохранение ассоциации CakePHP 4.1, принадлежащей To, не работает

У меня есть две таблицы БД - Users и UserTypes, где Users имеет внешний ключ user_type_id. Ниже приведен их шаблонный код модели.

Entity/User.php:

class User extends Entity {
    protected $_accessible = [
        'id' => true,
        'email' => true,
        'password' => true,
        'name' => true,
        'user_type_id' => true, // added this as an experiment now, but I shouldn't need it if I understand correctly
        'user_type' => true
    ];
}

Entity/UserType.php:

class UserType extends Entity {
    protected $_accessible = [
        'id' => true,
        'name' => true,
        'users' => true,
    ];
}

Table/UsersTable.php:

class UsersTable extends Table
{
public function initialize(array $config): void
{
    parent::initialize($config);

    $this->setTable('users');
    $this->setDisplayField('name');
    $this->setPrimaryKey('id');

    $this->belongsTo('UserTypes', [
        'joinType' => 'INNER'
    ]);
}


public function validationDefault(Validator $validator): Validator
{
    $validator
        ->integer('id')
        ->allowEmptyString('id', null, 'create');

    $validator
        ->email('email')
        ->requirePresence('email', 'create')
        ->notEmptyString('email');

    $validator
        ->scalar('password')
        ->maxLength('password', 255)
        ->requirePresence('password', 'create')
        ->notEmptyString('password');

    $validator
        ->scalar('name')
        ->maxLength('name', 255)
        ->allowEmptyString('name');

    return $validator;
}

public function buildRules(RulesChecker $rules): RulesChecker
{
    $rules->add($rules->isUnique(['email']));
    $rules->add($rules->existsIn(['user_type_id'], 'UserTypes'));

    return $rules;
}
}

Table/UserTypesTable.php:

class UserTypesTable extends Table
{
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('user_types');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
        $this->hasMany('Users');
    }

    public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->integer('id')
            ->allowEmptyString('id', null, 'create');

        $validator
            ->scalar('name')
            ->requirePresence('name', 'create')
            ->notEmptyString('name');

        return $validator;
    }
}

Теперь проблема в том, что я не могу сохранить поле user_type_id записи в таблице users базы данных. Чтобы воспроизвести проблему, я создал следующее простое действие в UsersController.php:

public function testAdd()
{
    $testUser = $this->Users->newEmptyEntity();
    $testUser->name = "Test";
    $testUser->email = "[email protected]";
    $testUser->password = "secret";
    $testUser->user_type = $this->Users->UserTypes->get(1); // this exists and I've verified get() finds it correctly
    $this->Users->save($testUser);
}

Результатом этого является SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails, а предпринятый запрос — INSERT INTO users (email, password, name) VALUES (:c0, :c1, :c2), в котором явно нет необходимого поля user_type_id.

Я попытался изменить тип пользователя существующего пользователя, который не дает сбоя (поскольку ограничение FK уже выполнено), но никаких изменений не происходит.

Я также попробовал $this->Users->save($testUser, ['associated' => ['UserTypes']]);, который ничего не изменил для результирующего SQL-запроса INSERT.

Итак, мой вопрос: как я могу вставить и обновить поле внешнего ключа моей сущности пользователя?

---- РЕДАКТИРОВАТЬ ----

Запуск debug(get_class($this->Users)) непосредственно перед save() дает:

APP/Controller/UsersController.php (line 270)
'App\Model\Table\UsersTable'

И запуск debug($testUser) непосредственно перед save() дает:

APP/Controller/UsersController.php (line 271)
object(App\Model\Entity\User) id:0 {
'name' => 'Test'
'email' => '[email protected]'
'password' => '$2y$10$13nmS6Iag3seqkae9L.M0Ow.xV0Tasd/y9XNu12xX9yIozsXNLEnO'
'user_type' => object(App\Model\Entity\UserType) id:1 {
'id' => (int) 1
'name' => 'Full admin'
'[new]' => false
'[accessible]' => [
'id' => true,
'name' => true,
'users' => true,
]
'[dirty]' => [
]
'[original]' => [
]
'[virtual]' => [
]
'[hasErrors]' => false
'[errors]' => [
]
'[invalid]' => [
]
'[repository]' => 'UserTypes'
protected _accessible => [
'id' => true,
'name' => true,
'users' => true,
]
protected _fields => [
'id' => (int) 1,
'name' => 'Full admin',
]
protected _original => [
]
protected _hidden => [
]
protected _virtual => [
]
protected _dirty => [
]
protected _accessors => [
'App\Model\Entity\User' => [
'set' => [
'password' => '_setPassword',
'Password' => '_setPassword',
'name' => '',
'email' => '',
'user_type' => '',
],
],
]
protected _new => false
protected _errors => [
]
protected _invalid => [
]
protected _registryAlias => 'UserTypes'
}
'[new]' => true
'[accessible]' => [
'id' => true,
'email' => true,
'password' => true,
'name' => true,
'created' => true,
'modified' => true,
'user_type_id' => true,
'user_type' => true
]
'[dirty]' => [
'name' => true,
'email' => true,
'password' => true,
'user_type' => true,
]
'[original]' => [
]
'[virtual]' => [
]
'[hasErrors]' => false
'[errors]' => [
]
'[invalid]' => [
]
'[repository]' => 'Users'
protected _accessible => [
'id' => true,
'email' => true,
'password' => true,
'name' => true,
'created' => true,
'modified' => true,
'user_type_id' => true,
'user_type' => true
]
protected _hidden => [
(int) 0 => 'password',
]
protected _fields => [
'name' => 'Test',
'email' => '[email protected]',
'password' => '$2y$10$13nmS6Iag3seqkau9L.M0Ow.xV0Tasd/y9XNe12xX9yIozsXNLEnO',
'user_type' => object(App\Model\Entity\UserType) id: 1 {},
]
protected _original => [
]
protected _virtual => [
]
protected _dirty => [
'name' => true,
'email' => true,
'password' => true,
'user_type' => true,
]
protected _accessors => [
'App\Model\Entity\User' => [
'set' => [
'password' => '_setPassword',
'Password' => '_setPassword',
'name' => '',
'email' => '',
'user_type' => '',
],
],
]
protected _new => true
protected _errors => [
]
protected _invalid => [
]
protected _registryAlias => 'Users'
}

person user1507558    schedule 15.08.2020    source источник
comment
Есть и другие, более простые способы сделать это, но этот тоже должен работать, поэтому, возможно, стоит выяснить, что происходит. Попробуйте собрать некоторый контекст с помощью debug(get_class($this->Users)); и debug($testUser); (прямо перед вызовом save()).   -  person ndm    schedule 15.08.2020
comment
@ndm Я отредактировал вопрос, включив в него два вывода debug(). Мне также было бы интересно услышать об этих более простых способах, о которых вы говорите. Спасибо!   -  person user1507558    schedule 15.08.2020
comment
О боже, новый формат отладки не создает настоящих пробелов ;( Во всяком случае, возможно ли user_type_id поле, которое вы добавили позже, т.е. после того, как таблица базы данных уже была создана?   -  person ndm    schedule 15.08.2020
comment
Да, то же самое я думал об отладке :( Да, она была добавлена ​​позже. Но какое это имеет значение, если она правильно описана в объекте и табличных объектах? Тьфу, у меня всегда были проблемы с ORM.   -  person user1507558    schedule 15.08.2020
comment
Это важно, потому что ORM будет пытаться сохранить только те поля, которые присутствуют в схеме, а схемы по умолчанию кэшируются, поэтому, если вы добавили поле позже и не очистили кеш после этого, ваш новое поле не будет сохранено. Попробуйте очистить кеш модели (/tmp/cache/models) и повторите попытку.   -  person ndm    schedule 15.08.2020
comment
Невероятно, но именно в этом была причина. Благодарю вас! Я все же хотел бы услышать о более простых способах, которые вы упомянули изначально, если это возможно, на случай, если я пройду через ненужные обручи.   -  person user1507558    schedule 15.08.2020
comment
Что ж, самым простым способом было бы просто заполнить поле внешнего ключа, т.е. установить user_type_id в 1 и все готово.   -  person ndm    schedule 16.08.2020
comment
Ах да, имеет смысл. Я пробовал это как якобы операцию проверки дурака, которая, однако, также не удалась из-за устаревшего кэша схемы. Не уверен, что лучше всего принять ваш ответ, так как он был в комментарии, поэтому я просто отмечу его как полезный.   -  person user1507558    schedule 17.08.2020
comment
Что ж, в идеале кто-то должен опубликовать ответ, который можно принять ... Я довольно ленив в эти дни, жара действительно раздавила меня, так что ваши шансы опередить меня довольно высоки;)   -  person ndm    schedule 18.08.2020


Ответы (1)


Написание комментария @ndm в качестве ответа для всех, кто окажется здесь с той же проблемой. Оказывается, моя схема ORM была закэширована до того, как я добавил таблицу user_types DB. Очистка /tmp/cache/models сработала для меня. Осторожно, кеш!

person user1507558    schedule 19.08.2020