SearchAndImportGamesCommand::execute()   D
last analyzed

Complexity

Conditions 14
Paths 244

Size

Total Lines 88
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 14
eloc 56
c 1
b 0
f 0
nc 244
nop 2
dl 0
loc 88
rs 4.8833

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace VideoGamesRecords\IgdbBundle\Command;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Symfony\Component\Console\Attribute\AsCommand;
9
use Symfony\Component\Console\Command\Command;
10
use Symfony\Component\Console\Input\InputArgument;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Input\InputOption;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use Symfony\Component\Console\Style\SymfonyStyle;
15
use VideoGamesRecords\IgdbBundle\Entity\Game;
16
use VideoGamesRecords\IgdbBundle\Entity\Genre;
17
use VideoGamesRecords\IgdbBundle\Entity\Platform;
18
use VideoGamesRecords\IgdbBundle\Client\IgdbClient;
19
20
#[AsCommand(
21
    name: 'igdb:search-import:games',
22
    description: 'Search and import specific games from IGDB API by name and platform'
23
)]
24
class SearchAndImportGamesCommand extends Command
25
{
26
    public function __construct(
27
        private readonly IgdbClient $igdbClient,
28
        private readonly EntityManagerInterface $entityManager
29
    ) {
30
        parent::__construct();
31
    }
32
33
    protected function configure(): void
34
    {
35
        $this
36
            ->addArgument('name', InputArgument::REQUIRED, 'Game name to search for')
37
            ->addOption(
38
                'platform',
39
                'p',
40
                InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
41
                'Platform ID(s) to filter by'
42
            )
43
            ->addOption('limit', 'l', InputOption::VALUE_OPTIONAL, 'Maximum number of results to process', 10)
44
            ->addOption('exact-match', 'x', InputOption::VALUE_NONE, 'Only import games with exact name match')
45
            ->addOption(
46
                'dry-run',
47
                'd',
48
                InputOption::VALUE_NONE,
49
                'Show what would be imported without actually importing'
50
            );
51
    }
52
53
    protected function execute(InputInterface $input, OutputInterface $output): int
54
    {
55
        $io = new SymfonyStyle($input, $output);
56
57
        $gameName = $input->getArgument('name');
58
        $platformIds = $input->getOption('platform');
59
        $limit = (int) $input->getOption('limit');
60
        $exactMatch = $input->getOption('exact-match');
61
        $dryRun = $input->getOption('dry-run');
62
63
        $io->title("Searching for games matching: '$gameName'");
64
65
        if (!empty($platformIds)) {
66
            $platformIds = array_map('intval', $platformIds);
67
            $io->info('Filtering by platform IDs: ' . implode(', ', $platformIds));
68
        }
69
70
        try {
71
            $games = $this->igdbClient->searchGamesByName($gameName, $platformIds, $limit);
72
73
            if (empty($games)) {
74
                $io->warning('No games found matching your criteria.');
75
                return Command::SUCCESS;
76
            }
77
78
            $io->info(sprintf('Found %d game(s) in IGDB', count($games)));
79
80
            $toImport = [];
81
            foreach ($games as $gameData) {
82
                // Skip if exact match required and names don't match exactly
83
                if ($exactMatch && strcasecmp($gameData['name'], $gameName) !== 0) {
84
                    continue;
85
                }
86
87
                // Check if game already exists in our database
88
                $existingGame = $this->entityManager->getRepository(Game::class)->find($gameData['id']);
89
                if ($existingGame) {
90
                    $io->note(sprintf(
91
                        'Game "%s" (ID: %d) already exists in database',
92
                        $gameData['name'],
93
                        $gameData['id']
94
                    ));
95
                    continue;
96
                }
97
98
                $toImport[] = $gameData;
99
            }
100
101
            if (empty($toImport)) {
102
                $io->success('All matching games are already in your database.');
103
                return Command::SUCCESS;
104
            }
105
106
            $io->section(sprintf('Games to import (%d):', count($toImport)));
107
            $table = $io->createTable();
108
            $table->setHeaders(['ID', 'Name', 'Platforms', 'First Release Date']);
109
110
            foreach ($toImport as $gameData) {
111
                $platforms = $this->getPlatformNames($gameData['platforms'] ?? []);
112
                $releaseDate = isset($gameData['first_release_date']) && is_numeric($gameData['first_release_date'])
113
                    ? date('Y-m-d', $gameData['first_release_date'])
114
                    : 'N/A';
115
116
                $table->addRow([
117
                    $gameData['id'],
118
                    $gameData['name'],
119
                    implode(', ', $platforms),
120
                    $releaseDate
121
                ]);
122
            }
123
            $table->render();
124
125
            if ($dryRun) {
126
                $io->note('Dry run mode - no games were imported.');
127
                return Command::SUCCESS;
128
            }
129
130
            if (!$io->confirm('Do you want to import these games?', false)) {
131
                $io->info('Import cancelled.');
132
                return Command::SUCCESS;
133
            }
134
135
            $this->importGames($toImport, $io);
136
137
            return Command::SUCCESS;
138
        } catch (\Exception $e) {
139
            $io->error('Failed to search and import games: ' . $e->getMessage());
140
            return Command::FAILURE;
141
        }
142
    }
143
144
    private function importGames(array $games, SymfonyStyle $io): void
145
    {
146
        $io->progressStart(count($games));
147
        $importedCount = 0;
148
149
        foreach ($games as $gameData) {
150
            $game = new Game();
151
            $this->updateGame($game, $gameData);
152
            $game->setId($gameData['id']);
153
154
            if (isset($gameData['created_at'])) {
155
                $game->setCreatedAt(new \DateTimeImmutable('@' . $gameData['created_at']));
156
            }
157
158
            $this->entityManager->persist($game);
159
            $importedCount++;
160
            $io->progressAdvance();
161
        }
162
163
        $this->entityManager->flush();
164
        $io->progressFinish();
165
166
        $io->success(sprintf('Successfully imported %d games!', $importedCount));
167
    }
168
169
    private function updateGame(Game $game, array $gameData): void
170
    {
171
        $game->setName($gameData['name']);
172
        $game->setSlug($gameData['slug'] ?? null);
173
        $game->setStoryline($gameData['storyline'] ?? null);
174
        $game->setSummary($gameData['summary'] ?? null);
175
        $game->setUrl($gameData['url'] ?? null);
176
        $game->setChecksum($gameData['checksum'] ?? null);
177
        $game->setFirstReleaseDate($gameData['first_release_date'] ?? null);
178
179
        // Gérer l'association avec version_parent (auto-référence)
180
        if (isset($gameData['version_parent']) && is_numeric($gameData['version_parent'])) {
181
            $versionParent = $this->entityManager->getRepository(Game::class)->find($gameData['version_parent']);
182
            $game->setVersionParent($versionParent);
183
        }
184
185
        // Gérer les associations ManyToMany avec les genres
186
        if (isset($gameData['genres']) && is_array($gameData['genres'])) {
187
            foreach ($gameData['genres'] as $genreId) {
188
                $genre = $this->entityManager->getRepository(Genre::class)->find($genreId);
189
                if ($genre) {
190
                    $game->addGenre($genre);
191
                }
192
            }
193
        }
194
195
        // Gérer les associations ManyToMany avec les plateformes
196
        if (isset($gameData['platforms']) && is_array($gameData['platforms'])) {
197
            foreach ($gameData['platforms'] as $platformData) {
198
                $platformId = null;
199
                if (is_object($platformData)) {
200
                    $platformArray = (array) $platformData;
201
                    $platformId = $platformArray['id'] ?? null;
202
                } elseif (is_array($platformData)) {
203
                    $platformId = $platformData['id'] ?? null;
204
                } elseif (is_numeric($platformData)) {
205
                    $platformId = $platformData;
206
                }
207
208
                if ($platformId) {
209
                    $platform = $this->entityManager->getRepository(Platform::class)->find($platformId);
210
                    if ($platform) {
211
                        $game->addPlatform($platform);
212
                    }
213
                }
214
            }
215
        }
216
217
        if (isset($gameData['updated_at'])) {
218
            $game->setUpdatedAt(new \DateTimeImmutable('@' . $gameData['updated_at']));
219
        } else {
220
            $game->setUpdatedAt(new \DateTimeImmutable());
221
        }
222
    }
223
224
    private function getPlatformNames(array $platforms): array
225
    {
226
        $names = [];
227
        foreach ($platforms as $platformData) {
228
            if (is_object($platformData)) {
229
                // Convert stdClass to array
230
                $platformArray = (array) $platformData;
231
                if (isset($platformArray['name'])) {
232
                    $names[] = $platformArray['name'];
233
                } elseif (isset($platformArray['id'])) {
234
                    $platform = $this->entityManager->getRepository(Platform::class)->find($platformArray['id']);
235
                    if ($platform) {
236
                        $names[] = $platform->getName();
237
                    }
238
                }
239
            } elseif (is_array($platformData) && isset($platformData['name'])) {
240
                $names[] = $platformData['name'];
241
            } elseif (is_numeric($platformData)) {
242
                $platform = $this->entityManager->getRepository(Platform::class)->find($platformData);
243
                if ($platform) {
244
                    $names[] = $platform->getName();
245
                }
246
            }
247
        }
248
        return $names;
249
    }
250
}
251