Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
40.58% |
28 / 69 |
|
20.00% |
1 / 5 |
CRAP | |
0.00% |
0 / 1 |
| FindDupesCommand | |
40.58% |
28 / 69 |
|
20.00% |
1 / 5 |
77.63 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| execute | |
95.45% |
21 / 22 |
|
0.00% |
0 / 1 |
3 | |||
| findDuplicatesForWaypoint | |
35.29% |
6 / 17 |
|
0.00% |
0 / 1 |
15.75 | |||
| handleDuplicate | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
| applyUserChoice | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
| 1 | <?php |
| 2 | |
| 3 | declare(strict_types=1); |
| 4 | |
| 5 | namespace App\Command; |
| 6 | |
| 7 | use Symfony\Component\Console\Helper\QuestionHelper; |
| 8 | use App\Entity\Waypoint; |
| 9 | use App\Repository\WaypointRepository; |
| 10 | use Doctrine\ORM\EntityManagerInterface; |
| 11 | use Symfony\Component\Console\Attribute\AsCommand; |
| 12 | use Symfony\Component\Console\Command\Command; |
| 13 | use Symfony\Component\Console\Helper\ProgressBar; |
| 14 | use Symfony\Component\Console\Input\InputInterface; |
| 15 | use Symfony\Component\Console\Output\OutputInterface; |
| 16 | use Symfony\Component\Console\Question\ChoiceQuestion; |
| 17 | use Symfony\Component\Console\Style\SymfonyStyle; |
| 18 | |
| 19 | #[AsCommand( |
| 20 | name: 'app:find-dupes', |
| 21 | description: 'Find duplicated entries' |
| 22 | )] |
| 23 | class FindDupesCommand extends Command |
| 24 | { |
| 25 | public function __construct( |
| 26 | private readonly EntityManagerInterface $entityManager, |
| 27 | private readonly WaypointRepository $waypointRepository |
| 28 | ) |
| 29 | { |
| 30 | parent::__construct(); |
| 31 | } |
| 32 | |
| 33 | protected function execute( |
| 34 | InputInterface $input, |
| 35 | OutputInterface $output |
| 36 | ): int |
| 37 | { |
| 38 | $io = new SymfonyStyle($input, $output); |
| 39 | $waypoints = $this->waypointRepository->findAll(); |
| 40 | $progressBar = new ProgressBar($output, count($waypoints)); |
| 41 | /** @var QuestionHelper $helper */ |
| 42 | $helper = $this->getHelper('question'); |
| 43 | |
| 44 | $choices = [ |
| 45 | 'Remove A', |
| 46 | 'Remove B', |
| 47 | 'Change name A with B', |
| 48 | 'Change name B with A', |
| 49 | 'Skip', |
| 50 | ]; |
| 51 | |
| 52 | $question = new ChoiceQuestion('Please select [Skip]', $choices, 4); |
| 53 | $question->setErrorMessage('Choice %s is invalid.'); |
| 54 | |
| 55 | $removals = 0; |
| 56 | |
| 57 | foreach ($waypoints as $waypoint) { |
| 58 | $removals += $this->findDuplicatesForWaypoint($waypoint, $waypoints, $io, $helper, $input, $output, $choices, $question); |
| 59 | $progressBar->advance(); |
| 60 | } |
| 61 | |
| 62 | $progressBar->finish(); |
| 63 | |
| 64 | if ($removals !== 0) { |
| 65 | $io->warning(sprintf('%d duplicates have been removed.', $removals)); |
| 66 | } else { |
| 67 | $io->success('Database is clean :)'); |
| 68 | } |
| 69 | |
| 70 | return Command::SUCCESS; |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * @param Waypoint[] $waypoints |
| 75 | * @param string[] $choices |
| 76 | */ |
| 77 | private function findDuplicatesForWaypoint( |
| 78 | Waypoint $waypoint, |
| 79 | array $waypoints, |
| 80 | SymfonyStyle $io, |
| 81 | QuestionHelper $helper, |
| 82 | InputInterface $input, |
| 83 | OutputInterface $output, |
| 84 | array $choices, |
| 85 | ChoiceQuestion $question |
| 86 | ): int { |
| 87 | foreach ($waypoints as $test) { |
| 88 | if ($test->getId() === $waypoint->getId()) { |
| 89 | continue; |
| 90 | } |
| 91 | |
| 92 | if ($test->getGuid() === $waypoint->getGuid()) { |
| 93 | $io->warning('@TODO Duplicated GUID found for: '.$waypoint->getName()); |
| 94 | } |
| 95 | |
| 96 | if ($test->getLat() === $waypoint->getLat() && $test->getLon() === $waypoint->getLon()) { |
| 97 | return $this->handleDuplicate( |
| 98 | $waypoint, |
| 99 | $test, |
| 100 | $io, |
| 101 | $helper, |
| 102 | $input, |
| 103 | $output, |
| 104 | $choices, |
| 105 | $question |
| 106 | ); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | /** |
| 114 | * @param string[] $choices |
| 115 | */ |
| 116 | private function handleDuplicate( |
| 117 | Waypoint $waypoint, |
| 118 | Waypoint $test, |
| 119 | SymfonyStyle $io, |
| 120 | QuestionHelper $helper, |
| 121 | InputInterface $input, |
| 122 | OutputInterface $output, |
| 123 | array $choices, |
| 124 | ChoiceQuestion $question |
| 125 | ): int { |
| 126 | $io->text([ |
| 127 | '', |
| 128 | sprintf('A: %s - %d', $waypoint->getName(), $waypoint->getId()), |
| 129 | sprintf('B: %s - %d', $test->getName(), $test->getId()), |
| 130 | ]); |
| 131 | |
| 132 | if ($waypoint->getName() === $test->getName()) { |
| 133 | $this->entityManager->remove($test); |
| 134 | $this->entityManager->flush(); |
| 135 | return 1; |
| 136 | } |
| 137 | |
| 138 | $io->warning('Name mismatch!'); |
| 139 | /** @var string $choice */ |
| 140 | $choice = $helper->ask($input, $output, $question); |
| 141 | |
| 142 | return $this->applyUserChoice($choice, $waypoint, $test, $choices); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * @param string[] $choices |
| 147 | */ |
| 148 | private function applyUserChoice( |
| 149 | string $choice, |
| 150 | Waypoint $waypoint, |
| 151 | Waypoint $test, |
| 152 | array $choices |
| 153 | ): int { |
| 154 | if ($choice === $choices[0]) { |
| 155 | $this->entityManager->remove($waypoint); |
| 156 | $this->entityManager->flush(); |
| 157 | return 1; |
| 158 | } |
| 159 | |
| 160 | if ($choice === $choices[1]) { |
| 161 | $this->entityManager->remove($test); |
| 162 | $this->entityManager->flush(); |
| 163 | return 1; |
| 164 | } |
| 165 | |
| 166 | if ($choice === $choices[2]) { |
| 167 | $waypoint->setName((string)$test->getName()); |
| 168 | $this->entityManager->persist($waypoint); |
| 169 | $this->entityManager->flush(); |
| 170 | } elseif ($choice === $choices[3]) { |
| 171 | $test->setName((string)$waypoint->getName()); |
| 172 | $this->entityManager->persist($test); |
| 173 | $this->entityManager->flush(); |
| 174 | } |
| 175 | |
| 176 | return 0; |
| 177 | } |
| 178 | } |