|
|
@@ -0,0 +1,336 @@
|
|
|
+<?php
|
|
|
+declare(strict_types=1);
|
|
|
+
|
|
|
+namespace App\Commands;
|
|
|
+
|
|
|
+use App\Entity\Galaxy;
|
|
|
+use App\Services\GalaxyFactory;
|
|
|
+use Doctrine\ORM\EntityManagerInterface;
|
|
|
+use Symfony\Component\Console\Attribute\AsCommand;
|
|
|
+use Symfony\Component\Console\Command\Command;
|
|
|
+use Symfony\Component\Console\Input\InputArgument;
|
|
|
+use Symfony\Component\Console\Input\InputInterface;
|
|
|
+use Symfony\Component\Console\Input\InputOption;
|
|
|
+use Symfony\Component\Console\Output\OutputInterface;
|
|
|
+use Symfony\Component\Console\Style\SymfonyStyle;
|
|
|
+use Symfony\Component\Console\Question\ConfirmationQuestion;
|
|
|
+
|
|
|
+#[AsCommand(
|
|
|
+ name: 'astra:delete:galaxy',
|
|
|
+ description: 'Supprime une ou plusieurs galaxies et toutes leurs entités associées'
|
|
|
+)]
|
|
|
+class DeleteGalaxyCommand extends Command
|
|
|
+{
|
|
|
+ public function __construct(
|
|
|
+ private EntityManagerInterface $entityManager,
|
|
|
+ private GalaxyFactory $galaxyFactory
|
|
|
+ ) {
|
|
|
+ parent::__construct();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function configure(): void
|
|
|
+ {
|
|
|
+ $this
|
|
|
+ ->addArgument(
|
|
|
+ 'galaxy-ids',
|
|
|
+ InputArgument::IS_ARRAY | InputArgument::OPTIONAL,
|
|
|
+ 'IDs des galaxies à supprimer (séparés par des espaces)'
|
|
|
+ )
|
|
|
+ ->addOption(
|
|
|
+ 'all',
|
|
|
+ 'a',
|
|
|
+ InputOption::VALUE_NONE,
|
|
|
+ 'Supprimer toutes les galaxies'
|
|
|
+ )
|
|
|
+ ->addOption(
|
|
|
+ 'force',
|
|
|
+ 'f',
|
|
|
+ InputOption::VALUE_NONE,
|
|
|
+ 'Forcer la suppression sans demander confirmation'
|
|
|
+ )
|
|
|
+ ->addOption(
|
|
|
+ 'dry-run',
|
|
|
+ 'd',
|
|
|
+ InputOption::VALUE_NONE,
|
|
|
+ 'Afficher ce qui serait supprimé sans effectuer la suppression'
|
|
|
+ )
|
|
|
+ ->setHelp(
|
|
|
+ <<<EOT
|
|
|
+Cette commande permet de supprimer des galaxies et toutes leurs entités associées.
|
|
|
+
|
|
|
+Exemples d'utilisation :
|
|
|
+ <info>php bin/console astra:delete:galaxy 1</info> # Supprime la galaxie avec l'ID 1
|
|
|
+ <info>php bin/console astra:delete:galaxy 1 2 3</info> # Supprime les galaxies 1, 2 et 3
|
|
|
+ <info>php bin/console astra:delete:galaxy --all</info> # Supprime toutes les galaxies
|
|
|
+ <info>php bin/console astra:delete:galaxy --all --force</info> # Supprime toutes les galaxies sans confirmation
|
|
|
+ <info>php bin/console astra:delete:galaxy 1 --dry-run</info> # Affiche ce qui serait supprimé sans supprimer
|
|
|
+
|
|
|
+<comment>ATTENTION :</comment> Cette opération est <error>irréversible</error> et supprimera définitivement :
|
|
|
+- La/les galaxie(s) spécifiée(s)
|
|
|
+- Tous les secteurs associés
|
|
|
+- Tous les systèmes stellaires
|
|
|
+- Toutes les planètes
|
|
|
+- Les relations avec les jeux (le jeu sera conservé mais détaché)
|
|
|
+EOT
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function execute(InputInterface $input, OutputInterface $output): int
|
|
|
+ {
|
|
|
+ $io = new SymfonyStyle($input, $output);
|
|
|
+ $galaxyIds = $input->getArgument('galaxy-ids');
|
|
|
+ $deleteAll = $input->getOption('all');
|
|
|
+ $force = $input->getOption('force');
|
|
|
+ $dryRun = $input->getOption('dry-run');
|
|
|
+
|
|
|
+ if (!$deleteAll && empty($galaxyIds)) {
|
|
|
+ $io->error('Vous devez spécifier au moins un ID de galaxie ou utiliser --all pour supprimer toutes les galaxies.');
|
|
|
+ return Command::FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($deleteAll && !empty($galaxyIds)) {
|
|
|
+ $io->error('Vous ne pouvez pas utiliser --all et spécifier des IDs en même temps.');
|
|
|
+ return Command::FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Récupérer les galaxies à supprimer
|
|
|
+ $galaxies = $this->getGalaxiesToDelete($galaxyIds, $deleteAll, $io);
|
|
|
+
|
|
|
+ if (empty($galaxies)) {
|
|
|
+ $io->warning('Aucune galaxie à supprimer.');
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Afficher les informations sur ce qui va être supprimé
|
|
|
+ $this->displayDeletionPreview($io, $galaxies, $dryRun);
|
|
|
+
|
|
|
+ if ($dryRun) {
|
|
|
+ $io->note('Mode dry-run activé : aucune suppression effectuée.');
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Demander confirmation si pas forcé
|
|
|
+ if (!$force && !$this->confirmDeletion($io, $galaxies)) {
|
|
|
+ $io->info('Suppression annulée.');
|
|
|
+ return Command::SUCCESS;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Effectuer la suppression
|
|
|
+ return $this->performDeletion($io, $galaxies, $deleteAll);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function getGalaxiesToDelete(array $galaxyIds, bool $deleteAll, SymfonyStyle $io): array
|
|
|
+ {
|
|
|
+ $galaxyRepository = $this->entityManager->getRepository(Galaxy::class);
|
|
|
+
|
|
|
+ if ($deleteAll) {
|
|
|
+ return $galaxyRepository->findAll();
|
|
|
+ }
|
|
|
+
|
|
|
+ $galaxies = [];
|
|
|
+ $notFoundIds = [];
|
|
|
+
|
|
|
+ foreach ($galaxyIds as $id) {
|
|
|
+ $galaxy = $galaxyRepository->find($id);
|
|
|
+ if ($galaxy) {
|
|
|
+ $galaxies[] = $galaxy;
|
|
|
+ } else {
|
|
|
+ $notFoundIds[] = $id;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($notFoundIds)) {
|
|
|
+ $io->warning('Galaxies non trouvées avec les IDs : ' . implode(', ', $notFoundIds));
|
|
|
+ }
|
|
|
+
|
|
|
+ return $galaxies;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function displayDeletionPreview(SymfonyStyle $io, array $galaxies, bool $dryRun): void
|
|
|
+ {
|
|
|
+ $title = $dryRun ? 'Aperçu de la suppression (dry-run)' : 'Galaxies à supprimer';
|
|
|
+ $io->title($title);
|
|
|
+
|
|
|
+ $totalStats = [
|
|
|
+ 'galaxies' => 0,
|
|
|
+ 'sectors' => 0,
|
|
|
+ 'systems' => 0,
|
|
|
+ 'planets' => 0,
|
|
|
+ 'habitablePlanets' => 0,
|
|
|
+ 'terraformablePlanets' => 0,
|
|
|
+ 'gamesDetached' => 0
|
|
|
+ ];
|
|
|
+
|
|
|
+ $tableData = [];
|
|
|
+
|
|
|
+ foreach ($galaxies as $galaxy) {
|
|
|
+ $counts = $this->galaxyFactory->countGalaxyEntities($galaxy);
|
|
|
+ $totalStats['galaxies']++;
|
|
|
+ $totalStats['sectors'] += $counts['sectors'];
|
|
|
+ $totalStats['systems'] += $counts['systems'];
|
|
|
+ $totalStats['planets'] += $counts['planets'];
|
|
|
+ $totalStats['habitablePlanets'] += $counts['habitablePlanets'];
|
|
|
+ $totalStats['terraformablePlanets'] += $counts['terraformablePlanets'];
|
|
|
+
|
|
|
+ if ($galaxy->getGame()) {
|
|
|
+ $totalStats['gamesDetached']++;
|
|
|
+ }
|
|
|
+
|
|
|
+ $tableData[] = [
|
|
|
+ $galaxy->getId(),
|
|
|
+ $galaxy->getName(),
|
|
|
+ $counts['sectors'],
|
|
|
+ $counts['systems'],
|
|
|
+ $counts['planets'],
|
|
|
+ $counts['habitablePlanets'],
|
|
|
+ $counts['terraformablePlanets'],
|
|
|
+ $galaxy->getGame() ? 'Oui' : 'Non'
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ $io->table(
|
|
|
+ ['ID', 'Nom', 'Secteurs', 'Systèmes', 'Planètes', 'Habitables', 'Terraformables', 'Jeu associé'],
|
|
|
+ $tableData
|
|
|
+ );
|
|
|
+
|
|
|
+ $io->section('Résumé total');
|
|
|
+ $io->definitionList(
|
|
|
+ ['Galaxies' => $totalStats['galaxies']],
|
|
|
+ ['Secteurs' => $totalStats['sectors']],
|
|
|
+ ['Systèmes stellaires' => $totalStats['systems']],
|
|
|
+ ['Planètes' => $totalStats['planets']],
|
|
|
+ ['Planètes habitables' => $totalStats['habitablePlanets']],
|
|
|
+ ['Planètes terraformables' => $totalStats['terraformablePlanets']],
|
|
|
+ ['Jeux détachés' => $totalStats['gamesDetached']]
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ private function confirmDeletion(SymfonyStyle $io, array $galaxies): bool
|
|
|
+ {
|
|
|
+ $count = count($galaxies);
|
|
|
+ $message = $count === 1
|
|
|
+ ? "Êtes-vous sûr de vouloir supprimer cette galaxie ? Cette action est irréversible."
|
|
|
+ : "Êtes-vous sûr de vouloir supprimer ces {$count} galaxies ? Cette action est irréversible.";
|
|
|
+
|
|
|
+ $question = new ConfirmationQuestion($message, false);
|
|
|
+ return $io->askQuestion($question);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function performDeletion(SymfonyStyle $io, array $galaxies, bool $deleteAll): int
|
|
|
+ {
|
|
|
+ $io->section('Suppression en cours...');
|
|
|
+
|
|
|
+ $progressBar = $io->createProgressBar(count($galaxies));
|
|
|
+ $progressBar->start();
|
|
|
+
|
|
|
+ $allStats = [];
|
|
|
+ $errors = [];
|
|
|
+
|
|
|
+ foreach ($galaxies as $galaxy) {
|
|
|
+ try {
|
|
|
+ $stats = $this->deleteGalaxy($galaxy);
|
|
|
+ $allStats[] = $stats;
|
|
|
+ $progressBar->advance();
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ $errors[] = [
|
|
|
+ 'galaxy' => $galaxy->getName() . ' (ID: ' . $galaxy->getId() . ')',
|
|
|
+ 'error' => $e->getMessage()
|
|
|
+ ];
|
|
|
+ $progressBar->advance();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $progressBar->finish();
|
|
|
+ $io->newLine(2);
|
|
|
+
|
|
|
+ // Afficher les résultats
|
|
|
+ $this->displayDeletionResults($io, $allStats, $errors);
|
|
|
+
|
|
|
+ return empty($errors) ? Command::SUCCESS : Command::FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function displayDeletionResults(SymfonyStyle $io, array $allStats, array $errors): void
|
|
|
+ {
|
|
|
+ if (!empty($allStats)) {
|
|
|
+ $io->success('Suppression terminée avec succès !');
|
|
|
+
|
|
|
+ $totalDeleted = [
|
|
|
+ 'galaxies' => count($allStats),
|
|
|
+ 'sectors' => array_sum(array_column($allStats, 'sectorsDeleted')),
|
|
|
+ 'systems' => array_sum(array_column($allStats, 'systemsDeleted')),
|
|
|
+ 'planets' => array_sum(array_column($allStats, 'planetsDeleted')),
|
|
|
+ 'gamesDetached' => count(array_filter($allStats, fn($s) => $s['gameDetached']))
|
|
|
+ ];
|
|
|
+
|
|
|
+ $io->definitionList(
|
|
|
+ ['Galaxies supprimées' => $totalDeleted['galaxies']],
|
|
|
+ ['Secteurs supprimés' => $totalDeleted['sectors']],
|
|
|
+ ['Systèmes supprimés' => $totalDeleted['systems']],
|
|
|
+ ['Planètes supprimées' => $totalDeleted['planets']],
|
|
|
+ ['Jeux détachés' => $totalDeleted['gamesDetached']]
|
|
|
+ );
|
|
|
+
|
|
|
+ // Détail par galaxie
|
|
|
+ if (count($allStats) > 1) {
|
|
|
+ $io->section('Détail par galaxie');
|
|
|
+ $tableData = [];
|
|
|
+ foreach ($allStats as $stats) {
|
|
|
+ if (!isset($stats['error'])) {
|
|
|
+ $tableData[] = [
|
|
|
+ $stats['galaxyId'],
|
|
|
+ $stats['galaxyName'],
|
|
|
+ $stats['sectorsDeleted'],
|
|
|
+ $stats['systemsDeleted'],
|
|
|
+ $stats['planetsDeleted']
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $io->table(['ID', 'Nom', 'Secteurs', 'Systèmes', 'Planètes'], $tableData);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($errors)) {
|
|
|
+ $io->error('Certaines suppressions ont échoué :');
|
|
|
+ foreach ($errors as $error) {
|
|
|
+ $io->text("• {$error['galaxy']} : {$error['error']}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Supprime une galaxie et toutes ses entités associées
|
|
|
+ */
|
|
|
+ public function deleteGalaxy(Galaxy $galaxy): array
|
|
|
+ {
|
|
|
+ $stats = [
|
|
|
+ 'galaxyName' => $galaxy->getName(),
|
|
|
+ 'galaxyId' => $galaxy->getId(),
|
|
|
+ 'sectorsDeleted' => 0,
|
|
|
+ 'systemsDeleted' => 0,
|
|
|
+ 'planetsDeleted' => 0,
|
|
|
+ 'gameDetached' => false
|
|
|
+ ];
|
|
|
+
|
|
|
+ // Détacher le jeu associé s'il existe
|
|
|
+ if ($galaxy->getGame()) {
|
|
|
+ $galaxy->getGame()->setGalaxy(null);
|
|
|
+ $stats['gameDetached'] = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Compter les entités avant suppression
|
|
|
+ foreach ($galaxy->getSectors() as $sector) {
|
|
|
+ $stats['sectorsDeleted']++;
|
|
|
+
|
|
|
+ foreach ($sector->getSystems() as $system) {
|
|
|
+ $stats['systemsDeleted']++;
|
|
|
+ $stats['planetsDeleted'] += $system->getPlanets()->count();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Supprimer la galaxie (cascade supprimera automatiquement les secteurs, systèmes et planètes)
|
|
|
+ $this->entityManager->remove($galaxy);
|
|
|
+ $this->entityManager->flush();
|
|
|
+
|
|
|
+ return $stats;
|
|
|
+ }
|
|
|
+}
|