DoctrineExtensions
DoctrineExtensions copied to clipboard
A [SoftDeleteable] cycle has been detected, so a topological sort is not possible. The getCycle() method provides the list of nodes that form the cycle.
Hello, i have an issue with sofrdelete on Symfony 6.3 and doctrine extension 3 softdeleteable
i would like soft delete an entity User but i've got a doctrine error :
UserController :
#[Route("/delete/{userId}", name: "app_admin_deleteUser", methods: ["POST"])]
public function deleteUser(Request $request, $userId)
{
$user = $this->em->getRepository(AdminUser::class)->find($userId);
$formDelete = $this->createDeleteForm($user);
$formDelete->handleRequest($request);
if ($formDelete->isSubmitted() && $formDelete->isValid()) {
$this->em->remove($user);
$this->em->flush();
$this->session->getFlashBag()->add('success', 'Utilisateur supprimé.');
}
return $this->redirectToRoute('app_admin_users');
}
My User Entity :
<?php
namespace App\Entity\Admin;
use App\Classes\AdminUserTrait;
use App\Entity\Application\AdminUserExtraData;
use App\Entity\Application\Connection;
use App\Entity\Application\Dossier;
use App\Entity\Application\DossierConsultation;
use App\Entity\Application\Evenement;
use App\Entity\Application\Invitation;
use App\Entity\Application\Notification;
use App\Entity\Application\Prospect;
use App\Repository\Admin\AdminUserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Webingco\AdminBundle\Entity\AdminUserModel as BaseAdminUser;
#[ORM\Entity(repositoryClass: AdminUserRepository::class)]
#[Gedmo\SoftDeleteable(fieldName: 'deletedAt', timeAware: false, hardDelete: true)]
class AdminUser extends BaseAdminUser
{
use AdminUserTrait;
public $level;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\OneToMany(targetEntity: Dossier::class, mappedBy: 'commercial', orphanRemoval: true)]
private $dossiers;
#[ORM\OneToOne(targetEntity: AdminUserExtraData::class, mappedBy: 'adminUser', cascade: ['persist', 'remove'])]
private $adminUserExtraData;
#[ORM\JoinTable(name: 'admin_user_responsables')]
#[ORM\ManyToMany(targetEntity: AdminUser::class, inversedBy: 'commerciaux')]
private $responsables;
#[ORM\ManyToMany(targetEntity: AdminUser::class, mappedBy: 'responsables')]
private $commerciaux;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Notification::class, orphanRemoval: true)]
private $notifications;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: DossierConsultation::class, orphanRemoval: true)]
private $dossierConsultations;
#[ORM\OneToOne(mappedBy: 'user', targetEntity: Connection::class, cascade: ['persist', 'remove'])]
private $connection;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Prospect::class, orphanRemoval: true)]
private $prospects;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Invitation::class, orphanRemoval: true)]
private $invitations;
#[ORM\ManyToMany(targetEntity: AdminUser::class, mappedBy: 'prospecteursCommerciaux')]
private Collection $prospecteurs;
#[ORM\JoinTable(name: 'admin_user_prospecteurs_commerciaux')]
#[ORM\JoinColumn(name: 'prospecteur_id')]
#[ORM\InverseJoinColumn(name: 'commercial_id')]
#[ORM\ManyToMany(targetEntity: AdminUser::class, inversedBy: 'prospecteurs')]
private Collection $prospecteursCommerciaux;
#[ORM\OneToMany(mappedBy: 'user', targetEntity: Evenement::class, orphanRemoval: true)]
private $evenements;
#[ORM\Column(name: 'deletedAt', type: Types::DATETIME_MUTABLE, nullable: true)]
private $deletedAt;
public function __construct()
{
$this->dossiers = new ArrayCollection();
$this->responsables = new ArrayCollection();
$this->commerciaux = new ArrayCollection();
$this->notifications = new ArrayCollection();
$this->dossierConsultations = new ArrayCollection();
$this->prospects = new ArrayCollection();
$this->invitations = new ArrayCollection();
$this->prospecteursCommerciaux = new ArrayCollection();
$this->prospecteurs = new ArrayCollection();
$this->evenements = new ArrayCollection();
}
....
public function getDeletedAt(): ?\DateTime
{
return $this->deletedAt;
}
public function setDeletedAt(?\DateTime $deletedAt): self
{
$this->deletedAt = $deletedAt;
return $this;
}
}
AdminUserModel Entity Superclass :
<?php
namespace Webingco\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Gedmo\Mapping\Annotation as Gedmo;
use Webingco\AdminBundle\Classes\AdminUserTrait;
#[ORM\MappedSuperclass]
#[UniqueEntity(fields: ['email'], message: 'Cette adresse email est déja utilisée.')]
class AdminUserModel implements UserInterface, PasswordAuthenticatedUserInterface
{
use AdminUserTrait;
#[ORM\Column(type: 'string', length: 180, unique: true)]
private $email;
#[ORM\Column(type: 'json')]
private $roles = [];
/**
* @var string The hashed password
*/
#[ORM\Column(type: 'string')]
private $password;
#[ORM\Column(type: 'boolean')]
private $isVerified = false;
#[ORM\Column(type: 'boolean')]
private $isEnabled;
#[ORM\Column(type: 'datetime')]
#[Gedmo\Timestampable(on: 'create')]
private $createdAt;
#[ORM\Column(type: 'datetime')]
#[Gedmo\Timestampable(on: 'update')]
private $updatedAt;
#[ORM\Column(type: 'string', length: 100, nullable: true)]
private $lastname;
#[ORM\Column(type: 'string', length: 100, nullable: true)]
private $firstname;
#[ORM\Column(type: 'string', length: 20, nullable: true)]
private $phone;
#[ORM\Column(type: 'string', length: 50, nullable: true)]
private $ip;
#[ORM\Column(type: 'datetime', nullable: true)]
private $lastLogin;
private $plainPassword = null;
...
}
Doctrine.yaml :
doctrine:
dbal:
password: '%env(DATABASE_PASSWORD)%'
url: '%env(resolve:DATABASE_URL)%'
# IMPORTANT: You MUST configure your server version,
# either here or in the DATABASE_URL env var (see .env file)
#server_version: '13'
orm:
filters:
softdeleteable:
class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
enabled: true
dql:
string_functions:
JSON_CONTAINS: Scienta\DoctrineJsonFunctions\Query\AST\Functions\Mysql\JsonContains
FIELD: DoctrineExtensions\Query\Mysql\Field
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
report_fields_where_declared: true
mappings:
gedmo_tree:
prefix: Gedmo\Tree\Entity
dir: "%kernel.project_dir%/vendor/gedmo/doctrine-extensions/src/Tree/Entity"
alias: GedmoTree # (optional) it will default to the name set for the mapping
is_bundle: false
App:
is_bundle: false
dir: '%kernel.project_dir%/src/Entity'
prefix: 'App\Entity'
alias: App
stof_doctrine_extensions.yaml :
stof_doctrine_extensions:
default_locale: fr_FR
orm:
default:
sluggable: true
timestampable: true
tree: true
softdeleteable: true