2012-06-02 21 views
5

que tienen esta entidad:¿Cómo actualizar una colección manyToMany de una entidad en onFlush event listener?

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

y esto oyente:

<?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); 

     } 
    } 

} 

y funciona bien si la recolección de la manzana está vacía, pero si tiene algún elemento que conseguir un error de duplicación.

¿Cómo puedo saber a la UnitOfWork que sólo quiero volver a calcular la colección del cerdo?

ACTUALIZACIÓN

Hay un nuevo evento de pre-limpieza (https://github.com/doctrine/doctrine2/pull/169) y creo que este tipo de cosas se pueden hacer allí. ¡Ese RP no está en la rama que estoy usando, pero intentémoslo!

Respuesta

4

Al actualizar una entidad durante el evento onFlush de un oyente, todo lo que necesita para llamar es 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); 

Si va a crear otras entidades demasiado, es necesario persistir ellos y calcular sus cambios primero !

$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); 
+1

Lo sentimos, pero el pre-limpieza resolvió mi problema. Modificar el UoW es una especie de piratería y muy desordenado, puede volverte loco con las colecciones. – coma

+0

Coma, estoy de acuerdo. Tu pregunta era sobre hacer cosas en onFlush(), y esta es la única forma de hacerlo. Si su caso de uso puede implementarse en preFlush(), ¡eso es genial! – jmlnik

+0

Bueno ... parece que el evento preFlush tan esperado hace casi cualquier cosa ... todas las listas programadas están vacías. – coma

1

Esto se puede hacer con el nuevo evento preFlush (Symfony 2.1).

Añadir un escucha para el evento (es una mala práctica de inyectar todo el contenedor de servicios, pero sometimes is the way to go):

services: 
    mybundle.updater.listener: 
     class: Foo\MyBundle\Listener\UpdaterListener 
     arguments: ["@service_container"] 
     tags: 
      - { name: doctrine.event_listener, event: preFlush } 

y el oyente debe ser algo como:

<?php 

namespace Foo\MyBundle\Listener; 

use Doctrine\ORM\Event\PreFlushEventArgs; 
use Foo\MyBundle\SomeInterface; 

class UpdaterListener 
{ 
    /** 
    * @param \Doctrine\ORM\Event\PreFlushEventArgs $ea 
    */ 
    public function preFlush(PreFlushEventArgs $ea) 
    { 
     /* @var $em \Doctrine\ORM\EntityManager */ 
     $em = $ea->getEntityManager(); 
     /* @var $uow \Doctrine\ORM\UnitOfWork */ 
     $uow = $em->getUnitOfWork(); 

     foreach ($uow->getScheduledEntityInsertions() as $entity) { 

      if($entity instanceof SomeInterface) { 

       /* 
       * do your stuff here and don't worry because 
       * it'll execute before the flush 
       */ 

      } 

     } 

    } 

} 
0

Cuando querer actualizar la entidad actual que está enviando a onFlush y también la creación de una asociación a esa entidad

(para este ejemplo voy a utilizar Parent objeto y child objeto)

Digamos que cuando cambie la propiedad del objeto padre 'estresado' a 1 También quiero asociar una marca nueva child objeto para el objeto principal de mi método onflush, que se verá algo como esto:

public function onFlush(onFlushEventArgs $args) 
{ 
    .... 

    $child = $this->createChild($em, $entity); // return the new object. just the object. 
    $uow->persist($child); 
    $childMeta = $em->getMetadataFactory()->getMetadataFor('AcmeFamilyTreeBundle:Child'); 
    $uow->computeChangeSet($childMeta, $child) 

    $parent->setStressed(1); 
    $parentMeta = $em->getMetadataFactory()->getMetadataFor('AcmeFamilyTreeBundle:Parent'); 
    $uow->recomputeSingleEntityChangeSet($parentMeta, $parent) 
} 

Así que hay que ver:

  1. que necesita para persistir el objeto secundario utilizando $uow->persist()no$em->persist()
  2. computeChangeSet en el objeto secundario.
  3. recomputeSingleEntityChangeSet en el objeto padre

Para obtener ayuda con la creación del método onFlush, por favor ver the documentation

Cuestiones relacionadas