(doctrine2 + symfony2) cascading remove: нарушение ограничений целостности 1451

Во-первых, извините за моего бедного английского...

Я получил четыре объекта: User, Application, Bundle и Entity. Вот их отношения (с каскадом persist и remove, см. Код ниже):

  • Пользователь 1-n Приложение
  • Приложение 1-n Пакет
  • Пакет 1-n Сущность

Он работает нормально. Но пользователь может иметь два своих объекта по умолчанию, и мне нужно получить к ним доступ напрямую.

Итак, я добавляю в User два поля, entity1 и entity2, с отношением 1-1. И теперь мое приложение вылетает:

An exception occurred while executing 'DELETE FROM bundle WHERE id = ?' with params {"1":13}:

SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`misc`.`entity`, CONSTRAINT `FK_E284468F1FAD9D3` FOREIGN KEY (`bundle_id`) REFERENCES `bundle` (`id`))

Я пробовал несколько вещей, в том числе те, которые были основаны в этом сообщении, но я не смог его исправить.

Любая помощь приветствуется, спасибо заранее.

РЕДАКТИРОВАТЬ: Мне нужно указать, что отношения User- > Entity опциональны: Пользовательский сущность1 и entity2 могут быть нулевыми. Ошибка происходит, даже если они являются нулевыми.

Вот определения моих сущностей:

# User :
    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $applications;

    /**
     * @ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
     * @ORM\JoinColumn(name="entity1_id", referencedColumnName="id")
     */
    private $entity1;

    /**
     * @ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
     * @ORM\JoinColumn(name="entity2_id", referencedColumnName="id")
     */
    private $entity2;

#Application :
    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $bundles;

    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    protected $user;

#Bundle :
    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
     * @ORM\JoinColumn(name="application_id", referencedColumnName="id")
     */
    protected $application;

    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $entitys;

#Entity :
    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
     * @ORM\JoinColumn(name="bundle_id", referencedColumnName="id")
     */
    protected $bundle;

Ответ 1

Итак, благодаря этому французскому форуму, я исправил проблему.

Мне нужно было добавить nullable = true и onDelete = "SET NULL" в @ORM\JoinColumn

Вот работоспособная конфигурация, возможно, это поможет кому-то:

#User.
    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Application", mappedBy="user", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $applications;

    /**
     * @ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
     * @ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
     */
    private $entity1;

    /**
     * @ORM\OneToOne(targetEntity="\sfCommands\ContentBundle\Entity\Entity")
     * @ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
     */
    private $entity2;

#Application.
    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", mappedBy="application", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $bundles;

    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\UserBundle\Entity\User", inversedBy="applications", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
     */
    protected $user;

#Bundle.
    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Application", inversedBy="bundles", cascade={"persist"})
     * @ORM\JoinColumn(name="application_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
     */
    protected $application;

    /**
     * @ORM\OneToMany(targetEntity="\sfCommands\ContentBundle\Entity\Entity", mappedBy="bundle", cascade={"remove"}, orphanRemoval=true)
     * @ORM\OrderBy({"name" = "ASC"})
     */
    protected $entitys;

#Entity.
    /**
     * @ORM\ManyToOne(targetEntity="\sfCommands\ContentBundle\Entity\Bundle", inversedBy="entitys", cascade={"persist"})
     * @ORM\JoinColumn(name="bundle_id", referencedColumnName="id", nullable=true, onDelete="SET NULL")
     */
    protected $bundle;

Ответ 2

Используйте onDelete = "CASCADE", если вы используете аннотацию

/**
 * @ORM\ManyToOne(targetEntity="Report", inversedBy="responses")
 * @ORM\JoinColumn(name="reportId", referencedColumnName="id",onDelete="CASCADE")
 */

Использовать onDelete: CASCADE, если вы используете yml

joinColumn:
      name: pid
      referencedColumnName: id
      onDelete: CASCADE

Ответ 3

onDelete = "CASCADE" также отлично работает. Но не забудьте запустить доктрину app/console: schema: update --force до того, как изменения уровня DB вступят в силу.

Ответ 4

orphanRemoval несколько раз не работает, потому что это зависит от (получает расписание) PersistencCollection. И мы могли бы называть ArrayCollection#removeElement().

Ниже приведен фрагмент PersistencCollection#remove()

    if ($this->association !== null &&
        $this->association['type'] & ClassMetadata::TO_MANY &&
        $this->owner &&
        $this->association['orphanRemoval']) {
        $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
    }

и ArrayCollection этого не делает.