I have this entity:
<?php
namespace Comakai\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM,
    Symfony\Component\Validator\Constraints as Assert;
/**
 * @ORM\Entity
 */
class Stuff {
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    /**
     * @ORM\Column(type="text")
     * @Assert\NotBlank()
     */
    private $content;
    /**
     * @ORM\ManyToMany(targetEntity="Apple", cascade={"persist"})
     */
    private $apples;
    /**
     * @ORM\ManyToMany(targetEntity="Pig")
     */
    private $pigs;
    public function __construct() {
        $this->apples = new \Doctrine\Common\Collections\ArrayCollection();
        $this->pigs = new \Doctrine\Common\Collections\ArrayCollection();
    }
    public function setApples($apples) {
        $this->getApples()->clear();
        foreach ($apples as $apple) {
            $this->addApple($apple);
        }
    }
    public function setPigs($pigs) {
        $this->getPigs()->clear();
        foreach ($pigs as $pig) {
            $this->addPig($pig);
        }
    }
    /**
     * Get id
     *
     * @return integer 
     */
    public function getId() {
        return $this->id;
    }
    /**
     * Set content
     *
     * @param text $content
     */
    public function setContent($content) {
        $this->content = $content;
    }
    /**
     * Get content
     *
     * @return text 
     */
    public function getContent() {
        return $this->content;
    }
    /**
     * Add apples
     *
     * @param Comakai\MyBundle\Entity\Apple $apples
     */
    public function addApple(\Comakai\MyBundle\Entity\Apple $apples) {
        $this->apples[] = $apples;
    }
    /**
     * Get apples
     *
     * @return Doctrine\Common\Collections\Collection 
     */
    public function getApples() {
        return $this->apples;
    }
    /**
    * Add pigs
    *
    * @param Comakai\MyBundle\Entity\Pig $pigs
    */
    public function addPig(\Comakai\MyBundle\Entity\Pig $pigs) {
        $this->pigs[] = $pigs;
    }
    /**
    * Get pigs
    *
    * @return Doctrine\Common\Collections\Collection 
    */
    public function getPigs() {
        return $this->pigs;
    }
}
and this listener:
<?php
namespace Comakai\MyBundle\Listener;
use Comakai\MyBundle\Util\SluggerParser
    Doctrine\ORM\Event\OnFlushEventArgs,
    Comakai\MyBundle\Entity\Stuff,
    Comakai\MyBundle\Entity\Apple,
    Comakai\MyBundle\Entity\Pig;
class Listener {
    /**
     * @param \Doctrine\ORM\Event\OnFlushEventArgs $ea
     */
    public function onFlush(OnFlushEventArgs $ea) {
        $em = $ea->getEntityManager();
        $uow = $em->getUnitOfWork();
        foreach ($uow->getScheduledEntityInsertions() AS $entity) {
            $this->save($entity, $em, $uow);
        }
        foreach ($uow->getScheduledEntityUpdates() AS $entity) {
            $this->save($entity, $em, $uow);
        }
    }
    public function save($entity, $em, $uow) {
        if ($entity instanceof Stuff) {
            $pigRepository = $em->getRepository('Comakai\MyBundle\Entity\Pig');
            $content = $entity->getContent();
            preg_match_all('/@@ pig:(\d+) @@/i', $content, $matches);
            $entity->getPigs()->clear();
            foreach($matches[1] as $pigID) {
                $pig = $pigRepository->find($pigID);
                if(!empty($pig)) {
                    $entity->addPig($pig);
                }
            }
            $entity->setContent($content);
            $meta = $em->getClassMetadata(get_class($entity));
            $uow->recomputeSingleEntityChangeSet($meta, $entity);
            $uow->computeChangeSet($meta, $entity);
        }
    }
}
And it works fine if apple's collection is empty, but if it has some item I get a duplication error.
How can I tell to the UnitOfWork that I only want to recalculate the pig's collection?
UPDATE
There is a new preFlush event (https://github.com/doctrine/doctrine2/pull/169) and I think this kind of things can be done there. That PR is not in the branch I'm using but let's try it!
When updating an entity during a listener's onFlush event, all you need to call is computeChangeSet():
// make changes to entity
$entity->field = 'value';
// or assign an existing entity to an assocation
$entity->user = $myExistingUserEntity;
$entity->tags->add($myExistingTagEntity);
$meta = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($meta, $entity);
If you're creating other entities too, you need to persist them and compute their changes first!
$myNewUserEntity = new Entity\User;
$myNewTagEntity = new Entity\Tag;
$entity->user = $myNewUserEntity;
// make sure you call add() on the owning side for *ToMany associations
$entity->tags->add($myNewTagEntity);
$em->persist($myNewUserEntity);
$em->persist($myNewTagEntity);
$metaUser = $em->getClassMetadata(get_class($myNewUserEntity));
$uow->computeChangeSet($metaUser, $myNewUserEntity);
$metaTag = $em->getClassMetadata(get_class($myNewTagEntity));
$uow->computeChangeSet($metaTag, $myNewTagEntity);
$meta = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($meta, $entity);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With