feat: FetchNHLMatch + FakeSportRadarEngine

This commit is contained in:
Aymeric GUERACAGUE 2025-12-20 22:46:29 +01:00
parent 59991f006c
commit edd4a185da
Signed by: Superkooka
GPG Key ID: F78F2B172E894865
20 changed files with 319 additions and 48 deletions

5
config/services_dev.yaml Normal file
View File

@ -0,0 +1,5 @@
imports:
- { resource: services.yaml }
services:
App\Application\SportRadar\SportRadarEngine: '@App\Infrastructure\SportRadar\FakeSportRadarEngine'

View File

@ -7,3 +7,6 @@ parameters:
message: "#^Property .+::.+ is never read, only written\\.$#" message: "#^Property .+::.+ is never read, only written\\.$#"
paths: paths:
- src/Domain/Entity/*.php - src/Domain/Entity/*.php
-
message: "#^Variable \\$metadata might not be defined\\.$#"
path: src/Infrastructure/Persistence/Mapping/*.php

View File

@ -9,4 +9,4 @@ class Provider
public readonly string $name, public readonly string $name,
) { ) {
} }
} }

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
namespace App\Application\UseCase;
use App\Application\SportRadar\SportRadarEngine;
use App\Domain\Entity\Game;
use App\Domain\Entity\Provider;
use App\Domain\Entity\Season;
use App\Domain\Entity\Team;
use App\Domain\Enum\ENHLSeasonType;
use Doctrine\ORM\EntityManagerInterface;
class FetchNHLMatch
{
public function __construct(
private readonly SportRadarEngine $sportRadarEngine,
private readonly EntityManagerInterface $entityManager,
) {
}
public function __invoke(FetchNHLMatchRequest $request): void
{
$provider = $this->entityManager->getRepository(Provider::class)->findOneBy(['id' => '885fe581-c4c3-45e7-a06c-29ece7d47fad']); // id should not be here
$matchs = $this->sportRadarEngine->getNHLSchedule($request->year, ENHLSeasonType::from($request->type));
$season = $this->entityManager->getRepository(Season::class)->findOneBy(['providerSeasonId' => $matchs['season']['providerId']]);
/** @var array<string, Team> $teams */
$teams = [];
foreach ($matchs['games'] as $match) {
$game = new Game();
$game->create(
$provider,
$match['providerId'],
$season,
$match['scheduled'],
null,
$teams[$match['homeTeamProviderId']] ?? $teams[$match['homeTeamProviderId']] = $this->findTeamOrThrow($provider, $match['homeTeamProviderId']),
$teams[$match['awayTeamProviderId']] ?? $teams[$match['awayTeamProviderId']] = $this->findTeamOrThrow($provider, $match['awayTeamProviderId']),
$match['venueArena'],
);
$this->entityManager->persist($game);
}
$this->entityManager->flush();
}
private function findTeamOrThrow(Provider $provider, string $teamId): Team // should be in a trait?
{
return $this->entityManager->getRepository(Team::class)->findOneBy(['provider' => $provider, 'providerTeamId' => $teamId]) ?? throw new \Exception("Team $teamId not found");
}
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Application\UseCase; namespace App\Application\UseCase;
class GetNHLMatchRequest class FetchNHLMatchRequest
{ {
public function __construct( public function __construct(
public readonly int $year, public readonly int $year,

View File

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Application\UseCase;
use App\Application\ReadModel\Provider;
use App\Application\SportRadar\SportRadarEngine;
use App\Domain\Enum\ENHLSeasonType;
use Doctrine\ORM\EntityManagerInterface;
class GetNHLMatch
{
public function __construct(
private readonly SportRadarEngine $sportRadarEngine,
private readonly EntityManagerInterface $entityManager,
) {
}
public function __invoke(GetNHLMatchRequest $request): void
{
dd($this->entityManager->getRepository(Provider::class)->findAll());
dd($this->sportRadarEngine->getNHLSchedule($request->year, ENHLSeasonType::from($request->type))['games'][0]);
}
}

View File

@ -6,26 +6,32 @@ namespace App\Domain\Entity;
class Game extends AEntity class Game extends AEntity
{ {
private string $srId; private Provider $provider;
private string $providerGameId;
private Season $season; private Season $season;
private \DateTimeImmutable $scheduled; private \DateTimeImmutable $startTimeScheduled;
private ?\DateTimeImmutable $endTimeScheduled;
private Team $home; private Team $home;
private Team $away; private Team $away;
private string $venue_arena; private string $venue;
public function create( public function create(
string $srId, Provider $provider,
string $providerGameId,
Season $season, Season $season,
\DateTimeImmutable $scheduled, \DateTimeImmutable $startTimeScheduled,
?\DateTimeImmutable $endTimeScheduled,
Team $home, Team $home,
Team $away, Team $away,
string $venue_arena, string $venue,
): void { ): void {
$this->srId = $srId; $this->provider = $provider;
$this->providerGameId = $providerGameId;
$this->season = $season; $this->season = $season;
$this->scheduled = $scheduled; $this->startTimeScheduled = $startTimeScheduled;
$this->endTimeScheduled = $endTimeScheduled;
$this->home = $home; $this->home = $home;
$this->away = $away; $this->away = $away;
$this->venue_arena = $venue_arena; $this->venue = $venue;
} }
} }

View File

@ -6,11 +6,17 @@ namespace App\Domain\Entity;
class League extends AEntity class League extends AEntity
{ {
private Provider $provider;
private string $providerLeagueId;
private string $name; private string $name;
public function create( public function create(
Provider $provider,
string $providerLeagueId,
string $name, string $name,
): void { ): void {
$this->provider = $provider;
$this->providerLeagueId = $providerLeagueId;
$this->name = $name; $this->name = $name;
} }
} }

View File

@ -11,5 +11,4 @@ class Provider extends AEntity
): void { ): void {
$this->name = $name; $this->name = $name;
} }
}
}

