diff --git a/config/services_dev.yaml b/config/services_dev.yaml new file mode 100644 index 0000000..2396de1 --- /dev/null +++ b/config/services_dev.yaml @@ -0,0 +1,5 @@ +imports: + - { resource: services.yaml } + +services: + App\Application\SportRadar\SportRadarEngine: '@App\Infrastructure\SportRadar\FakeSportRadarEngine' diff --git a/phpstan.neon b/phpstan.neon index c345aba..e5d4cbe 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -7,3 +7,6 @@ parameters: message: "#^Property .+::.+ is never read, only written\\.$#" paths: - src/Domain/Entity/*.php + - + message: "#^Variable \\$metadata might not be defined\\.$#" + path: src/Infrastructure/Persistence/Mapping/*.php diff --git a/src/Application/ReadModel/Provider.php b/src/Application/ReadModel/Provider.php index adabdda..ac7eab1 100644 --- a/src/Application/ReadModel/Provider.php +++ b/src/Application/ReadModel/Provider.php @@ -9,4 +9,4 @@ class Provider public readonly string $name, ) { } -} \ No newline at end of file +} diff --git a/src/Application/UseCase/FetchNHLMatch.php b/src/Application/UseCase/FetchNHLMatch.php new file mode 100644 index 0000000..adc5aa9 --- /dev/null +++ b/src/Application/UseCase/FetchNHLMatch.php @@ -0,0 +1,55 @@ +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 $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"); + } +} diff --git a/src/Application/UseCase/GetNHLMatchRequest.php b/src/Application/UseCase/FetchNHLMatchRequest.php similarity index 87% rename from src/Application/UseCase/GetNHLMatchRequest.php rename to src/Application/UseCase/FetchNHLMatchRequest.php index 4c2951e..c440d2f 100644 --- a/src/Application/UseCase/GetNHLMatchRequest.php +++ b/src/Application/UseCase/FetchNHLMatchRequest.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\Application\UseCase; -class GetNHLMatchRequest +class FetchNHLMatchRequest { public function __construct( public readonly int $year, diff --git a/src/Application/UseCase/GetNHLMatch.php b/src/Application/UseCase/GetNHLMatch.php deleted file mode 100644 index e05aa1c..0000000 --- a/src/Application/UseCase/GetNHLMatch.php +++ /dev/null @@ -1,25 +0,0 @@ -entityManager->getRepository(Provider::class)->findAll()); - dd($this->sportRadarEngine->getNHLSchedule($request->year, ENHLSeasonType::from($request->type))['games'][0]); - } -} diff --git a/src/Domain/Entity/Game.php b/src/Domain/Entity/Game.php index 8605010..d132993 100644 --- a/src/Domain/Entity/Game.php +++ b/src/Domain/Entity/Game.php @@ -6,26 +6,32 @@ namespace App\Domain\Entity; class Game extends AEntity { - private string $srId; + private Provider $provider; + private string $providerGameId; private Season $season; - private \DateTimeImmutable $scheduled; + private \DateTimeImmutable $startTimeScheduled; + private ?\DateTimeImmutable $endTimeScheduled; private Team $home; private Team $away; - private string $venue_arena; + private string $venue; public function create( - string $srId, + Provider $provider, + string $providerGameId, Season $season, - \DateTimeImmutable $scheduled, + \DateTimeImmutable $startTimeScheduled, + ?\DateTimeImmutable $endTimeScheduled, Team $home, Team $away, - string $venue_arena, + string $venue, ): void { - $this->srId = $srId; + $this->provider = $provider; + $this->providerGameId = $providerGameId; $this->season = $season; - $this->scheduled = $scheduled; + $this->startTimeScheduled = $startTimeScheduled; + $this->endTimeScheduled = $endTimeScheduled; $this->home = $home; $this->away = $away; - $this->venue_arena = $venue_arena; + $this->venue = $venue; } } diff --git a/src/Domain/Entity/League.php b/src/Domain/Entity/League.php index 44bf48e..dbc0fed 100644 --- a/src/Domain/Entity/League.php +++ b/src/Domain/Entity/League.php @@ -6,11 +6,17 @@ namespace App\Domain\Entity; class League extends AEntity { + private Provider $provider; + private string $providerLeagueId; private string $name; public function create( + Provider $provider, + string $providerLeagueId, string $name, ): void { + $this->provider = $provider; + $this->providerLeagueId = $providerLeagueId; $this->name = $name; } } diff --git a/src/Domain/Entity/Provider.php b/src/Domain/Entity/Provider.php index 770e922..3b161f5 100644 --- a/src/Domain/Entity/Provider.php +++ b/src/Domain/Entity/Provider.php @@ -11,5 +11,4 @@ class Provider extends AEntity ): void { $this->name = $name; } - -} \ No newline at end of file +} diff --git a/src/Domain/Entity/Season.php b/src/Domain/Entity/Season.php index 2157d33..008ec1c 100644 --- a/src/Domain/Entity/Season.php +++ b/src/Domain/Entity/Season.php @@ -6,17 +6,23 @@ namespace App\Domain\Entity; class Season extends AEntity { + private Provider $provider; + private string $providerSeasonId; private League $league; - private string $type; // should be an enum private int $year; + private string $kind; // should be an enum public function create( + Provider $provider, + string $providerSeasonId, League $league, - string $type, + string $kind, int $year, ): void { + $this->provider = $provider; + $this->providerSeasonId = $providerSeasonId; $this->league = $league; - $this->type = $type; + $this->kind = $kind; $this->year = $year; } } diff --git a/src/Domain/Entity/Team.php b/src/Domain/Entity/Team.php index 9142baf..2ec9c6c 100644 --- a/src/Domain/Entity/Team.php +++ b/src/Domain/Entity/Team.php @@ -6,17 +6,23 @@ namespace App\Domain\Entity; class Team extends AEntity { + private Provider $provider; + private string $providerTeamId; private string $name; private string $alias; - private string $sr_id; + private bool $active; public function create( + Provider $provider, + string $providerTeamId, string $name, string $alias, - string $sr_id, + bool $active, ): void { + $this->provider = $provider; + $this->providerTeamId = $providerTeamId; $this->name = $name; $this->alias = $alias; - $this->sr_id = $sr_id; + $this->active = $active; } } diff --git a/src/Infrastructure/Console/GetNHLScheduleCommand.php b/src/Infrastructure/Console/GetNHLScheduleCommand.php index cecbbe5..c617676 100644 --- a/src/Infrastructure/Console/GetNHLScheduleCommand.php +++ b/src/Infrastructure/Console/GetNHLScheduleCommand.php @@ -3,7 +3,7 @@ namespace App\Infrastructure\Console; 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\Command\Command; @@ -17,7 +17,7 @@ class GetNHLScheduleCommand public function __invoke(): int { - $this->commandBus->ask(new GetNHLMatchRequest(2025, 'REG')); + $this->commandBus->ask(new FetchNHLMatchRequest(2025, 'REG')); return Command::SUCCESS; } diff --git a/src/Infrastructure/Persistence/Mapping/App.Application.ReadModel.Provider.php b/src/Infrastructure/Persistence/Mapping/App.Application.ReadModel.Provider.php index ee85b3e..d4dcbd8 100644 --- a/src/Infrastructure/Persistence/Mapping/App.Application.ReadModel.Provider.php +++ b/src/Infrastructure/Persistence/Mapping/App.Application.ReadModel.Provider.php @@ -18,4 +18,4 @@ $builder $builder ->createField('name', 'string') ->nullable(false) - ->build(); \ No newline at end of file + ->build(); diff --git a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Game.php b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Game.php new file mode 100644 index 0000000..8e92ce9 --- /dev/null +++ b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Game.php @@ -0,0 +1,59 @@ +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(); diff --git a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.League.php b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.League.php new file mode 100644 index 0000000..7d67bcc --- /dev/null +++ b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.League.php @@ -0,0 +1,27 @@ +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(); diff --git a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Provider.php b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Provider.php index ee85b3e..d4dcbd8 100644 --- a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Provider.php +++ b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Provider.php @@ -18,4 +18,4 @@ $builder $builder ->createField('name', 'string') ->nullable(false) - ->build(); \ No newline at end of file + ->build(); diff --git a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Season.php b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Season.php new file mode 100644 index 0000000..3656d8c --- /dev/null +++ b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Season.php @@ -0,0 +1,42 @@ +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(); diff --git a/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Team.php b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Team.php new file mode 100644 index 0000000..cc4a2cc --- /dev/null +++ b/src/Infrastructure/Persistence/Mapping/App.Domain.Entity.Team.php @@ -0,0 +1,42 @@ +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(); diff --git a/src/Infrastructure/SportRadar/FakeSportRadarEngine.php b/src/Infrastructure/SportRadar/FakeSportRadarEngine.php new file mode 100644 index 0000000..2518159 --- /dev/null +++ b/src/Infrastructure/SportRadar/FakeSportRadarEngine.php @@ -0,0 +1,40 @@ + [ + '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']), + ]; + } +} diff --git a/debug.json b/src/Infrastructure/SportRadar/Fixtures/sport_radar_nhl_schedule_2025_REG_19_12_2025.json similarity index 100% rename from debug.json rename to src/Infrastructure/SportRadar/Fixtures/sport_radar_nhl_schedule_2025_REG_19_12_2025.json