2012-06-02 3 views
5

Ich habe diese Einheit:Wie kann man eine manyToMany-Auflistung einer Entität im onFlush-Ereignis-Listener aktualisieren?

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

und diese Zuhörer:

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

     } 
    } 

} 

Und es funktioniert gut, wenn Apfel Sammlung leer ist, aber wenn es einige Artikel hat ich einen doppelten Fehler.

Wie kann ich dem UnitOfWork mitteilen, dass ich nur die Sammlung des Schweins neu berechnen möchte?

UPDATE

Es gibt ein neues Vorspülen Ereignis (https://github.com/doctrine/doctrine2/pull/169) und ich denke, diese Art von Dingen, es getan werden kann. Diese PR ist nicht in der Branche, die ich benutze, aber lass es uns versuchen!

Antwort

4

, wenn ein Unternehmen während eines onFlush Ereignis des Zuhörers zu aktualisieren, alles, was Sie anrufen müssen, ist 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); 

Wenn Sie andere Einheiten zu erstellen, müssen Sie sie anhalten und deren Veränderungen berechnen zuerst !

$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

Sorry, aber das Vorspülen mein Problem gelöst. Ändern der UoW ​​ist eine Art von Hacking und sehr chaotisch, kann es Sie verrückt mit Sammlungen. – coma

+0

Koma, stimme ich zu. Ihre Frage bestand darin, Sachen in onFlush() zu tun, und das ist der einzige Weg, dies zu tun. Wenn Ihr Anwendungsfall stattdessen in preFlush() implementiert werden kann, ist das großartig! – jmlnik

+0

Nun ... es sieht so aus, als würde das PreFlush-Ereignis so ziemlich alles Mögliche tun ... alle geplanten Listen sind leer. – coma

1

Dies kann mit dem neuen preFlush-Ereignis (Symfony 2.1) geschehen.

einen Zuhörer auf das Ereignis hinzufügen (ist eine schlechte Praxis den gesamten Service-Container aber sometimes is the way to go zu injizieren):

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

Und der Hörer etwas sein sollte:

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

Wenn zu wollen Aktualisieren Sie die aktuelle Entität, die Sie senden, an onFlush und erstellen Sie auch eine Zuordnung zu dieser Entität

(für dieses Beispiel werde ich verwenden Parent Objekt und child Objekt)

Lassen Sie uns sagen, wenn ich das übergeordnete Objekt-Eigenschaft ‚betonte‘ zu 1 ändern verbinde ich möchte auch ein brandneues child Objekt auf das übergeordnete Objekt in meinem onflush Methode, es so etwas wie folgt aussehen:

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

So gibt man sehen:

  1. Sie Ihr Kind Objekt anhalten müssen mit $uow->persist()nicht$em->persist()
  2. computeChangeSet auf dem untergeordneten Objekt.
  3. recomputeSingleEntityChangeSet auf das übergeordnete Objekt

Für Hilfe bei der Methode onFlush erstellen, finden Sie bitte the documentation

Verwandte Themen