Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 and Doctrine: OneToMany relation duplicates data in the table

I have two tables: articles and emails. The emails table will contain emails related to the specific articles, for example:

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

Etc....

There is a relation between article_id and the id from the articles table.

In my entity, I have this code:

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; 
}

In my controller, i am doing some tests where I will persist or not some entities. At the end, I am doing a flush

$em->flush(); 

And the strange behaviour is that in my Emails table, the data is duplicated as soon as I am doing the flush. When testing the entity manager with

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

I am getting an empty array.

Any idea why? Thank you very much.

EDIT:

Here is the test:

$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" );             
like image 600
Milos Cuculovic Avatar asked Dec 20 '13 13:12

Milos Cuculovic


People also ask

What is manytoone mapping in doctrine?

This ManyToOne mapping is required. It tells Doctrine to use the category_id column on the product table to relate each record in that table with a record in the category table.

Can you map just one side of a relationship in doctrine?

But actually, you can think about any relationship in two directions: each GenusNote has one Genus. Or, each Genus has many GenusNote. And in Doctrine, you can map just one side of a relationship, or both. Let me show you. Open Genus and add a new $notes property:

Why is doctrine not seeing my OneToMany relationship?

If you don't delete these, then Doctrine continues to read the metadata from those XML files, and ignores your annotations. This could explain why Doctrine is not seeing your OneToMany relationship!

Is OneToMany bidirectional?

If we do OneToMany the normal way, it is always bidirectional. Our Address entity now needs to have a $user field. Looking at the database, we can see that our Address table now has a user_id column. This setup might look perfectly fine, but there are a couple of big problems.


2 Answers

I didn't caught your problem before I reproduced it but now it's a kind of obvious. Your problem comes from the use of the clear method. Actually, if you clear your entity manager, it forgets their existence and will persist them as new ones.

If you have performance issue, you can limit the number of items you deal with and do it recursively until you're done.

I hope it helped you ;)

Good Luck

Cheers

like image 142
lenybernard Avatar answered Oct 13 '22 12:10

lenybernard


If I am reading this correctly then a simplified version of the code would be as follows:

$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();

It seems the only thing you are persisting is the $article_copy_object after changing the the one value. If that is the case then either A. there is something wrong in your entity we can not see in this code example or B. it is happening elsewhere and not during this operation.

like image 43
Jessie Green Avatar answered Oct 13 '22 12:10

Jessie Green