GameMatchingService::isExactMatch()   C
last analyzed

Complexity

Conditions 12
Paths 7

Size

Total Lines 33
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
eloc 20
nc 7
nop 2
dl 0
loc 33
rs 6.9666
c 1
b 0
f 0

How to fix   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\CoreBundle\Domain\Igdb\Service;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Psr\Log\LoggerInterface;
9
use VideoGamesRecords\CoreBundle\Entity\Game;
10
use VideoGamesRecords\CoreBundle\Domain\Igdb\DataTransformer\PlatformMappingTransformer;
11
use VideoGamesRecords\IgdbBundle\Client\IgdbClient;
12
use VideoGamesRecords\IgdbBundle\Entity\Game as IgdbGame;
13
14
class GameMatchingService
15
{
16
    public function __construct(
17
        private readonly EntityManagerInterface $entityManager,
18
        private readonly IgdbClient $igdbClient,
19
        private readonly PlatformMappingTransformer $platformMappingTransformer,
20
        private readonly IgdbMappingService $igdbMappingService,
21
        private readonly LoggerInterface $logger
22
    ) {
23
    }
24
25
    public function findAndAssociateIgdbGame(Game $coreGame): ?IgdbGame
26
    {
27
        if ($coreGame->getIgdbGame() !== null) {
28
            $this->logger->info('Game already has IGDB association', ['gameId' => $coreGame->getId()]);
29
            return $coreGame->getIgdbGame();
30
        }
31
32
        $gameName = $coreGame->getLibGameEn();
33
        $this->logger->info('Searching IGDB game for: ' . $gameName, ['gameId' => $coreGame->getId()]);
34
35
        $corePlatforms = $coreGame->getPlatforms();
36
        $igdbPlatformIds = [];
37
38
        foreach ($corePlatforms as $corePlatform) {
39
            $igdbPlatformId = $this->igdbMappingService->getPlatformIgdbId($corePlatform->getId());
40
            if ($igdbPlatformId !== null) {
41
                $igdbPlatformIds[] = $igdbPlatformId;
42
            }
43
        }
44
45
        if (empty($igdbPlatformIds)) {
46
            $this->logger->warning('No IGDB platform mappings found for game', [
47
                'gameId' => $coreGame->getId(),
48
                'gameName' => $gameName
49
            ]);
50
            return null;
51
        }
52
53
        $this->logger->info('Using IGDB platform IDs for search', [
54
            'gameId' => $coreGame->getId(),
55
            'igdbPlatformIds' => $igdbPlatformIds
56
        ]);
57
58
        $igdbGames = $this->igdbClient->searchGamesByName($gameName, $igdbPlatformIds, 50);
59
60
        foreach ($igdbGames as $igdbGameData) {
61
            if ($this->isExactMatch($coreGame, $igdbGameData)) {
62
                $igdbGame = $this->findOrCreateIgdbGame($igdbGameData);
63
64
                if ($igdbGame !== null) {
65
                    $coreGame->setIgdbGame($igdbGame);
66
                    $this->igdbMappingService->setGameMapping($coreGame->getId(), $igdbGame->getId());
0 ignored issues
show
Bug introduced by
It seems like $coreGame->getId() can also be of type null; however, parameter $vgrGameId of VideoGamesRecords\CoreBu...rvice::setGameMapping() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
                    $this->igdbMappingService->setGameMapping(/** @scrutinizer ignore-type */ $coreGame->getId(), $igdbGame->getId());
Loading history...
67
68
                    $this->entityManager->persist($coreGame);
69
                    $this->entityManager->flush();
70
71
                    $this->logger->info('Successfully associated IGDB game', [
72
                        'coreGameId' => $coreGame->getId(),
73
                        'igdbGameId' => $igdbGame->getId(),
74
                        'gameName' => $gameName
75
                    ]);
76
77
                    return $igdbGame;
78
                }
79
            }
80
        }
81
82
        $this->logger->info('No exact match found in IGDB', [
83
            'gameId' => $coreGame->getId(),
84
            'gameName' => $gameName,
85
            'searchResults' => count($igdbGames)
86
        ]);
87
88
        return null;
89
    }
90
91
    private function isExactMatch(Game $coreGame, array $igdbGameData): bool
92
    {
93
        $coreGameName = strtolower(trim($coreGame->getLibGameEn()));
94
        $igdbGameName = strtolower(trim($igdbGameData['name']));
95
96
        if ($coreGameName !== $igdbGameName) {
97
            return false;
98
        }
99
100
        $corePlatformIds = [];
101
        foreach ($coreGame->getPlatforms() as $platform) {
102
            $igdbPlatformId = $this->igdbMappingService->getPlatformIgdbId($platform->getId());
103
            if ($igdbPlatformId !== null) {
104
                $corePlatformIds[] = $igdbPlatformId;
105
            }
106
        }
107
108
        $igdbPlatformIds = [];
109
        if (isset($igdbGameData['platforms']) && is_array($igdbGameData['platforms'])) {
110
            foreach ($igdbGameData['platforms'] as $platformData) {
111
                if (is_array($platformData) && isset($platformData['id'])) {
112
                    $igdbPlatformIds[] = $platformData['id'];
113
                } elseif (is_numeric($platformData)) {
114
                    $igdbPlatformIds[] = (int) $platformData;
115
                } elseif (is_object($platformData) && property_exists($platformData, 'id')) {
116
                    $igdbPlatformIds[] = $platformData->id;
117
                }
118
            }
119
        }
120
121
        $commonPlatforms = array_intersect($corePlatformIds, $igdbPlatformIds);
122
123
        return !empty($commonPlatforms);
124
    }
125
126
    private function findOrCreateIgdbGame(array $igdbGameData): ?IgdbGame
127
    {
128
        $igdbGameRepository = $this->entityManager->getRepository(IgdbGame::class);
129
        $existingGame = $igdbGameRepository->find($igdbGameData['id']);
130
131
        if ($existingGame) {
132
            return $existingGame;
133
        }
134
135
        try {
136
            $igdbGame = new IgdbGame();
137
            $igdbGame->setId($igdbGameData['id']);
138
            $igdbGame->setName($igdbGameData['name']);
139
            $igdbGame->setSlug($igdbGameData['slug'] ?? null);
140
            $igdbGame->setStoryline($igdbGameData['storyline'] ?? null);
141
            $igdbGame->setSummary($igdbGameData['summary'] ?? null);
142
            $igdbGame->setUrl($igdbGameData['url'] ?? null);
143
            $igdbGame->setChecksum($igdbGameData['checksum'] ?? null);
144
            $igdbGame->setFirstReleaseDate($igdbGameData['first_release_date'] ?? null);
145
146
            if (isset($igdbGameData['created_at'])) {
147
                $igdbGame->setCreatedAt(new \DateTimeImmutable('@' . $igdbGameData['created_at']));
148
            }
149
150
            if (isset($igdbGameData['updated_at'])) {
151
                $igdbGame->setUpdatedAt(new \DateTimeImmutable('@' . $igdbGameData['updated_at']));
152
            }
153
154
            $this->entityManager->persist($igdbGame);
155
            $this->entityManager->flush();
156
157
            return $igdbGame;
158
        } catch (\Exception $e) {
159
            $this->logger->error('Failed to create IGDB game', [
160
                'igdbGameId' => $igdbGameData['id'],
161
                'error' => $e->getMessage()
162
            ]);
163
164
            return null;
165
        }
166
    }
167
168
    public function getMatchingStatistics(): array
169
    {
170
        $gameRepository = $this->entityManager->getRepository(Game::class);
171
172
        $totalGames = $gameRepository->createQueryBuilder('g')
173
            ->select('COUNT(g.id)')
174
            ->getQuery()
175
            ->getSingleScalarResult();
176
177
        $gamesWithIgdbAssociation = $gameRepository->createQueryBuilder('g')
178
            ->select('COUNT(g.id)')
179
            ->where('g.igdbGame IS NOT NULL')
180
            ->getQuery()
181
            ->getSingleScalarResult();
182
183
        $gamesWithoutIgdbAssociation = $totalGames - $gamesWithIgdbAssociation;
184
185
        return [
186
            'total_games' => $totalGames,
187
            'games_with_igdb_association' => $gamesWithIgdbAssociation,
188
            'games_without_igdb_association' => $gamesWithoutIgdbAssociation,
189
            'matching_percentage' => $totalGames > 0 ? round(($gamesWithIgdbAssociation / $totalGames) * 100, 2) : 0
190
        ];
191
    }
192
}
193