【发布时间】:2018-09-13 20:15:58
【问题描述】:
我正在使用 Symfony 4 & Doctrine 创建一个小应用程序。有用户(用户实体),他们拥有某种称为单选表(RadioTable 实体)的内容。无线电表包含无线电台(RadioStation 实体)。 RadioStation.radioTableId 与 RadioTable 相关(多对一), RadioTable.ownerId 与 User 相关(多对一)。
也许我应该注意到这是我与 SF 合作的第一个项目。
使用注解配置实体,这样:
<?php
namespace App\Entity;
/**
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
*/
class User implements UserInterface, \Serializable, EncoderAwareInterface
{
/**
* @ORM\OneToMany(targetEntity="App\Entity\RadioTable", mappedBy="owner", orphanRemoval=true)
*/
private $radioTables;
/**
* @ORM\Column(type="date")
*/
private $lastActivityDate;
}
// -----------------
namespace App\Entity;
/**
* @ORM\Entity(repositoryClass="App\Repository\RadioTableRepository")
* @ORM\EntityListeners({"App\EventListener\RadioTableListener"})
*/
class RadioTable
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="radioTables")
* @ORM\JoinColumn(nullable=false, onDelete="cascade")
*/
private $owner;
/**
* @ORM\Column(type="datetime")
*/
private $lastUpdateTime;
}
// -----------------
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\RadioStationRepository")
* @ORM\EntityListeners({"App\EventListener\RadioStationListener"})
*/
class RadioStation
{
/**
* @ORM\ManyToOne(targetEntity="App\Entity\RadioTable")
* @ORM\JoinColumn(nullable=false, onDelete="cascade")
*/
private $radioTable;
}
添加、删除或修改广播电台时,我需要在适当的 RadioTable 实体中更新$lastUpdateTime。另外,当创建、删除或更新单选表时,我需要更新单选表所有者(用户类)的$lastActivityDate。我正在尝试通过使用实体侦听器来实现这一点:
<?php
namespace App\EventListener;
class RadioStationListener
{
/**
* @PreFlush
* @PreRemove
*/
public function refreshLastUpdateTimeOfRadioTable(RadioStation $radioStation)
{
$radioStation->getRadioTable()->refreshLastUpdateTime();
}
}
// -----------------------------
namespace App\EventListener;
class RadioTableListener
{
/**
* @PreFlush
* @PreRemove
*/
public function refreshLastActivityDateOfUser(RadioTable $radioTable, PreFlushEventArgs $args)
{
$radioTable->getOwner()->refreshLastActivityDate();
/* hack */
$args->getEntityManager()->flush($radioTable->getOwner());
/* hack */
}
}
(在refresh*() 方法中,我只是为正确的实体字段创建\DateTime 的新实例。)
我遇到了问题。当我尝试更新/删除/创建广播电台时,RadioStation 监听器工作正常,相关 RadioTable 类已成功更新。但是当我尝试更新单选表时,用户类已更新,但没有被 Doctrine 持久化到数据库中。
我很困惑,因为这些实体侦听器中的代码结构非常相似。
部分我找到了问题的原因。很明显,只有所有者才能修改自己的单选表,并且用户必须登录才能修改它们。我正在使用 Symfony 的 Security 组件来支持登录机制。
当我暂时破解控制器代码以禁用安全性并尝试将单选表更新为匿名时,RadioTable 实体侦听器正常工作并且用户实体已成功修改并持久化到数据库。
要解决这个问题,我需要手动与 Doctrine 的实体管理器交谈,并以用户实体作为参数调用flush()(没有参数我正在做无限循环)。此行由/* hack */ 注释标记。
在这个冗长的故事之后,我想问一个问题:为什么我必须这样做?为什么我必须为用户对象手动调用flush(),但前提是使用安全组件并且用户已登录?
【问题讨论】:
-
看看你的
refresh*()函数会很有帮助。 -
我怀疑不同之处在于您在实体级别的生命周期事件中间调用它们(例如,而不是在侦听器中)。 可能是您的 RadioStation 在您的 RadioTable 之前处理,因此对 RadioTable 的更改随后会被持久化,而 User 在 RadioStation 之前处理,因此在处理 RadioStation(用户已处理)之后,您必须手动执行回到持久用户。只是一个猜测。调试/跟踪将有助于回答。
标签: symfony doctrine entity listener symfony4