View File

@ -6,17 +6,23 @@ namespace App\Domain\Entity;
class Season extends AEntity class Season extends AEntity
{ {
private Provider $provider;
private string $providerSeasonId;
private League $league; private League $league;
private string $type; // should be an enum
private int $year; private int $year;
private string $kind; // should be an enum
public function create( public function create(
Provider $provider,
string $providerSeasonId,
League $league, League $league,
string $type, string $kind,
int $year, int $year,
): void { ): void {
$this->provider = $provider;
$this->providerSeasonId = $providerSeasonId;
$this->league = $league; $this->league = $league;
$this->type = $type; $this->kind = $kind;
$this->year = $year; $this->year = $year;
} }
} }

View File

@ -6,17 +6,23 @@ namespace App\Domain\Entity;
class Team extends AEntity class Team extends AEntity
{ {
private Provider $provider;
private string $providerTeamId;
private string $name; private string $name;
private string $alias; private string $alias;
private string $sr_id; private bool $active;
public function create( public function create(
Provider $provider,
string $providerTeamId,
string $name, string $name,
string $alias, string $alias,
string $sr_id, bool $active,
): void { ): void {
$this->provider = $provider;
$this->providerTeamId = $providerTeamId;
$this->name = $name; $this->name = $name;
$this->alias = $alias; $this->alias = $alias;
$this->sr_id = $sr_id; $this->active = $active;
} }
} }

View File

@ -3,7 +3,7 @@
namespace App\Infrastructure\Console; namespace App\Infrastructure\Console;
use App\Application\CommandBus\UseCaseCommandBus; use App\Application\CommandBus\UseCaseCommandBus;
use App\Application\UseCase\GetNHLMatchRequest; use App\Application\UseCase\FetchNHLMatchRequest;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
@ -17,7 +17,7 @@ class GetNHLScheduleCommand
public function __invoke(): int public function __invoke(): int
{ {
$this->commandBus->ask(new GetNHLMatchRequest(2025, 'REG')); $this->commandBus->ask(new FetchNHLMatchRequest(2025, 'REG'));
return Command::SUCCESS; return Command::SUCCESS;
} }

View File

