如何修改updatedAt值且避免Doctrine生命周期回调自动覆盖?
解决AliceDataFixtures填充时Doctrine生命周期回调覆盖时间字段的问题
下面提供几种可行的解决方案,根据你的场景选择最适合的:
方案一:临时禁用Doctrine生命周期事件监听
在执行Alice数据填充前后,手动移除并恢复Doctrine的生命周期监听器,避免prePersist/preUpdate触发自动更新:
// 在Fixture加载类中执行以下逻辑 $em = $this->getContainer()->get('doctrine.orm.entity_manager'); $eventManager = $em->getEventManager(); // 备份原有监听器 $originalListeners = []; foreach (['prePersist', 'preUpdate'] as $event) { $originalListeners[$event] = $eventManager->getListeners($event); // 移除对应事件的所有监听器 foreach ($originalListeners[$event] as $listener) { $eventManager->removeEventListener($event, $listener); } } // 执行Alice数据填充 $this->getContainer()->get('hautelook_alice.doctrine.fixtures.loader')->load([ __DIR__.'/fixtures' // 你的Fixture文件路径 ]); // 恢复原有监听器 foreach ($originalListeners as $event => $listeners) { foreach ($listeners as $listener) { $eventManager->addEventListener($event, $listener); } }
方案二:给Timestampable Trait添加开关控制
修改你的时间Trait,增加一个临时禁用自动更新的开关,让Alice设置时间时绕过回调逻辑:
1. 更新TimestampableTrait
trait TimestampableTrait { private bool $disableAutoUpdate = false; public function disableAutoUpdate(): void { $this->disableAutoUpdate = true; } public function enableAutoUpdate(): void { $this->disableAutoUpdate = false; } public function setCreatedAt(\DateTimeInterface $createdAt = null): void { if ($this->disableAutoUpdate) { $this->createdAt = $createdAt; return; } $this->createdAt = $createdAt ?? new \DateTime(); } public function setUpdatedAt(\DateTimeInterface $updatedAt = null): void { if ($this->disableAutoUpdate) { $this->updatedAt = $updatedAt; return; } $this->updatedAt = $updatedAt ?? new \DateTime(); } }
2. 在Alice Fixture中使用开关
在你的YAML/JSON Fixture文件中,按顺序调用方法禁用自动更新、设置时间、再启用:
App\Entity\Article: article_{1..20}: title: '<sentence()>' content: '<paragraph()>' # 先禁用自动更新,再设置自定义时间 - { disableAutoUpdate: ~ } - { createdAt: <dateTimeBetween('-1 year', 'now')> } - { updatedAt: <dateTimeBetween(createdAt, 'now')> } - { enableAutoUpdate: ~ }
方案三:填充后用原生SQL批量更新时间
如果不想修改实体或监听器逻辑,可以在填充完成后直接执行原生SQL,批量更新时间字段为随机值:
// 填充完成后执行 $connection = $em->getConnection(); // 针对单个实体表更新 $connection->executeQuery(' UPDATE article SET created_at = DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY), updated_at = DATE_ADD(created_at, INTERVAL FLOOR(RAND() * 90) DAY) '); // 如果有多个实体表,重复执行对应SQL即可
这个方法适合测试环境,不需要改动现有业务逻辑,快速解决时间统一的问题。
内容的提问来源于stack exchange,提问作者nrog
相关产品推荐
相关产品推荐

