Symfony2 и Doctrine: отношение OneToMany дублирует данные в таблице

У меня есть две таблицы: статьи и электронные письма. Таблица электронных писем будет содержать электронные письма, связанные с конкретными статьями, например:

id    |  article_id |     email
1     |      1      |  [email protected]
2     |      1      |  [email protected]
3     |      2      |  [email protected]
4     |      2      |  [email protected]

Так далее....

Существует связь между article_id и идентификатором из таблицы article.

В моей сущности у меня есть этот код:

class Articles
{ 
    ....
     /**
      * @ORM\OneToMany(targetEntity="\Acme\DemoBundle\Entity\Emails", mappedBy="articles")
      */
      private $emails_rel; 

    ...
}

class Emails
{ 
    /**
     * @ORM\ManyToOne(targetEntity="\Acme\DemoBundle\Entity\Articles", inversedBy="emails_rel", cascade={"all"})
     * @ORM\JoinColumn(name="article_id", referencedColumnName="id")
     **/
    private $articles; 
}

В моем контроллере я делаю несколько тестов, в которых я сохраняю или не сохраняю некоторые объекты. В конце я делаю флеш

$em->flush(); 

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

$em->getUnitOfWork()->getScheduledEntityInsertions()

Я получаю пустой массив.

Есть идеи, почему? Большое спасибо.

РЕДАКТИРОВАТЬ:

Вот тест:

$articl = new Articles();
$articl = $em->createQuery("SELECT p FROM Acme\DemoBundle\Entity\Articles p WHERE p.bMailToMatch = 1")->getResult();
$nb = count($articl);

// BECAUSE I WILL WORK WITH A LOTS OF ENTRIES - PREVENT MEMORY OVERFLOW
$em->clear();    

if(isset( $articl )) {
    foreach($articl as $i => $art) {
        $array_emails = null;

        $art_emails = $art->getEmailsRel();
        foreach ($art_emails as $e) {
            $array_emails[] = $e->getEmail();
        }

        $art_id = $art->getId ();
        echo "\n\n---------- $i ----------\n " . $art->getDoi ();

        // TAKES ARTICLE WITH ZERO EMAIL
        if (!isset($array_emails) ) {
            $updated_article = $em->getRepository('AcmeDemoBundle:Articles')->findOneBy(array( 'id' => ($art_id)    )) ; 
            // Because of the clearing of the entity manager
            echo"\n\n$art_id Article DOI \n".$updated_article->getDoi();
            echo "\n==>Put the BMailToMatch's flag to 0";
            $updated_article->setBMailToMatch(0);
            $em->persist($updated_article);
        }
        else {
            echo " ==> ok\n";
        }

        if (($i % 3) == 0) {
            $em->flush();
            $em->clear();
        }
    }

    $em->flush();
    $em->clear();
}

return new Response ( "\n\nFinished!!\n" );             

person Milos Cuculovic    schedule 20.12.2013    source источник
comment
Не могли бы вы поместить код, который вы тестируете?   -  person Rafael Adel    schedule 20.12.2013
comment
@RafaelAdel, я загрузил код со своего контроллера   -  person Miloš    schedule 20.12.2013
comment
Будет ли работать, если удалить $em->clear(); отовсюду? Также почему вы просто не делаете один запрос update, пример UPDATE Acme\DemoBundle\Entity\Articles p SET p.bMailToMatch = 0 WHERE p.emails_rel IS EMPTY   -  person Alexey B.    schedule 24.12.2013
comment
Не могли бы вы уточнить, что вы имеете в виду под дубликатом?   -  person Jim Martens    schedule 25.12.2013
comment
Для чего это выражение if? if (($i % 3) == 0) { Почему вы хотите сбрасывать и очищать только каждую третью итерацию?   -  person Markus Kottländer    schedule 26.12.2013
comment
PS: После того, как я два раза удалил свой собственный ответ, мне было бы очень интересно решить эту проблему. :)   -  person Markus Kottländer    schedule 26.12.2013
comment
Как сказал @JimMartens, укажите, что вы подразумеваете под повторяющимися данными. Вы говорите об одном и том же адресе электронной почты [email protected], который несколько раз появляется в таблице адресов электронной почты? Присмотритесь к своим отношениям: в каждой статье может быть только одно электронное письмо или несколько? Каждое письмо может содержать только одну или несколько статей? Также кажется, что вы пытаетесь выполнять массовые вставки (поправьте меня, если я ошибаюсь) и делаете несколько запросов, чтобы получить дополнительный объект $updated_article у вас уже есть $art, эта логика в дополнение к сбросу каждые i%3==0 может вызвать некоторые «неожиданные ' поведение.   -  person Onema    schedule 28.12.2013
comment
Может вместо clear отсоединять уже обработанные статьи от менеджера сущностей после каждого сброса. Clear просто удаляет ссылки из Doctrine ORM на ваши сущности, но у вас все еще есть объекты в массиве.   -  person Guillermo Gutiérrez    schedule 31.12.2013
comment
Привет, Милош, если мой ответ правильный, можешь ли ты наградить меня, чтобы я получил награду? Спасибо и с Новым годом :)   -  person lenybernard    schedule 01.01.2014


Ответы (2)


Я не заметил вашу проблему до того, как воспроизвел ее, но теперь это очевидно. Ваша проблема возникает из-за использования метода ясности. На самом деле, если вы очистите диспетчер сущностей, он забудет об их существовании и сохранит их как новые.

Если у вас есть проблемы с производительностью, вы можете ограничить количество элементов, с которыми вы имеете дело, и делать это рекурсивно, пока не закончите.

Я надеюсь, что это помогло вам ;)

Удачи

Ваше здоровье

person lenybernard    schedule 28.12.2013
comment
это то, что я увидел, когда прочитал четкий вызов. Доктрина AFAIK сохраняет сопоставление объекта с записью базы данных в диспетчере сущностей (поэтому она не проверяет идентификатор в таблице каждый раз). Поэтому, если вы очистите их, каждый объект, который у вас есть, будет казаться новым. - person Carlos Robles; 30.12.2013

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

$articles_with_mail_to_match_true = $em->createQuery("SELECT p FROM Acme\DemoBundle\Entity\Articles p WHERE p.bMailToMatch = 1")->getResult();

foreach($articles_with_mail_to_match_true as $i => $article_with_mail_to_match_true) 
{
    if ( count($article_with_mail_to_match_true->getEmailsArray()) < 1 ) 
    {
        $article_copy_object = $em->getRepository('AcmeDemoBundle:Articles')->findOneBy(array( 'id' => ($article_with_mail_to_match_true->getId()) )) ; 

        $article_copy_object->setBMailToMatch(0);

        $em->persist($article_copy_object);
    }

    if (($i % 3) == 0) {
        $em->flush();
    }
}

$em->flush();

Кажется, единственное, что вы сохраняете, это $ article_copy_object после изменения значения one. Если это так, то либо A. в вашем объекте что-то не так, чего мы не можем увидеть в этом примере кода, либо B. это происходит где-то еще, а не во время этой операции.

person Jessie Green    schedule 27.12.2013