CakePHP 3 Сохранение ассоциации HasOne

возможная ошибка в рамках CakePHP, но не уверен в этом

Я получил следующие таблицы MySQL InnoDB:

database.users
+-----+---------------+----------+
| id  | user_group_id | username |
+-----+---------------+----------+
| INT |           INT | VARCHAR  |

database.user_settings
+-----+---------------+----------+
| id  |       user_id |     data |
+-----+---------------+----------+
| INT |           INT |  VARCHAR |

Я получил следующую инициализацию в табличных классах:

  • Модель \ Таблица \ UsersTable:

    $this->table('users');
    $this->displayField('id');
    $this->primaryKey('id');
    $this->belongsTo('UserGroups', [
    'foreignKey' => 'user_group_id'
    ]);
    $this->hasOne('UserSettings', [
    'foreignKey' => 'user_id'
    ]);
    
  • Модель \ Таблица \ UserSettingsTable

    $this->table('user_settings');
    $this->displayField('id');
    $this->primaryKey('id');
    $this->belongsTo('Users', [
    'foreignKey' => 'user_id'
    ]);
    
    // And folowing validation rules:
    $validator
    ->add('id', 'valid', ['rule' => 'numeric'])
    ->allowEmpty('id', 'create')
    // UserSettings.user_id validation rule:
    ->add('user_id', 'valid', ['rule' => 'numeric'])
    ->requirePresence('user_id', 'create')
    ->notEmpty('user_id');
    

    И я получил следующий код:

    $user = $this->Users->newEntity();
    if ($this->request->is('post')) {
    $user = $this->Users->patchEntity($user, $this->request->data, [
    'associated' => ['Users.UserSettings']
    ]);
    // Tried it also this way, won't change anything
    //$user = $this->Users->patchEntity($user, $this->request->data, [
    //  'associated' => ['user_setting']
    //]);
    $this->Users->save($user,['associated' => ['UserSettings']]);
    }
    

    Пример входных данных ($ this-> request-> data):

    [
    'user_group_id' => 1, // Not related to question
    'username' => 'test', // This will be saved without core modifications
    'user_setting' => [
    'data' => 'sample data' // Saved only after "fix" described below
    ]
    ];
    

    Это сохранит родительскую таблицу (пользователи), но не дочернюю таблицу (user_settings).

    Затем я получил следующую модификацию, от которой хочу избавиться:

    Если я положу это изменение в ORM\Associations\HasOne::saveAssociated(...)

    // $this->property() == 'user_setting'
    if (is_array($targetEntity)) {
    $targetEntity = $this->target()->newEntity($targetEntity);
    }
    

    Это мгновенно сработает так, как я хотел. HasOne на самом деле имеет требуемые данные, но также проверяет, находятся ли данные внутри Entity, в этом случае они были в массиве.

    Я играл с разными комбинациями названий ассоциаций, и кажется, что так и должно быть.

    Главный вопрос: как сохранить необязательную ассоциацию hasOne с родительской строкой?

    Можно ли как-то убедиться, что данные будут преобразованы в объект Entity?
    Я думаю, что это должно сработать, поскольку есть все необходимые данные и, похоже, они правильно обрабатывают отношения.

    2
  • Решение

    С помощью @ndm мне удалось решить эту проблему с помощью специального маршаллера.

    Сначала я взглянул на отдельные валидаторы, но для меня это кажется слишком сложным и хрупким способом решения очень простой прямой задачи.

    Мое честное мнение состоит в том, что все это должно как-то решаться внутри ядра фреймворка.

    Решение требуемого фк с помощью пользовательского маршаллера:

    Таким образом, вы должны быть уверены, что «пользовательская проверка» будет использоваться только тогда, когда UserSettings будет создан через Users, а user_settings.user_id должен быть легко доступен.

    class UsersMarshaller extends Marshaller {
    protected function _validate($data, $options, $isNew) {
    $errors = parent::_validate($data, $options, $isNew);
    if ($isNew) {
    unset($errors['user_setting']['user_id']['_required']);
    }
    return $errors;
    }
    }
    

    И в UsersTable учебный класс:

    public function marshaller() {
    return new UsersMarshaller($this);
    }
    

    Фактические ошибки в исходном вопросе:

    patchEntity(...) параметры вызова были ошибочными, даже если эти параметры делали что-то, что делало их похожими на правильные.
    Ассоциации должны быть такими: ['associated' => ['UserSettings']], Не Users.UserSettings или же user_setting,

    2

    Другие решения

    Правильное значение для associated собственность будет UserSettingsчто вы использовали, Users.UserSettingsбыл бы Users > Users > UserSettings ассоциация.

    1