Почему мой каскад продвижения заблокирован ограничениями внешнего ключа?

У меня есть следующая структура в базе данных:

введите здесь описание изображения

В моем коде я сначала создаю компанию, затем журнал, затем ряд расписаний, затем пользователя, затем ответственность журнала. Что-то вроде этого (не точный код для краткости, но я не думаю, что это имеет значение):

<?php

$company = new Company();
$company->setSomething(123);

$logbook = new Logbook();
$logbook->setSomething('abc');
$logbook->setCompany($company);

$schedules = array();
for ($x=0; $x<$something; $x++) {
  $schedule = new Schedule();
  $schedule->setSomething(doSomethingWithSomething($something[$x]);
  $schedule->setLogbook($logbook);
  $schedules[] = $schedule;
}

$user = new User();
$user->setSomething('something');
$user->setCompany($company);
$user->setCurrentLogbook($logbook);

$logbookResponsibility = new LogbookResponsibility();
$logbookResponsibility->setLogbook($logbook);
$logbookResponsibility->setResponsibilityId(1);
$logbookResponsibility->setUser($user);

$errors = someFormOfCheck();

if (!$errors) {
  $user->save();
  $logbookResponsibility->save();
  foreach ($schedules as $schedule) {
    $schedule->save();
  }
}

Я получаю следующую ошибку:

<b>Fatal error</b>:  Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`logbook`.`logbook_responsibility`, CONSTRAINT `fk_logbook_responsbility_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON UPDATE CASCADE)' in C:\wamp\bin\php\php5.3.8\pear\propel\connection\DebugPDOStatement.php:90

Таблица logbook_responsibility состоит из трех внешних ключей, каждый из которых имеет RESTRICT для обновлений, потому что я не хочу, чтобы можно было удалить пользователя, журнал или тип ответственности, если существует ответственность в журнале. Однако я считаю, что проблема определяется порядком каскада.

Если я полностью вынимаю $logbookResponsibility частей, все работает отлично. Если я поставлю части $logbookResponsibility после $user->save();, а затем также выпущу $logbookResponsibility->save();, это также сработает отлично.

Тогда мой вопрос: где я ошибаюсь? Что я предполагаю неправильно или делаю неправильно, чтобы каскад работал? Или есть что-то еще, что может его остановить?

Обновление: если я сделаю следующее, все будет работать нормально:

$company->save();
$logbook->save();
$user->save();
$logbookResponsibility->save();
foreach ($schedules as $scheudle) {
  $schedule->save();
}

то есть, если я сначала сохраню их вручную, а не полагаюсь на каскад.


person LeonardChallis    schedule 21.02.2013    source источник


Ответы (1)


Я не могу дать вам хороший, прямой ответ на точную проблему, с которой вы сталкиваетесь, но я столкнулся с аналогичными запутанными проблемами с внешними ключами и отношениями в Propel. Теперь моей лучшей практикой стала установка идентификаторов вручную, а не предоставление Propel возможности управлять их настройкой через определенные отношения. В данном случае это означало бы вообще не вызывать $logbookResponsibility->setLogbook() или $logbookResponsibility->setUser(). Вместо этого позвоните $logbookResponsibility->setLogbookId() и $logbookResponsibility->setUserId() после того, как вы позвонили save() в $user и $logbook.

Propel делает много вещей по ссылке, что может иметь непредвиденные последствия, когда вы имеете дело с внешними ключами и устанавливаете отношения, вызывая методы, ожидающие объекты (например, setLogbook()), а не методы, ожидающие идентификатора отношения (например, setLogbookId()).

person Emmeram Morning    schedule 21.02.2013
comment
Справедливое замечание, спасибо, хотя обратите внимание на мое обновление. Я могу использовать тот же синтаксис (передавая объекты), если я просто выполняю ручное сохранение вместо каскадного. Я думаю, что теперь меня раздражает больше, чем то, что я не знаю, почему. - person LeonardChallis; 21.02.2013
comment
Я чувствовал ту же боль. Причина сводится к какой-то глубокой проблеме передачи по ссылке в Propel. Propel либо (а) пытается выполнить INSERT для $logbookResponsibility без предварительного выполнения INSERT для $user, либо (б) не может передать $user по ссылке где-то и выдает оператор UPDATE, который не включает user.id. Вы пытались сделать так, чтобы Propel сбрасывал подготовленный оператор, чтобы вы могли его посмотреть? - person Emmeram Morning; 21.02.2013
comment
Установка идентификаторов вручную — это боль, но если вы хотите сделать одно сохранение, чтобы каскадировать цепочку отношений, это, вероятно, то, что вам нужно сделать. Тем не менее, мое решение состояло в том, чтобы выполнять отдельные вызовы save() вручную, как в вашем обновлении. - person Jordan Kasper; 22.02.2013
comment
Установите это как ответ, поскольку он предоставляет много информации по этому вопросу, поэтому любые потенциальные читатели могут принять собственное обоснованное решение. большое спасибо! - person LeonardChallis; 06.03.2013