Symfony 在持久化之后和刷新数据之前触发Uniquentity
我正在通过doctrine entity Manager导入一种csv数据,但是,我有一个循环,它执行文档级别提到的批处理。 这是我的用户类,它在电子邮件字段上有一个唯一的约束:Symfony 在持久化之后和刷新数据之前触发Uniquentity,symfony,doctrine,Symfony,Doctrine,我正在通过doctrine entity Manager导入一种csv数据,但是,我有一个循环,它执行文档级别提到的批处理。 这是我的用户类,它在电子邮件字段上有一个唯一的约束: /** * @ORM\Entity * @UniqueEntity("email") */ class User { /** * @ORM\Column(name="email", type="string", length=255,
/**
* @ORM\Entity
* @UniqueEntity("email")
*/
class User
{
/**
* @ORM\Column(name="email", type="string", length=255, unique=true)
* @Assert\Email
*/
protected $email;
}
不幸的是,如果我的数据中有多行具有相同的电子邮件地址,则不会触发uniquentity
验证,因为用户是持久化的,但不会刷新到数据库中
- 解决方案1:避免批处理,并在每次迭代时进行刷新,这是非常违规的,可能会引发连接关闭原则或某种内存泄漏
- 解决方案2:创建一个自定义约束,其灵感来源于
,然后检查每个项目的电子邮件地址,看是否有用户已经使用相同的邮件uniquentity(“email”)
em->persist()
,我在$entityManager->getUnitOfWork()->getScheduledEntityInsertions()
或$entityManager->getUnitOfWork()->getScheduledEntityUpdates()
中都找不到任何持久化对象
只有在新的插入过程中,对象才会保留在函数的响应中->getScheduleIdentityInsertions()
如果有人知道如何恢复$entityManager->persist()
步骤后保存的实体,我将不胜感激。
或者仅仅是第三种解决方案,它允许我触发对电子邮件唯一性的验证,即使在批处理上下文中也是如此。除非您愿意将整个电子邮件加载到内存缓存中,并使用REDIS或direct Array来处理,否则我看不到第二种方法,只能使用DB唯一性约束(即刷新) 考虑到您对内存过载的恐惧,您可以通过调用clear方法将持久化实体从托管池中分离出来。因此,您的代码如下所示:
foreach($data as $itrationNumber => $item) {
/** @var User|null $user **/
$user = $this->em->getRepository(User::class)->findOnBy(['email' => $item['email']]);
$user = ($user) ? $user : new User();
$user->setName('test');
if ($this->validator->validate($user)->count() === 0) {
$this->persist($user);
$this->em->flush();
}
$this->em->clear();
}
我个人将此解决方案用于批量处理巨大的CSV流,并从中获得了灵感
另外,我会考虑DB事务,但可能需要更多技巧。您应该保留第二点信息,以查看是否已处理电子邮件地址:
$emails = [];
foreach($data as $itrationNumber => $item) {
if (isset($emails[$item['email']])) {
continue;
}
/** @var User|null $user **/
$user = $this->em->getRepository(User::class)->findOnBy(['email' => $item['email']]);
$user = ($user) ? $user : new User();
$user->setName('test');
if ($this->validator->validate($user)->count() === 0) {
$this->persist($user);
$emails[$item['email']] = true;
}
if ($itratioNumber% 100 === 0) {
$this->em->flush();
}
}
此代码段没有规范化电子邮件地址,这应该首先完成(例如strtolower和删除+foo gmail本地部分)。您是否尝试使用
em->contains($user)
?你可能想得太多了。只要在保存新实体时维护一个电子邮件列表,并在添加新实体之前检查该列表。@Cerad是否有合适的方法将该列表传递给我的验证器,我想我将使用会话!为什么需要会话数据?你必须为每一个会重新开始的请求刷新。或者我只是在想。
$emails = [];
foreach($data as $itrationNumber => $item) {
if (isset($emails[$item['email']])) {
continue;
}
/** @var User|null $user **/
$user = $this->em->getRepository(User::class)->findOnBy(['email' => $item['email']]);
$user = ($user) ? $user : new User();
$user->setName('test');
if ($this->validator->validate($user)->count() === 0) {
$this->persist($user);
$emails[$item['email']] = true;
}
if ($itratioNumber% 100 === 0) {
$this->em->flush();
}
}