当尝试在我的 Symfony 6.3 应用程序中删除用户时,我收到以下消息:已检测到循环,因此无法进行拓扑排序。 getCycle() 方法提供形成循环的节点列表。
我审查了我的所有实体,具有教义属性。 我查看了 #[ORM 参数,以确保删除中的级联以正确的顺序和方向执行。 一切似乎都是正确的。我的用户实体有许多一对多和多对一关系,并且我确保从我的用户实体到相关实体执行具有删除设置的级联。
你必须知道我的用户有不同的状态(验证中、活动中、暂停中和退出中),并且我必须历史化(即保留每个更改的历史记录),所以我创建了一个 StateHisto 实体,它有一个 Many-To - 与我的用户实体的一种关系。
但出于性能原因,我必须将用户的最后状态保存在属性中才能直接获取。因此,我向我的用户实体添加了一个 lastStateHisto 属性,与 StateHisto 实体具有一对一关系。
这是我的代码的摘录:
用户实体:
#[Vich\Uploadable]
#[ORM\Table(name: '`user`')]
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[UniqueEntity(fields: ['surname', 'firstname', 'birthDate'], message: 'L\'utilisateur existe déjà')]
#[InheritanceType('SINGLE_TABLE')]
#[DiscriminatorColumn(name: 'discr', type: 'string')]
#[DiscriminatorMap(['user' => 'User', 'apprenant' => 'Apprenant', 'benevole' => 'Benevole', 'enfant' => 'Enfant'])]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
protected $id;
#[ORM\Column(type: 'string', length: 180, unique: true)]
protected $username;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* @var File|null
*/
#[Vich\UploadableField(mapping: 'user_image', fileNameProperty: 'imageName', size: 'imageSize')]
protected ?File $imageFile = null;
/**
* @var string|null
*/
#[ORM\Column(type: 'string', nullable: true)]
protected ?string $imageName = null;
/**
* @var int|null
*/
#[ORM\Column(type: 'integer', nullable: true)]
protected ?int $imageSize = null;
/**
* @var \DateTimeInterface|null
*/
#[ORM\Column(type: 'datetime', nullable: true)]
protected ?\DateTimeInterface $updatedAt = null;
#[ORM\Column(type: 'json')]
protected $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column(type: 'string')]
protected $password;
#[ORM\Column(type: 'string', length: 255)]
protected $surname;
#[ORM\Column(type: 'string', length: 255)]
protected $firstname;
#[ORM\Column(type: 'string', length: 255)]
protected $title;
#[ORM\Column(type: 'date')]
protected $birthDate;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
protected $tel1;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
protected $tel2;
#[ORM\Column(type: 'string', length: 3000, nullable: true)]
protected $comment;
#[ORM\Column(type: 'string', length: 360, unique: true, nullable: true)]
protected $email;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: StateHisto::class, cascade: ['persist', 'remove'], fetch: 'EAGER', orphanRemoval: true)]
#[ORM\JoinColumn()]
/**
* States: 1=In validation, 2=Active, 3=In pause, 4=Inactive
*/
protected $stateHisto;
#[ORM\OneToOne(cascade: ['persist'])]
#[ORM\JoinColumn(nullable: true)]
private ?StateHisto $lastStateHisto = null;
StateHisto 实体:
#[ORM\Entity(repositoryClass: StateHistoRepository::class)]
class StateHisto
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'date')]
private $date;
#[ORM\Column(type: 'integer')]
#[Assert\Choice(1, 2, 3, 4)]
/**
* States: 1=In validation, 2=Active, 3=In pause, 4=Inactive
*/
private $state;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private $reason;
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'stateHisto')]
#[ORM\JoinColumn(nullable: false)]
private $user;
#[ORM\Column(nullable: true)]
private ?int $stdExitReason = null;
过了一会儿,我意识到错误消息中提到的循环是我在同一个实体上组合了一对多关系和一对一关系:StateHisto。 这使得用户的删除操作无法级联到 StateHisto 表。
这里需要做的是,在启动删除用户之前,首先将 lastStateHisto 属性(保存一对一关系的属性)设置为 null。
这意味着在控制器中,你可以这样做:
$user->setLastStateHisto(null);
$entityManager->persist($user);
$entityManager->flush();
$entityManager->remove($user);
$entityManager->flush();
这样就可以了。
我遇到了同样的问题,但由于我没有在关系上设置级联,因此只需将 PreRemove 事件中的字段设为 null 就足够了。
示例:
class User
{
#[ORM\ManyToOne( targetEntity: 'User' )]
#[ORM\JoinColumn( name: 'user_updated_id', referencedColumnName: 'user_id' )]
private null|User $updatedBy;
#[ORM\PreRemove]
public function onPreRemove( LifecycleEventArgs $args ) : void
{
/* Remove any references to self, as this will throw a CycleDetectedException */
$this->updatedBy = null;
}
}
omg,好奇怪的bug,同样的事情发生在我身上,而且确实与自身存在
#[ORM\OneToOne(targetEntity: self::class, cascade: ['persist', 'remove'])]
关系有关。
用例的底层逻辑是获取下一页或上一页(页面是实体)。我认为这是学说中的一个错误(我报告了这个问题here)。在按照 @antman3351 的建议删除元素之前,我必须先清除我的关系:
#[ORM\PreRemove]
public function onPreRemove( LifecycleEventArgs $args ) : void {
foreach ($this->getPageTemplates() as $pageTemplate):
$pageTemplate->setNextPage(null);
$pageTemplate->setPreviousPage(null);
endforeach;
}