CakePHP: невозможно сохранить данные в родительскую сущность через дочерний объект, принадлежащий ребенку. - PullRequest
0 голосов
/ 31 мая 2019

Описание проблемы

Я пытаюсь настроить CakePHP 3.7 API для сохранения связанных данных в дочернем порядке.Сущности - например, давайте назовем их Users и Persons - и их отношения будут следующими:

UsersTable.php

...
  $this->belongsTo('Persons', [
    'foreignKey' => 'person_id',
    'joinType' => 'LEFT',
    'className' => 'MyPlugin.Persons',
  ]);
...

PersonsTable.php

  $this->hasOne('Users', [
    'foreignKey' => 'person_id',
    'className' => 'MyPlugin.Users'
  ]);

В своих соответствующих сущностях каждый из них имеет видимость свойства друг друга, установленную на true.То, что я пытаюсь сделать, это POST к маршруту /users/ (UsersController.php) и сохранить в нем также объект Persons.Полезная нагрузка такова:

{
    "username": "foo",
    "password": "bar",
    "persons": {
        "dob": "1982-07-03",
    }
}

Соответствующая часть метода сохранения приведена ниже, начиная с UsersController.php:

  if ($this->request->is('post') && !empty($this->request->getData())) {
    $data = $this->request->getData();
    $newEntity = $this->Users->newEntity($data, ['associated' => 'Persons']);

    $savedEntity =  $this->Users->save($newEntity);
  ...

Ошибка

Это приводит кследующая ошибка SQL.

PDOException: SQLSTATE [23502]: нарушение не равняется NULL: 7 ОШИБКА: нулевое значение в столбце «person_id» нарушает ненулевое ограничение ДЕТАЛИ: В ошибочной строке содержится (1, null, foo, bar)

Я понимаю, что это потому, что Cake пытается сохранить в Users без person_id, чтобы удовлетворить ограничение внешнего ключа.Невозможно изменить это отношение FK в моем домене приложения, так как мы желаем отношения «один ко многим» влево (пользователь -> 1 человек).

Я подозреваю, что отправка id в объекте personsполезная нагрузка JSON позволит сохранить это правильно.Однако по разным причинам это невозможно во время выполнения.Например, вот как это показано на странице «Сохранение данных» CakePHP Book ...

$data = [
  'title' => 'First Post',
  'user' => [
      'id' => 1,
      'username' => 'mark'
  ]
];

...
$article = $articles->newEntity($data, [
    'associated' => ['Users']
]);

$articles->save($article);

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

  if ($this->request->is('post') && !empty($this->request->getData())) {
    $data = $this->request->getData();
    $newEntity = $this->Users->Persons->newEntity($data, ['associated' => 'Persons']);

    $savedEntity =  $this->Users->Persons->save($newEntity);
  ...

Работа

Теперь я считаю, что это было возможно в CakePHP 2.X, как указано в этот ответ от ndm на аналогичный вопрос, где человекпытается сохранить связанную сущность belongsTo и ее родительскую сущность hasOne в одном запросе через сущность belongsTo.

Это ожидаемое поведение, saveAssociated () предназначено не только для сохранениясвязанные записи, он также сохранит основную запись, поэтому вы должны использовать только saveAssociated (), не нужно вручную устанавливать внешний ключ и т. д. CakePHP сделает это автоматически.

Контроллер

public function create() {
    if ($this->request->is('post') && !empty($this->request->data)):
        $this->CandidatesProblemReport->create();
        if ($this->CandidatesProblemReport->saveAssociated($this->request->data)):
            // ...
        endif;
    endif;
}

Однако я не могу найти или использовать метод saveAssociated() для объекта Cake\ORM\Table, который Users сущность наследуется от, в документации.Вызов этого метода приводит к ошибке «not found».Этот метод существует только для объекта Cake\ORM\Association, как описано в документации .Если я не пропускаю очевидное, есть ли способ использовать это или он используется внутри BelongsTo() и его родственными методами?

Запись / сброс сущности

Использование Cake\Log\Log::error($newEntity); илиdie(var_dump($newEntity)); показывает Users данные полезной нагрузки, гидратированной в объект, но я не вижу прикрепленного объекта Persons (см. Ниже).

object(MyPlugin\Model\Entity\User)[299]
  public 'username' => string 'foo' (length=3)
  public 'password' => string 'bar' (length=3)
  public '[new]' => boolean true
  public '[accessible]' => 
    array (size=5)
      '*' => boolean false
      'person_id' => boolean true
      'username' => boolean true
      'password' => boolean true
      'person' => boolean true
  public '[dirty]' => 
    array (size=2)
      'username' => boolean true
      'password' => boolean true
  public '[original]' => 
    array (size=0)
      empty
  public '[virtual]' => 
    array (size=0)
      empty
  public '[hasErrors]' => boolean false
  public '[errors]' => 
    array (size=0)
      empty
  public '[invalid]' => 
    array (size=0)
      empty
  public '[repository]' => string 'MyPlugin.Users' (length=17) 

Попытка \Cake\Log\Log::error($savedEntity); ничего не показываетв файле журнала.

save() аргументы ассоциации

Другое решение, которое я рассмотрел, - это использование $options['associated] из save(), как показано в документации (выдержка ниже).Если для этого параметра установлено значение true, как показано ниже, ошибка все еще возникает.

save (Cake \ Datasource \ EntityInterface $ entity, array $ options [])

... ассоциирован : если true, то будет сохранен ассоциированный 1-й уровеньсущности, поскольку они находятся в переданном $ entity, когда свойство, определенное для ассоциации, помечено как грязное.Если массив, он будет интерпретирован как список ассоциаций, которые будут сохранены.Можно использовать различные параметры для сохранения в связанных табличных объектах, используя этот ключ, задав пользовательским параметрам значение массива.Если ложь, никакие связанные записи не будут сохранены.(по умолчанию: true) ...

UsersController.php:

  if ($this->request->is('post') && !empty($this->request->getData())) {
    $data = $this->request->getData();
    $newEntity = $this->Users->newEntity($data, ['associated' => 'Persons']);

    $savedEntity =  $this->Users->save($newEntity, ['associated' => true]);
  ...

Резюме

Не пройдя через PersonsController.php и не применив отношения hasOne, мне не очень повезло получить мои Users и Persons данные для сохранения через UsersController.php.

Если я пропустил какую-либо важную информацию, или у вас есть вопросы / вам нужно больше вопросов, пожалуйста, задавайте! Возможно, я упустил что-то очевидное, но я был бы признателен за любые возможные предложения / решения.

1 Ответ

0 голосов
/ 01 июня 2019

Как идентифицировал @ndm, ошибка заключалась в опубликованных данных. Согласно «Сохранение данных: сохранение принадлежностей для ассоциаций» страница документации:

При сохранении принадлежащих принадлежностей, ORM ожидает отдельную вложенную сущность, названную с единственным , подчеркиванием версией имени ассоциации.

Они отправили ключ persons должен был быть person. Точно так же, если бы объект был назван PersonSnapshots, соответствующий ключ в полезной нагрузке, гидратированный в объекты, должен был бы быть person_snapshot.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...