Составной ключ и форма


У меня есть следующие ассоциации в моей базе данных (упрощенная версия):

схема БД

Это ассоциация "многие ко многим", но с атрибутом в таблице соединений, поэтомуя должен использовать ассоциации "один ко многим" / "многие к одному" .

У меня есть форма, в которой я могу добавить столько связей, сколько захочу, к одному элементу заказа и создать его одновременно (в основном вдохновленный учебником Как встроить коллекцию форм из документации.

Когда Я разместив форму, я получаю следующую ошибку:

Сущность типа TESTMyBundleEntityRelation имеет идентичность через чужая сущность TESTMyBundleEntityOrder, однако эта сущность не имеет самотождественности. Вы должны вызвать EntityManager#persist () на Связанный объект и убедитесь, что был сгенерирован идентификатор прежде чем пытаться сохранить 'TESTMyBundle EntityRelation'. В случае после вставки ID поколения (таких как MySQL автоинкремент или PostgreSQL SERIAL) это означает вы должны вызвать EntityManager#flush() между обеими персистентными операциями.

Я понимаю эту ошибку, потому что доктрина пытается сохранить объект(ы) Relation, связанный с порядком, поскольку у меня есть опция cascade={"persist"} в отношении OneToMany. Но как мне избежать такого поведения?

Я попытался удалить cascade={"persist"} и вручную сохранить сущность, но я получаю ту же ошибку (потому что мне нужно flush() заказать, чтобы получить идентификатор, и когда я делаю это, у меня есть то же самое сообщение об ошибке).
Я тоже пытался. detach() объекты Relation перед flush(), но без успеха.

3 5

3 ответа:

Эта проблема кажется уникальной, если 1) вы используете объединенную таблицу с составными ключами, 2) компонент forms и 3) объединенная таблица является сущностью, которая создается полем 'collection' компонента form. Я видел, что у многих людей есть проблемы, но не так много решений, поэтому я решил поделиться своими.

Я хотел сохранить свой составной первичный ключ, так как хотел убедиться, что в базе данных сохранится только один экземпляр из двух внешних ключей. С помощью эта установка сущности как пример
/** @Entity */
class Order
{
    /** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
    private $items;

    public function __construct(Customer $customer)
    {
        $this->items = new Doctrine\Common\Collections\ArrayCollection();
    }
}

/** @Entity */
class Product
{
    /** @OneToMany(targetEntity="OrderItem", mappedBy="product") */
    private $orders;
    .....

    public function __construct(Customer $customer)
    {
        $this->orders = new Doctrine\Common\Collections\ArrayCollection();
    }
}

/** @Entity */
class OrderItem
{
    /** @Id @ManyToOne(targetEntity="Order") */
    private $order;

    /** @Id @ManyToOne(targetEntity="Product") */
    private $product;

    /** @Column(type="integer") */
    private $amount = 1;
}

Проблема, с которой я столкнулся, если бы я строил объект Order в форме, которая имела поле коллекции OrderItems, Я не смог бы сохранить сущность OrderItem, не сохранив сначала сущность Order (поскольку doctrine/SQL нуждается в идентификаторе порядка для составного ключа), но Doctrine EntityManager не позволял мне сохранить объект Order, который имеет атрибуты OrderItem (потому что он настаивает на сохранении их en mass вместе). Вы не можете отключить каскад, так как он будет жаловаться, что вы не сохранили сначала связанные сущности, и вы не можете сохранить связанные сущности до сохранения Order. Какая загадка. Мое решение состояло в том, чтобы удалить связанные сущности, сохранить Order, а затем повторно ввести связанные сущности в объект Order и сохранить его снова. Поэтому сначала я создал функцию массового присвоения атрибута ArrayCollection $items

class Order
{
    .....
    public function setItemsArray(Doctrine\Common\Collections\ArrayCollection $itemsArray = null){
    if(null){
        $this->items->clear();
    }else{
        $this->items = $itemsArray;
    }
    ....
}

А затем в моем контроллере, где я обрабатываю форму для заказа.

//get entity manager
$em = $this->getDoctrine()->getManager();
//get order information (with items)
$order = $form->getData();
//pull out items array from order
$items = $order->getItems();
//clear the items from the order
$order->setItemsArray(null);
//persist and flush the Order object
$em->persist($order);
$em->flush();

//reintroduce the order items to the order object
$order->setItemsArray($items);
//persist and flush the Order object again ):
$em->persist($order);
$em->flush();

Это отстой, что вы должны persist и flush дважды (смотрите подробнее здесьPersist object with two foreign identities in doctrine ). Но это учение для вас, со всей его силой, оно, конечно, может поставить вас в затруднительное положение. Но, к счастью, вам придется делать это только при создании нового объекта, а не при редактировании, потому что объект уже находится в базе данных.

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

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

В итоге я создал отдельный первичный ключ в моей таблице Relation (вместо того, чтобы иметь составной ключ).
Похоже, что это грязное решение, и я уверен, что есть лучший способ справиться с этой ситуацией, но пока это работает.

Вот моя Relations сущность:

/**
 * Relation
 *
 * @ORM\Entity
 */
class Relation
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="Contact", inversedBy="relation")
     */
    protected $contact;

    /**
     * @ORM\ManyToOne(targetEntity="Order", inversedBy="relation")
     */
    protected $order;

    /**
     * @var integer
     *
     * @ORM\Column(name="invoice", type="integer", nullable=true)
     */
    private $invoice;

    //Rest of the entity...

Затем я добавил опцию cascade={"persist"} в отношении OneToMany с Order:

/**
 * Orders
 *
 * @ORM\Entity
 */
class Order
{   
    /**
     * @ORM\OneToMany(targetEntity="Relation", mappedBy="order", cascade={"persist"})
     */
    protected $relation;

    //Rest of the entity...

И вуаля!