Doctrine:多对多在事件侦听器中删除关联对象

Doctrine: many-to-many get removed associated object inside an event listener

我有很多这样的关联

Entity Car
{
   /**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="User", inversedBy="cars", fetch="EXTRA_LAZY")
     * @ORM\JoinTable(
     *  name="cars_users",
     *  joinColumns={
     *      @ORM\JoinColumn(name="car_id", referencedColumnName="id")
     *  },
     *  inverseJoinColumns={
     *      @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     *  }
     * )
     */
    protected $users; 

    /**
     * @param $user
      */
    public function removeUser(User $user)
    {
        if (!$this->users->contains($user)) {
            return;
        }

        $this->users->removeElement($user);
        $user->removeCar($this);

        return $this;
    }
}

Entity User 
{
/**
     * @var \Doctrine\Common\Collections\Collection
     *
     * @ORM\ManyToMany(targetEntity="Car", mappedBy="users")
     */
    protected $cars;
}

在我的控制器中我有

$carA = getRandomCar();
$userB = getAUserThatBelongToCarA();

//remove association between Car A and User B
$carA->removesUser($userB);
doctrineUpdateCar($carA);

我有一个 Doctrine Listener CarListener 我正在尝试找出 User 是否已在该侦听器中删除,并告诉它是哪个 User

我尝试了 preUpdate postUpdate postFlush 但无法找到在 CarListener

中获取 $userB 对象的方法

您的 preUpdate 侦听器将收到一个 PreUpdateEventArgs 对象,您可以使用该对象获取大量有关要通过 hasChangedField() 等方法持久化的更改的信息。更多信息在这里:

http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/events.html#preupdate

Doctrine 确实表现得很奇怪...在更复杂的环境中遇到了同样的问题。所以我决定从你的例子中复制汽车和用户 class。使用完全相同的实体,除了汽车实体中的 removeUser 函数。认为这会更短:

class Car
{
    public function removeUser(User $user)
    {
        $this->users->removeElement($user);
    }
}

在控制器中我得到了这个:

$em = $this->getDoctrine()->getManager();

$cars = $this->getDoctrine()->getRepository('AppBundle:Car');
$users = $this->getDoctrine()->getRepository('AppBundle:User');

$car = $cars->findOneById(2);
$removeUser = $users->findOneById(3);

$car->removeUser($removeUser);
// $em->flush();

ID 为#3 的用户总是从#2 汽车中移除。无论 car_user table 中的条目是否存在,都会执行 DELETE 语句。 如果我在 EntityManager ($em) 的 flush 方法中发表评论,则会调用 onFlush 事件。 UnitsOfWork 对象上的函数 getScheduledEntityInsertions()getScheduledEntityUpdates()getScheduledEntityDeletions()getScheduledCollectionDeletions()getScheduledCollectionUpdates() 仅返回空数组...

我认为最简单的方法是通知自己有关更改的信息。找到了一种通过交出注释来跟踪它们的简单方法。以下是链接:

事件

我建议您使用 onflush 事件让 listener/subscriber 监听,因为它是在计算更改之后但在对数据库执行任何操作之前调度的。这使得它非常理想,因为您可以访问所有即将进行的更改,但实际上还没有将它们写入数据库(因此您可以选择中止)。

listener/subscriber 被传递给 OnFlushEventArgs object,您可以从中访问 UnitOfWork。工作单元有 5 种方法来收集即将进行的更改:

  • getScheduledEntityInsertions()
  • getScheduledEntityUpdates()
  • getScheduledEntityDeletions()
  • getScheduledCollectionUpdates()
  • getScheduledCollectionDeletions()

Collections

您正在做的是从 collection Car::$users 中删除 User 并从 collection User::cars 中删除 Car

因此实际上没有删除任何实体,也没有更改任何字段。相反,您正在更改 collections。换句话说,您要查看的是 getScheduledCollectionUpdates()

getScheduledCollectionUpdates() 返回的 collection(s) 是 PersistentCollection 的一个实例,它有这些方法告诉你发生了什么变化:

  • getInsertDiff()
  • getDeleteDiff()

在您的情况下,您可以使用 getDeleteDiff() 找出哪个实体已从 collection 中删除。