Symfony& Doctrine:调用flush()后的UnitOfWork未定义索引

时间:2017-07-06 09:44:32

标签: php symfony doctrine-orm unit-of-work

我有3个实体:

文件夹:

<?php
namespace CMS\ExtranetBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Folder
 *
 * @ORM\Table(name="folder")
 * @ORM\HasLifecycleCallbacks
 * @ORM\Entity(repositoryClass="CMS\ExtranetBundle\Repository\FolderRepository")
 */
class Folder
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="guid")
     * @ORM\GeneratedValue(strategy="UUID")
     */
    public $id;
    // Used in NotificationListener
    public $beforeRemoveId;
    /**
     * @ORM\OneToMany(targetEntity="Document", mappedBy="folder", cascade={"persist", "remove"})
     */
    public $documents;
}

文件:

<?php
namespace CMS\ExtranetBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Document
 * @ORM\Entity(repositoryClass="CMS\ExtranetBundle\Repository\DocumentRepository")
 * @ORM\Table(name="document")
 * @ORM\HasLifecycleCallbacks
 */
class Document
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="guid")
     * @ORM\GeneratedValue(strategy="UUID")
     */
    public $id;
    /**
     * @ORM\ManyToOne(targetEntity="Folder", inversedBy="documents", cascade={"persist"})
     * @ORM\JoinColumn(name="folder_id", referencedColumnName="id", nullable=true)
     */
    public $folder;
}

和通知:

<?php
namespace CMS\ExtranetBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Table(name="notification")
 * @ORM\Entity(repositoryClass="CMS\ExtranetBundle\Repository\NotificationRepository")
 * @ORM\HasLifecycleCallbacks
 */
class Notification
{
    /**
     * @ORM\Id
     * @ORM\Column(name="id", type="guid")
     * @ORM\GeneratedValue(strategy="UUID")
     */
    public $id;
    /**
     * @ORM\ManyToOne(targetEntity="User", inversedBy="notifications", cascade={"persist"})
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
     */
    public $user;
    /**
     * @ORM\Column(name="type", type="string", length=16)
     */
    public $type;
    /**
     * @ORM\Column(name="object_id", type="string", length=36)
     */
    public $objectId;
}

最后,我有一个监听器而不是“监听”“文档”实体(preRemove和postRemove)以删除通知实体。通知实体未链接到具有关系的Document,因为字段“objectId”是通用的,它可以包含其他实体,具体取决于“type”属性。

这是我的听众:

<?php
namespace CMS\ExtranetBundle\EventListener;

use Doctrine\ORM\Event\LifecycleEventArgs;

class NotificationListener
{
    /**
     * Enregistre l'ID avant la suppression pour l'utiliser dans le postRemove
     *
     * @param LifecycleEventArgs $args
     * @return bool|void
     */
    public function preRemove(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $class  = (new \ReflectionClass($entity))->getShortName();
        if (!$this->isOfValidClass($class)) {
            return;
        }
        $entity->beforeRemoveId = $entity->getId();
    }
    /**
     * Lors de la suppression d'une entité, supprime les notifications correspondantes.
     *
     * @param LifecycleEventArgs $args
     * @return bool|void
     */
    public function postRemove(LifecycleEventArgs $args)
    {
        $entity = $args->getEntity();
        $class  = (new \ReflectionClass($entity))->getShortName();
        if (!$this->isOfValidClass($class)) {
            return;
        }
        $id = $entity->beforeRemoveId;
        if (!$id) {
            return;
        }

        $em = $args->getEntityManager();
        $notifications = $em->getRepository('CMSExtranetBundle:Notification')->findBy([
            'type'     => strtolower($class),
            'objectId' => $id
        ]);
        if (count($notifications)) {
            $batchSize = 20;
            $i = 1;
            foreach ($notifications as $notification) {
                $em->remove($notification);
                if (($i % $batchSize) === 0) {
                    $em->flush();
                    $em->clear();
                }
                ++$i;
            }
            // It fails after calling flush()
            $em->flush();
        }
    }
    private function isOfValidClass($class)
    {
        $allowedClasssNames = [
            'Document',
        ];
        foreach ($allowedClasssNames as $allowedClasssName) {
            if ($class == $allowedClasssName) {
                return true;
            }
        }
        return false;
    }
}

我正在尝试删除包含多个文档的文件夹实体。

这是我的控制器:

<?php
namespace CMS\ExtranetBundle\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;
use CMS\ExtranetBundle\Entity\Folder;
use CMS\ExtranetBundle\Security\Voters\FolderVoter;

class FolderController extends DefaultController
{
    public function deleteAction(Request $request, Folder $folder)
    {
        // Authorization
        $this->denyAccessUnlessGranted(FolderVoter::WRITE, $folder);
        $em = $this->getDoctrine()->getManager();
        $em->remove($folder);
        $em->flush();
        return new JsonResponse([
            'status'      => true,
        ]);
    }
}

我的问题是,有时,当我删除包含具有通知的文档的文件夹时,我收到此错误:

enter image description here

(NotificationListener.php的第73行对应于postRemove中循环之后的最后$em->flush();

如果文件没有通知,则可以正常使用

有什么想法吗?

1 个答案:

答案 0 :(得分:1)

好的,感谢irc.freenode.net上的srm` @#symfony-fr,我改变了删除Notification的方法。这是我的NotificationListener的postRemove()方法:

/**
 * Lors de la suppression d'une entité, supprime les notifications correspondantes.
 *
 * @param LifecycleEventArgs $args
 * @return bool|void
 */
public function postRemove(LifecycleEventArgs $args)
{
    $entity = $args->getEntity();

    // Récupère le nom 'court' de la classe
    $class = (new \ReflectionClass($entity))->getShortName();

    if (!$this->isValidClass($class)) {
        return;
    }
    $id = $entity->beforeRemoveId;
    if (!$id) {
        return;
    }
    $em = $args->getEntityManager();

    $queryBuilder = $em
        ->createQueryBuilder()
        ->delete('CMSExtranetBundle:Notification', 'n')
        ->where('n.type = :type')
        ->andWhere('n.objectId = :objectIds')
        ->setParameter(':type', strtolower($class))
        ->setParameter(':objectIds', $entity->beforeRemoveId);

    $queryBuilder->getQuery()->execute();

}

谢谢! :-D