From 87a0491ae27fab95bb991a560949bb921cf84fbc Mon Sep 17 00:00:00 2001 From: Marius Klocke Date: Sun, 2 Feb 2025 14:29:27 +0100 Subject: [PATCH] Refactor filter handling --- src/Infrastructure/API/GraphQL/EventType.php | 6 +- .../GraphQL/Loader/BufferedMatchDayLoader.php | 8 +-- .../GraphQL/Loader/BufferedMatchLoader.php | 2 +- .../GraphQL/Loader/BufferedPitchLoader.php | 2 +- .../API/GraphQL/Loader/BufferedTeamLoader.php | 2 +- src/Infrastructure/API/GraphQL/MatchType.php | 4 +- src/Infrastructure/API/GraphQL/TeamType.php | 2 +- .../Persistence/Read/AbstractRepository.php | 14 ++++- .../Read/Criteria/EqualityFilter.php | 38 ++---------- .../Persistence/Read/Criteria/Filter.php | 14 +---- .../Read/Criteria/PatternFilter.php | 15 +---- .../Persistence/Read/Criteria/RangeFilter.php | 61 ++++--------------- .../Persistence/Read/Criteria/Sorting.php | 16 ++--- .../Persistence/Read/DbalGateway.php | 31 +++------- .../Persistence/Read/Field/DateField.php | 7 +++ .../Persistence/Read/Field/DateTimeField.php | 7 +++ .../Read/Field/EmbeddedObjectField.php | 5 ++ .../Persistence/Read/Field/Field.php | 2 + .../Persistence/Read/Field/FloatField.php | 7 +++ .../Persistence/Read/Field/IntegerField.php | 7 +++ .../Read/Field/SerializedArrayField.php | 5 ++ .../Persistence/Read/Field/StringField.php | 7 +++ .../Persistence/Read/RankingRepository.php | 33 ++++++++-- .../Persistence/Read/TeamRepository.php | 4 +- 24 files changed, 137 insertions(+), 162 deletions(-) diff --git a/src/Infrastructure/API/GraphQL/EventType.php b/src/Infrastructure/API/GraphQL/EventType.php index a4ca9305..a34db34b 100644 --- a/src/Infrastructure/API/GraphQL/EventType.php +++ b/src/Infrastructure/API/GraphQL/EventType.php @@ -65,7 +65,7 @@ public function getQueries(): array if (isset($args['start_date']) || isset($args['end_date'])) { $filters[] = new RangeFilter( - 'occurred_at', + $repo->getField('occurred_at'), Filter::MODE_INCLUDE, $args['start_date'] ?? null, $args['end_date'] ?? null @@ -74,7 +74,7 @@ public function getQueries(): array if (isset($args['type'])) { $filters[] = new EqualityFilter( - 'type', + $repo->getField('type'), Filter::MODE_INCLUDE, [$args['type']] ); @@ -82,7 +82,7 @@ public function getQueries(): array return $repo->findMany( $filters, - [new Sorting('occurred_at', Sorting::DIRECTION_DESCENDING)], + [new Sorting($repo->getField('occurred_at'), Sorting::DIRECTION_DESCENDING)], new Pagination(50, 0) ); } diff --git a/src/Infrastructure/API/GraphQL/Loader/BufferedMatchDayLoader.php b/src/Infrastructure/API/GraphQL/Loader/BufferedMatchDayLoader.php index c96281da..bf15f630 100644 --- a/src/Infrastructure/API/GraphQL/Loader/BufferedMatchDayLoader.php +++ b/src/Infrastructure/API/GraphQL/Loader/BufferedMatchDayLoader.php @@ -59,13 +59,13 @@ public function getBySeason(string $seasonId): array if (count($seasonIds)) { $filter = new EqualityFilter( - 'season_id', + $this->matchDayRepository->getField('season_id'), Filter::MODE_INCLUDE, $seasonIds ); $sorting = new Sorting( - 'number', + $this->matchDayRepository->getField('number'), Sorting::DIRECTION_ASCENDING ); @@ -94,13 +94,13 @@ public function getByTournament(string $tournamentId): array if (count($tournamentIds)) { $filter = new EqualityFilter( - 'tournament_id', + $this->matchDayRepository->getField('season_id'), Filter::MODE_INCLUDE, $tournamentIds ); $sorting = new Sorting( - 'number', + $this->matchDayRepository->getField('number'), Sorting::DIRECTION_ASCENDING ); diff --git a/src/Infrastructure/API/GraphQL/Loader/BufferedMatchLoader.php b/src/Infrastructure/API/GraphQL/Loader/BufferedMatchLoader.php index aa918e19..614be2e0 100644 --- a/src/Infrastructure/API/GraphQL/Loader/BufferedMatchLoader.php +++ b/src/Infrastructure/API/GraphQL/Loader/BufferedMatchLoader.php @@ -46,7 +46,7 @@ public function getByMatchDay(string $matchDayId): array if (count($matchDayIds)) { $filter = new EqualityFilter( - 'match_day_id', + $this->matchRepository->getField('match_day_id'), Filter::MODE_INCLUDE, $matchDayIds ); diff --git a/src/Infrastructure/API/GraphQL/Loader/BufferedPitchLoader.php b/src/Infrastructure/API/GraphQL/Loader/BufferedPitchLoader.php index ba7c4d00..5c9339ef 100644 --- a/src/Infrastructure/API/GraphQL/Loader/BufferedPitchLoader.php +++ b/src/Infrastructure/API/GraphQL/Loader/BufferedPitchLoader.php @@ -38,7 +38,7 @@ public function getByPitch(string $pitchId): ?array if (count($pitchIds)) { $filter = new EqualityFilter( - 'id', + $this->pitchRepository->getField('id'), Filter::MODE_INCLUDE, $pitchIds ); diff --git a/src/Infrastructure/API/GraphQL/Loader/BufferedTeamLoader.php b/src/Infrastructure/API/GraphQL/Loader/BufferedTeamLoader.php index 7f0179be..dbf32a77 100644 --- a/src/Infrastructure/API/GraphQL/Loader/BufferedTeamLoader.php +++ b/src/Infrastructure/API/GraphQL/Loader/BufferedTeamLoader.php @@ -70,7 +70,7 @@ public function getByTeam(string $teamId): ?array if (count($teamIds)) { $filter = new EqualityFilter( - 'id', + $this->teamRepository->getField('id'), Filter::MODE_INCLUDE, $teamIds ); diff --git a/src/Infrastructure/API/GraphQL/MatchType.php b/src/Infrastructure/API/GraphQL/MatchType.php index 1c8843f5..5630cc6e 100644 --- a/src/Infrastructure/API/GraphQL/MatchType.php +++ b/src/Infrastructure/API/GraphQL/MatchType.php @@ -114,7 +114,7 @@ public function getQueries(): array if (isset($args['min_date']) || isset($args['max_date'])) { $filters[] = new RangeFilter( - 'kickoff', + $repo->getField('kickoff'), Filter::MODE_INCLUDE, $args['min_date'] ?? null, $args['max_date'] ?? null @@ -123,7 +123,7 @@ public function getQueries(): array return $repo->findMany( $filters, - [new Sorting('kickoff', Sorting::DIRECTION_ASCENDING)] + [new Sorting($repo->getField('kickoff'), Sorting::DIRECTION_ASCENDING)] ); } ] diff --git a/src/Infrastructure/API/GraphQL/TeamType.php b/src/Infrastructure/API/GraphQL/TeamType.php index 2c0acede..025541df 100644 --- a/src/Infrastructure/API/GraphQL/TeamType.php +++ b/src/Infrastructure/API/GraphQL/TeamType.php @@ -76,7 +76,7 @@ public function getQueries(): array $repo = $context->getContainer()->get(TeamRepository::class); $filter = new PatternFilter( - 'name', + $repo->getField('name'), Filter::MODE_INCLUDE, $args['pattern'] ); diff --git a/src/Infrastructure/Persistence/Read/AbstractRepository.php b/src/Infrastructure/Persistence/Read/AbstractRepository.php index a248f186..76445dce 100644 --- a/src/Infrastructure/Persistence/Read/AbstractRepository.php +++ b/src/Infrastructure/Persistence/Read/AbstractRepository.php @@ -9,6 +9,7 @@ use HexagonalPlayground\Infrastructure\Persistence\Read\Criteria\Sorting; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\EmbeddedObjectField; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; +use RuntimeException; abstract class AbstractRepository { @@ -61,7 +62,7 @@ public function findById(string $id): ?array $this->getTableName(), $this->flattenedFieldDefinitions, [], - [new EqualityFilter('id', Filter::MODE_INCLUDE, [$id])] + [new EqualityFilter($this->getField('id'), Filter::MODE_INCLUDE, [$id])] )); } @@ -93,4 +94,15 @@ abstract protected function getFieldDefinitions(): array; * @return string */ abstract protected function getTableName(): string; + + public function getField(string $name): Field + { + foreach ($this->getFieldDefinitions() as $field) { + if ($field->getName() === $name) { + return $field; + } + } + + throw new RuntimeException(sprintf('Unknown field "%s" for table "%s"', $name, $this->getTableName())); + } } diff --git a/src/Infrastructure/Persistence/Read/Criteria/EqualityFilter.php b/src/Infrastructure/Persistence/Read/Criteria/EqualityFilter.php index 3cf40fac..53b56340 100644 --- a/src/Infrastructure/Persistence/Read/Criteria/EqualityFilter.php +++ b/src/Infrastructure/Persistence/Read/Criteria/EqualityFilter.php @@ -5,9 +5,7 @@ use DateTimeInterface; use HexagonalPlayground\Domain\Exception\InvalidInputException; -use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\DateTimeField; -use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\IntegerField; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\StringField; @@ -16,7 +14,7 @@ class EqualityFilter extends Filter /** @var array|int[]|string[]|DateTimeInterface[] */ private array $values; - public function __construct(string $field, string $mode, array $values) + public function __construct(IntegerField|StringField|DateTimeField $field, string $mode, array $values) { if (count($values) === 0) { throw new InvalidInputException('Invalid EqualityFilter: Array of values must not be empty'); @@ -25,6 +23,10 @@ public function __construct(string $field, string $mode, array $values) $this->field = $field; $this->mode = $mode; $this->values = $values; + + foreach ($this->values as $value) { + $this->field->validate($value); + } } /** @@ -34,34 +36,4 @@ public function getValues(): array { return $this->values; } - - public function validate(Field $fieldDefinition): void - { - $inputName = 'EqualityFilter.' . $fieldDefinition->getName(); - - // Validate Field type - switch (get_class($fieldDefinition)) { - case IntegerField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertInteger($value, $inputName); - }; - break; - case StringField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertString($value, $inputName); - }; - break; - case DateTimeField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertInstanceOf($value, DateTimeInterface::class, $inputName); - }; - break; - default: - throw new InvalidInputException('Invalid EqualityFilter: Unsupported field type'); - } - - foreach ($this->values as $value) { - $validator($value); - } - } } diff --git a/src/Infrastructure/Persistence/Read/Criteria/Filter.php b/src/Infrastructure/Persistence/Read/Criteria/Filter.php index 503eb844..99c7d1a6 100644 --- a/src/Infrastructure/Persistence/Read/Criteria/Filter.php +++ b/src/Infrastructure/Persistence/Read/Criteria/Filter.php @@ -10,27 +10,17 @@ abstract class Filter public const MODE_INCLUDE = 'include'; public const MODE_EXCLUDE = 'exclude'; - /** @var string */ - protected string $field; + protected Field $field; - /** @var string */ protected string $mode; - /** - * @return string - */ - public function getField(): string + public function getField(): Field { return $this->field; } - /** - * @return string - */ public function getMode(): string { return $this->mode; } - - abstract public function validate(Field $fieldDefinition): void; } diff --git a/src/Infrastructure/Persistence/Read/Criteria/PatternFilter.php b/src/Infrastructure/Persistence/Read/Criteria/PatternFilter.php index 908f8450..fc14e1de 100644 --- a/src/Infrastructure/Persistence/Read/Criteria/PatternFilter.php +++ b/src/Infrastructure/Persistence/Read/Criteria/PatternFilter.php @@ -3,34 +3,21 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Criteria; -use HexagonalPlayground\Domain\Exception\InvalidInputException; -use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\StringField; class PatternFilter extends Filter { - /** @var string */ private string $pattern; - public function __construct(string $field, string $mode, string $pattern) + public function __construct(StringField $field, string $mode, string $pattern) { $this->field = $field; $this->mode = $mode; $this->pattern = $pattern; } - /** - * @return string - */ public function getPattern(): string { return $this->pattern; } - - public function validate(Field $fieldDefinition): void - { - if (!($fieldDefinition instanceof StringField)) { - throw new InvalidInputException('Invalid PatternFilter: Can only be used on string fields'); - } - } } diff --git a/src/Infrastructure/Persistence/Read/Criteria/RangeFilter.php b/src/Infrastructure/Persistence/Read/Criteria/RangeFilter.php index 30320683..ee71d476 100644 --- a/src/Infrastructure/Persistence/Read/Criteria/RangeFilter.php +++ b/src/Infrastructure/Persistence/Read/Criteria/RangeFilter.php @@ -5,21 +5,17 @@ use DateTimeInterface; use HexagonalPlayground\Domain\Exception\InvalidInputException; -use HexagonalPlayground\Application\TypeAssert; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\DateTimeField; -use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\FloatField; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\IntegerField; class RangeFilter extends Filter { - /** @var null|int|float|DateTimeInterface */ - private $minValue; + private null|int|float|DateTimeInterface $minValue; - /** @var null|int|float|DateTimeInterface */ - private $maxValue; + private null|int|float|DateTimeInterface $maxValue; - public function __construct(string $field, string $mode, $minValue, $maxValue) + public function __construct(IntegerField|FloatField|DateTimeField $field, string $mode, null|int|float|DateTimeInterface $minValue, null|int|float|DateTimeInterface $maxValue) { if ($minValue === null && $maxValue === null) { throw new InvalidInputException('Invalid RangeFilter: Neither minValue nor maxValue given'); @@ -29,54 +25,23 @@ public function __construct(string $field, string $mode, $minValue, $maxValue) $this->mode = $mode; $this->minValue = $minValue; $this->maxValue = $maxValue; + + if ($this->minValue !== null) { + $this->field->validate($this->minValue); + } + + if ($this->maxValue !== null) { + $this->field->validate($this->maxValue); + } } - /** - * @return null|int|float|DateTimeInterface - */ - public function getMinValue() + public function getMinValue(): null|int|float|DateTimeInterface { return $this->minValue; } - /** - * @return null|int|float|DateTimeInterface - */ - public function getMaxValue() + public function getMaxValue(): null|int|float|DateTimeInterface { return $this->maxValue; } - - public function validate(Field $fieldDefinition): void - { - $inputName = 'RangeFilter.' . $fieldDefinition->getName(); - - switch (get_class($fieldDefinition)) { - case IntegerField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertInteger($value, $inputName); - }; - break; - case FloatField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertNumber($value, $inputName); - }; - break; - case DateTimeField::class: - $validator = function ($value) use ($inputName): void { - TypeAssert::assertInstanceOf($value, DateTimeInterface::class, $inputName); - }; - break; - default: - throw new InvalidInputException('Invalid RangeFilter: Unsupported field type'); - } - - if ($this->minValue !== null) { - $validator($this->minValue); - } - - if ($this->maxValue !== null) { - $validator($this->maxValue); - } - } } diff --git a/src/Infrastructure/Persistence/Read/Criteria/Sorting.php b/src/Infrastructure/Persistence/Read/Criteria/Sorting.php index 7f13c81f..a65e1371 100644 --- a/src/Infrastructure/Persistence/Read/Criteria/Sorting.php +++ b/src/Infrastructure/Persistence/Read/Criteria/Sorting.php @@ -3,34 +3,28 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Criteria; +use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; + class Sorting { public const DIRECTION_ASCENDING = 'ASC'; public const DIRECTION_DESCENDING = 'DESC'; - /** @var string */ - private string $field; + private Field $field; - /** @var string */ private string $direction; - public function __construct(string $field, string $direction) + public function __construct(Field $field, string $direction) { $this->field = $field; $this->direction = $direction; } - /** - * @return string - */ - public function getField(): string + public function getField(): Field { return $this->field; } - /** - * @return string - */ public function getDirection(): string { return $this->direction; diff --git a/src/Infrastructure/Persistence/Read/DbalGateway.php b/src/Infrastructure/Persistence/Read/DbalGateway.php index 40913c8e..1a393cde 100644 --- a/src/Infrastructure/Persistence/Read/DbalGateway.php +++ b/src/Infrastructure/Persistence/Read/DbalGateway.php @@ -71,24 +71,11 @@ public function fetch( $this->parameterInc = 0; foreach ($filters as $filter) { - $field = $fields[$filter->getField()] ?? null; - - if ($field === null) { - throw new InvalidInputException('Invalid Filter: Unknown field'); - } - - $filter->validate($field); - $this->applyFilter($query, $filter, $field); + $this->applyFilter($query, $filter); } foreach ($sortings as $sorting) { - $field = $fields[$sorting->getField()] ?? null; - - if ($field === null) { - throw new InvalidInputException('Invalid Sorting: Unknown field'); - } - - $this->applySorting($query, $sorting, $field); + $this->applySorting($query, $sorting); } if ($pagination !== null) { @@ -103,20 +90,19 @@ public function fetch( /** * @param QueryBuilder $query * @param Filter $filter - * @param Field $field * @throws InvalidInputException */ - private function applyFilter(QueryBuilder $query, Filter $filter, Field $field): void + private function applyFilter(QueryBuilder $query, Filter $filter): void { switch (true) { case $filter instanceof RangeFilter: - $this->applyRangeFilter($query, $filter, $field); + $this->applyRangeFilter($query, $filter, $filter->getField()); break; case $filter instanceof EqualityFilter: - $this->applyEqualityFilter($query, $filter, $field); + $this->applyEqualityFilter($query, $filter, $filter->getField()); break; case $filter instanceof PatternFilter: - $this->applyPatternFilter($query, $filter, $field); + $this->applyPatternFilter($query, $filter, $filter->getField()); break; default: throw new InvalidInputException('Unsupported filter type'); @@ -203,11 +189,10 @@ private function applyPatternFilter(QueryBuilder $query, PatternFilter $filter, /** * @param QueryBuilder $query * @param Sorting $sorting - * @param Field $field */ - private function applySorting(QueryBuilder $query, Sorting $sorting, Field $field): void + private function applySorting(QueryBuilder $query, Sorting $sorting): void { - $query->addOrderBy($field->getName(), $sorting->getDirection()); + $query->addOrderBy($sorting->getField()->getName(), $sorting->getDirection()); } /** diff --git a/src/Infrastructure/Persistence/Read/Field/DateField.php b/src/Infrastructure/Persistence/Read/Field/DateField.php index b24071ab..e6564b90 100644 --- a/src/Infrastructure/Persistence/Read/Field/DateField.php +++ b/src/Infrastructure/Persistence/Read/Field/DateField.php @@ -3,7 +3,9 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Field; +use HexagonalPlayground\Application\TypeAssert; use DateTimeImmutable; +use DateTimeInterface; class DateField extends Field { @@ -17,4 +19,9 @@ public function hydrate(array $row): ?string return (new DateTimeImmutable($value))->format('Y-m-d'); } + + public function validate(mixed $value): void + { + TypeAssert::assertInstanceOf($value, DateTimeInterface::class, $this->getName()); + } } diff --git a/src/Infrastructure/Persistence/Read/Field/DateTimeField.php b/src/Infrastructure/Persistence/Read/Field/DateTimeField.php index 741bde5f..14986f69 100644 --- a/src/Infrastructure/Persistence/Read/Field/DateTimeField.php +++ b/src/Infrastructure/Persistence/Read/Field/DateTimeField.php @@ -3,7 +3,9 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Field; +use HexagonalPlayground\Application\TypeAssert; use DateTimeImmutable; +use DateTimeInterface; use DateTimeZone; class DateTimeField extends Field @@ -33,4 +35,9 @@ public function hydrate(array $row): ?string // Adjust timezone identifier for not breaking tests return str_replace('+00:00', 'Z', $string); } + + public function validate(mixed $value): void + { + TypeAssert::assertInstanceOf($value, DateTimeInterface::class, $this->getName()); + } } diff --git a/src/Infrastructure/Persistence/Read/Field/EmbeddedObjectField.php b/src/Infrastructure/Persistence/Read/Field/EmbeddedObjectField.php index d314134d..5b8fa393 100644 --- a/src/Infrastructure/Persistence/Read/Field/EmbeddedObjectField.php +++ b/src/Infrastructure/Persistence/Read/Field/EmbeddedObjectField.php @@ -39,4 +39,9 @@ public function hydrate(array $row): ?array return count($result) ? $result : null; } + + public function validate(mixed $value): void + { + // TODO: Implement + } } diff --git a/src/Infrastructure/Persistence/Read/Field/Field.php b/src/Infrastructure/Persistence/Read/Field/Field.php index 170f9fa0..e7ff8877 100644 --- a/src/Infrastructure/Persistence/Read/Field/Field.php +++ b/src/Infrastructure/Persistence/Read/Field/Field.php @@ -36,4 +36,6 @@ public function withName(string $name): Field return $clone; } + + abstract public function validate(mixed $value): void; } diff --git a/src/Infrastructure/Persistence/Read/Field/FloatField.php b/src/Infrastructure/Persistence/Read/Field/FloatField.php index f8ec898e..e1810dfa 100644 --- a/src/Infrastructure/Persistence/Read/Field/FloatField.php +++ b/src/Infrastructure/Persistence/Read/Field/FloatField.php @@ -3,6 +3,8 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Field; +use HexagonalPlayground\Application\TypeAssert; + class FloatField extends Field { public function hydrate(array $row): ?float @@ -11,4 +13,9 @@ public function hydrate(array $row): ?float return $value !== null ? (float)$value : null; } + + public function validate(mixed $value): void + { + TypeAssert::assertNumber($value, $this->getName()); + } } diff --git a/src/Infrastructure/Persistence/Read/Field/IntegerField.php b/src/Infrastructure/Persistence/Read/Field/IntegerField.php index 5a76fdb9..2b0fa598 100644 --- a/src/Infrastructure/Persistence/Read/Field/IntegerField.php +++ b/src/Infrastructure/Persistence/Read/Field/IntegerField.php @@ -3,6 +3,8 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Field; +use HexagonalPlayground\Application\TypeAssert; + class IntegerField extends Field { public function hydrate(array $row): ?int @@ -11,4 +13,9 @@ public function hydrate(array $row): ?int return $value !== null ? (int)$value : null; } + + public function validate(mixed $value): void + { + TypeAssert::assertInteger($value, $this->getName()); + } } diff --git a/src/Infrastructure/Persistence/Read/Field/SerializedArrayField.php b/src/Infrastructure/Persistence/Read/Field/SerializedArrayField.php index b7af63d5..dcbd2174 100644 --- a/src/Infrastructure/Persistence/Read/Field/SerializedArrayField.php +++ b/src/Infrastructure/Persistence/Read/Field/SerializedArrayField.php @@ -9,4 +9,9 @@ public function hydrate(array $row): array { return json_decode($row[$this->getName()], true); } + + public function validate(mixed $value): void + { + // TODO: Implement + } } diff --git a/src/Infrastructure/Persistence/Read/Field/StringField.php b/src/Infrastructure/Persistence/Read/Field/StringField.php index dea66cf9..cb8d7d1f 100644 --- a/src/Infrastructure/Persistence/Read/Field/StringField.php +++ b/src/Infrastructure/Persistence/Read/Field/StringField.php @@ -3,6 +3,8 @@ namespace HexagonalPlayground\Infrastructure\Persistence\Read\Field; +use HexagonalPlayground\Application\TypeAssert; + class StringField extends Field { public function hydrate(array $row): ?string @@ -11,4 +13,9 @@ public function hydrate(array $row): ?string return $value !== null ? (string)$value : null; } + + public function validate(mixed $value): void + { + TypeAssert::assertString($value, $this->getName()); + } } diff --git a/src/Infrastructure/Persistence/Read/RankingRepository.php b/src/Infrastructure/Persistence/Read/RankingRepository.php index 9d143331..6c358f7a 100644 --- a/src/Infrastructure/Persistence/Read/RankingRepository.php +++ b/src/Infrastructure/Persistence/Read/RankingRepository.php @@ -10,6 +10,7 @@ use HexagonalPlayground\Infrastructure\Persistence\Read\Field\Field; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\IntegerField; use HexagonalPlayground\Infrastructure\Persistence\Read\Field\StringField; +use RuntimeException; class RankingRepository extends AbstractRepository { @@ -90,7 +91,7 @@ public function findRanking(string $seasonId): ?array $this->getTableName(), $this->flattenedFieldDefinitions, [], - [new EqualityFilter('season_id', Filter::MODE_INCLUDE, [$seasonId])] + [new EqualityFilter($this->getField('season_id'), Filter::MODE_INCLUDE, [$seasonId])] ); return $this->hydrator->hydrateOne($result); @@ -106,8 +107,8 @@ public function findRankingPositions(string $seasonId): array 'ranking_positions', $this->flattenedPositionFields, [], - [new EqualityFilter('season_id', Filter::MODE_INCLUDE, [$seasonId])], - [new Sorting('sort_index', Sorting::DIRECTION_ASCENDING)] + [new EqualityFilter($this->getPositionField('season_id'), Filter::MODE_INCLUDE, [$seasonId])], + [new Sorting($this->getPositionField('sort_index'), Sorting::DIRECTION_ASCENDING)] ); return $this->positionHydrator->hydrateMany($result); @@ -123,10 +124,32 @@ public function findRankingPenalties(string $seasonId): array 'ranking_penalties', $this->flattenedPenaltyFields, [], - [new EqualityFilter('season_id', Filter::MODE_INCLUDE, [$seasonId])], - [new Sorting('created_at', Sorting::DIRECTION_ASCENDING)] + [new EqualityFilter($this->getPenaltyField('season_id'), Filter::MODE_INCLUDE, [$seasonId])], + [new Sorting($this->getPenaltyField('created_at'), Sorting::DIRECTION_ASCENDING)] ); return $this->penaltyHydrator->hydrateMany($result); } + + private function getPenaltyField(string $name): Field + { + foreach ($this->penaltyFields as $field) { + if ($field->getName() === $name) { + return $field; + } + } + + throw new RuntimeException(sprintf('Unknown field "%s" for table "ranking_penalties"', $name)); + } + + private function getPositionField(string $name): Field + { + foreach ($this->positionFields as $field) { + if ($field->getName() === $name) { + return $field; + } + } + + throw new RuntimeException(sprintf('Unknown field "%s" for table "ranking_positions"', $name)); + } } diff --git a/src/Infrastructure/Persistence/Read/TeamRepository.php b/src/Infrastructure/Persistence/Read/TeamRepository.php index 0be67c28..fbb94c0b 100644 --- a/src/Infrastructure/Persistence/Read/TeamRepository.php +++ b/src/Infrastructure/Persistence/Read/TeamRepository.php @@ -53,7 +53,7 @@ public function findBySeasonIds(array $seasonIds): array $fields, ['seasons_teams_link' => 'id = team_id'], [new EqualityFilter( - 'season_id', + $fields['season_id'], Filter::MODE_INCLUDE, $seasonIds )] @@ -76,7 +76,7 @@ public function findByUserIds(array $userIds): array $fields, ['users_teams_link' => 'id = team_id'], [new EqualityFilter( - 'user_id', + $fields['user_id'], Filter::MODE_INCLUDE, $userIds )]