6

私はこのエンティティを持っています:

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

そしてこのリスナー:

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

        }
    }

}

アップルのコレクションが空の場合は正常に動作しますが、アイテムがある場合は重複エラーが発生します。

豚のコレクションだけを再計算したいことを UnitOfWork に伝えるにはどうすればよいですか?

アップデート

新しい preFlush イベント ( https://github.com/doctrine/doctrine2/pull/169 ) があり、そこでこのようなことができると思います。そのPRは私が使用しているブランチにはありませんが、試してみましょう!

4

3 に答える 3

7

リスナーのonFlushイベント中にエンティティを更新する場合、呼び出す必要があるのは次の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);

他のエンティティも作成している場合は、それらを永続化し、最初にそれらの変更を計算する必要があります!

$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);
于 2012-06-07T02:55:01.583 に答える
1

これは、新しい preFlush イベント (Symfony 2.1) で実行できます。

イベントにリスナーを追加します (サービス コンテナー全体を注入するのは悪い習慣ですが、場合によってはそれを行う方法もあります)。

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

リスナーは次のようになります。

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

            }

        }

    }

}
于 2012-07-10T19:13:32.430 に答える
0

送信先の現在のエンティティを更新し、onFlushそのエンティティへの関連付けを作成する場合

(この例では、Parentオブジェクトとchildオブジェクトを使用します)

親オブジェクトのプロパティを「stressed」に変更すると1、新しいchildオブジェクトをonflushメソッドの親オブジェクトにも関連付けたいとします。次のようになります。

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

そこで、次のように表示されます。

  1. notを使用して子オブジェクトを永続化する必要があります$uow->persist() $em->persist()
  2. computeChangeSet子オブジェクトで。
  3. recomputeSingleEntityChangeSet親オブジェクト

onFlush メソッドの作成については、ドキュメントを参照してください。

于 2013-10-17T14:51:32.983 に答える