@ -18,4 +18,4 @@ $builder
$builder $builder
->createField('name', 'string') ->createField('name', 'string')
->nullable(false) ->nullable(false)
->build(); ->build();

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
$builder = new Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder($metadata);
$builder
->setReadOnly()
->setTable('game')
;
$builder
->createField('id', 'uuid')
->nullable(false)
->makePrimaryKey()
->build();
$builder
->createField('providerGameId', 'uuid')
->columnName('provider_game_id')
->nullable(false)
->build();
$builder
->createField('startTimeScheduled', 'datetimetz_immutable')
->columnName('start_time_scheduled')
->nullable(false)
->build();
$builder
->createField('endTimeScheduled', 'datetimetz_immutable')
->columnName('end_time_scheduled')
->nullable(true)
->build();
$builder
->createField('venue', 'string')
->nullable(false)
->build();
$builder
->createManyToOne('provider', App\Domain\Entity\Provider::class)
->addJoinColumn('provider_id', 'id', false, false, 'CASCADE')
->build();
$builder
->createManyToOne('season', App\Domain\Entity\Season::class)
->addJoinColumn('season_id', 'id', false, false, 'CASCADE')
->build();
$builder
->createManyToOne('home', App\Domain\Entity\Team::class)
->addJoinColumn('home_team_id', 'id', false, false, 'CASCADE')
->build();
$builder
->createManyToOne('away', App\Domain\Entity\Team::class)
->addJoinColumn('away_team_id', 'id', false, false, 'CASCADE')
->build();

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
$builder = new Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder($metadata);
$builder
->setReadOnly()
->setTable('league')
;
$builder
->createField('id', 'uuid')
->nullable(false)
->makePrimaryKey()
->build();
$builder
->createField('providerLeagueId', 'uuid')
->columnName('provider_league_id')
->nullable(false)
->build();
$builder
->createManyToOne('provider', 'App\\Domain\\Entity\\Provider')
->addJoinColumn('provider_id', 'id', false, false, 'CASCADE')
->build();

View File

@ -18,4 +18,4 @@ $builder
$builder $builder
->createField('name', 'string') ->createField('name', 'string')
->nullable(false) ->nullable(false)
->build(); ->build();

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
$builder = new Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder($metadata);
$builder
->setReadOnly()
->setTable('season')
;
$builder
->createField('id', 'uuid')
->nullable(false)
->makePrimaryKey()
->build();
$builder
->createField('providerSeasonId', 'uuid')
->columnName('provider_season_id')
->nullable(false)
->build();
$builder
->createField('year', 'integer')
->nullable(false)
->build();
$builder
->createField('kind', 'string') // should be enum
->nullable(false)
->build();
$builder
->createManyToOne('provider', App\Domain\Entity\Provider::class)
->addJoinColumn('provider_id', 'id', false, false, 'CASCADE')
->build();
$builder
->createManyToOne('league', App\Domain\Entity\League::class)
->addJoinColumn('league_id', 'id', false, false, 'CASCADE')
->build();

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
$builder = new Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder($metadata);
$builder
->setReadOnly()
->setTable('team')
;
$builder
->createField('id', 'uuid')
->nullable(false)
->makePrimaryKey()
->build();
$builder
->createField('providerTeamId', 'uuid')
->columnName('provider_team_id')
->nullable(false)
->build();
$builder
->createField('name', 'string')
->nullable(false)
->build();
$builder
->createField('alias', 'string')
->nullable(false)
->build();
$builder
->createField('active', 'boolean')
->nullable(false)
->build();
$builder
->createManyToOne('provider', App\Domain\Entity\Provider::class)
->addJoinColumn('provider_id', 'id', false, false, 'CASCADE')
->build();

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Infrastructure\SportRadar;
use App\Application\SportRadar\SportRadarEngine;
use App\Domain\Enum\ENHLSeasonType;
class FakeSportRadarEngine implements SportRadarEngine
{
public function getNHLSchedule(int $year, ENHLSeasonType $seasonType): array
{
if (2025 !== $year || ENHLSeasonType::REGULAR_SEASON !== $seasonType) {
return [];
}
$fake_sport_radar_nhl_schedule = json_decode(file_get_contents(__DIR__ . '/Fixtures/sport_radar_nhl_schedule_2025_REG_19_12_2025.json'), true);
return [
'league' => [
'providerId' => $fake_sport_radar_nhl_schedule['league']['id'],
'name' => $fake_sport_radar_nhl_schedule['league']['name'],
'alias' => $fake_sport_radar_nhl_schedule['league']['alias'],
],
'season' => [
'providerId' => $fake_sport_radar_nhl_schedule['season']['id'],
'year' => $fake_sport_radar_nhl_schedule['season']['year'],
'type' => ENHLSeasonType::from($fake_sport_radar_nhl_schedule['season']['type']),
],
'games' => array_map(fn ($game) => [
'providerId' => $game['id'],
'scheduled' => new \DateTimeImmutable($game['scheduled']),
'homeTeamProviderId' => $game['home']['id'],
'awayTeamProviderId' => $game['away']['id'],
'venueArena' => $game['venue']['name'],
], $fake_sport_radar_nhl_schedule['games']),
];
}
}