Symfony2: отношение ManyToMany и тип формы Collection

Я столкнулся с большой проблемой с типом формы коллекции для отношения ManyToMany в моем проекте Symfony2.

Среда: - Symfony 2.0.14 - Доктрина 2.1

Вот код:

Почтовый объект

class Post
{
/**
 * @ORM\Id
 * @ORM\Column(type="integer")
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @ORM\Column(type="string", length=255, nullable=false)
 */
private $title;

/**
 * @ORM\ManyToMany(targetEntity="Tag", inversedBy="posts", cascade={"persist"})
 * @ORM\JoinTable(name="posts_tags")
 */
private $tags;

public function setTags(\Doctrine\Common\Collections\ArrayCollection $tags)
{
    foreach($tags as $tag)
    {
        $tag->addSnippet($this);
    }
}

public function addTag(\My\BlogBundle\Entity\Tag $tags)
{
    $this->tags[] = $tags;
}

public function getTags()
{
    return $this->tags;
}

Объект тега

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

/**
 * @var string $name
 * 
 * @ORM\Column(type="string", length=100, unique=true, nullable=false)
 */
private $name;

/**
 * @var Snippet
 * 
 * @ORM\ManyToMany(targetEntity="Post", mappedBy="tags") 
 */
private $posts;

public function addSnippet(\My\BlogBundle\Entity\Post $posts)
{
    $this->posts[] = $posts;
}

Класс формы PostType

->add('tags', 'collection', array(
            'type' => new TagType(),
            'allow_add' => true,
            'prototype' => true,
            'by_reference' => false,
        ))

Все работает отлично, но выдает ошибку при вставке тега, который уже существует в базе данных SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'tag1' for key 'UNIQ_6FBC94265E237E06'.

У вас есть обходной путь для этой проблемы или я что-то упустил? Мой контроллер — это стандартный CRUD-контроллер, сгенерированный app/console.enter code here


person DaveW    schedule 31.05.2012    source источник


Ответы (1)


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

public function existTags(\Doctrine\Common\Collections\ArrayCollection $tags)
{
    foreach($this->tags as $tag)
    {
        if ( $tag->getID() === $tags->getId() )
            return true;
    }
    return false;
}

А потом

public function addTag(\My\BlogBundle\Entity\Tag $tags)
{
    if ( !$this->existTag($tags) );
        $this->tags[] = $tags;
}

Это мои модели:

namespace models;


/**
 * @Entity
 * @Table(name="tag")
 */
class Tag
{

    /**
     * @Id @Column(type="integer", nullable=false, name="id")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;


    /**
     * @Column(type="string", nullable=false)
     */
    protected $name;

    public function getId(){ return $this->id; }
    public function getName(){ return $this->name; }

    public function setId($id){ $this->id = $id; }
    public function setName($name){ $this->name = $name; }


}

Запись в блоге:

namespace models;

use \Doctrine\Common\Collections\ArrayCollection;

/**
 * @Entity
 * @Table(name="entry")
 */
class Entry
{

    /**
     * @Id @Column(type="integer", nullable=false, name="id")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Column(type="string", nullable=false, name="body")
     */
    protected $body;



    /**
     * @ManyToMany(targetEntity="Tag")
     * @JoinTable(name="entry_tagged",
     *      joinColumns={@JoinColumn(name="entry_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="tag_id", referencedColumnName="id")}
     *      )
     */
    private $tags;





    public function __construct(){
        $this->tags = new ArrayCollection();
    }

    public function existTag(models\Tag $tag)
    {
        foreach($this->tags as $temp)
        {
            if ( $tag->getID() === $temp->getId() )
                return true;
        }
        return false;
    }

    public function addTag(models\Tag $tag)
    {
        if ( !$this->existTag($tag) );
            $this->tags->add($tag);
    }


    public function getId(){ return $this->id; }
    public function getBody(){ return $this->body; }
    public function getTags(){ return $this->tags; }

    public function setId($id){ $this->id = $id; }
    public function setBody($body){ $this->body = $body; }
    public function setTags($tags){ $this->tags = $tags; }

}

Наконец, используемая таблица соединения имеет имя «entry_tagged» и выглядит следующим образом:

entry_id | tag_id
person manix    schedule 31.05.2012
comment
Похоже, он пытается вставить другой тег с тем же именем, поэтому мне это немного странно. - person DaveW; 31.05.2012
comment
да, по этой причине вам нужно проверить, существует ли новое для вставки в коллекции $ tags, например, метод выше - person manix; 31.05.2012
comment
Я поместил его в сущность Post - все та же ошибка. Я что-то пропустил? - person DaveW; 31.05.2012
comment
Вы можете отредактировать сообщение, чтобы увидеть все атрибуты объекта тега и сообщения? - person manix; 31.05.2012
comment
Что ж, я воспроизвел вашу модель, и она сработала, но в таблицу posts_tags я включил еще один столбец auto_increment в качестве первичного ключа. И это работает нормально - person manix; 01.06.2012
comment
Спасибо за ваш вклад, я удалил свои сущности и снова создам их с нуля. Ваш метод должен работать, так как я создал похожие сущности (но с ассоциацией OneToMany), и мне нужно проверить существующие экстримы. Я просто подумал, что Doctrine достаточно умна, чтобы проверить это на стороне ORM :) - person DaveW; 01.06.2012
comment
Не могли бы вы поделиться кодом, пожалуйста. Я определенно что-то упускаю или я слишком глуп, чтобы понять, как это должно работать, но все равно получаю ту же ошибку... - person DaveW; 02.06.2012
comment
@DaveW, пост обновлен. Посмотрите на используемую структуру, она позволяет избежать повторного использования одного и того же тега. - person manix; 04.06.2012
comment
Попробую позже. Большое спасибо за ваш ответ! - person DaveW; 04.06.2012