From 16882ee06fb3bff202f0403daaf905775515b747 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Tue, 11 Feb 2020 22:48:30 +0100 Subject: [PATCH 1/7] #35 - fluent repository prototype --- src/Event/HandleDatabaseModifierEvent.php | 34 +++++++++++ src/Event/HandleModifierEvent.php | 35 +++++++++++ src/Exception/InvalidOptionException.php | 13 ++++ .../Database/LimitDatabaseHandler.php | 27 +++++++++ .../Database/WithIdDatabaseHandler.php | 45 ++++++++++++++ src/Handlers/ModifierHandler.php | 10 ++++ src/Modifiers/Limit.php | 30 ++++++++++ src/Modifiers/Modifier.php | 8 +++ src/Modifiers/WithId.php | 31 ++++++++++ .../Database/GenericLineRepository.php | 60 +++++++++++++++---- src/Provider/FluentRepository.php | 11 ++++ src/Provider/LineRepository.php | 4 +- src/Provider/Repository.php | 3 +- 13 files changed, 294 insertions(+), 17 deletions(-) create mode 100644 src/Event/HandleDatabaseModifierEvent.php create mode 100644 src/Event/HandleModifierEvent.php create mode 100644 src/Exception/InvalidOptionException.php create mode 100644 src/Handlers/Database/LimitDatabaseHandler.php create mode 100644 src/Handlers/Database/WithIdDatabaseHandler.php create mode 100644 src/Handlers/ModifierHandler.php create mode 100644 src/Modifiers/Limit.php create mode 100644 src/Modifiers/Modifier.php create mode 100644 src/Modifiers/WithId.php create mode 100644 src/Provider/FluentRepository.php diff --git a/src/Event/HandleDatabaseModifierEvent.php b/src/Event/HandleDatabaseModifierEvent.php new file mode 100644 index 0000000..74bbc81 --- /dev/null +++ b/src/Event/HandleDatabaseModifierEvent.php @@ -0,0 +1,34 @@ +builder = $builder; + } + + public function getBuilder(): QueryBuilder + { + return $this->builder; + } + + public function replaceBuilder(QueryBuilder $builder): void + { + $this->builder = $builder; + } +} diff --git a/src/Event/HandleModifierEvent.php b/src/Event/HandleModifierEvent.php new file mode 100644 index 0000000..af28988 --- /dev/null +++ b/src/Event/HandleModifierEvent.php @@ -0,0 +1,35 @@ +repository = $repository; + $this->modifier = $modifier; + $this->meta = $meta; + } + + public function getModifier(): Modifier + { + return $this->modifier; + } + + public function getRepository() + { + return $this->repository; + } + + public function getMeta(): array + { + return $this->meta; + } +} diff --git a/src/Exception/InvalidOptionException.php b/src/Exception/InvalidOptionException.php new file mode 100644 index 0000000..b052f10 --- /dev/null +++ b/src/Exception/InvalidOptionException.php @@ -0,0 +1,13 @@ +getModifier(); + $builder = $event->getBuilder(); + + $builder + ->setFirstResult($modifier->getOffset()) + ->setMaxResults($modifier->getCount()) + ; + } +} diff --git a/src/Handlers/Database/WithIdDatabaseHandler.php b/src/Handlers/Database/WithIdDatabaseHandler.php new file mode 100644 index 0000000..1cbb324 --- /dev/null +++ b/src/Handlers/Database/WithIdDatabaseHandler.php @@ -0,0 +1,45 @@ +id = $id; + } + + public function process(HandleModifierEvent $event) + { + if (!$event instanceof HandleDatabaseModifierEvent) { + return; + } + + /** @var WithId $modifier */ + $modifier = $event->getModifier(); + $builder = $event->getBuilder(); + $alias = $event->getMeta()['alias']; + $provider = $event->getMeta()['provider']; + + $id = $modifier->getId(); + $mapper = apply([$this->id, 'generate'], $provider); + + $builder + ->where($modifier->isMultiple() ? "{$alias} in (:id)" : "{$alias} = :id") + ->setParameter(':id', $modifier->isMultiple() ? array_map($mapper, $id) : $mapper($id)); + ; + } +} diff --git a/src/Handlers/ModifierHandler.php b/src/Handlers/ModifierHandler.php new file mode 100644 index 0000000..b4e015e --- /dev/null +++ b/src/Handlers/ModifierHandler.php @@ -0,0 +1,10 @@ +offset = $offset; + $this->count = $count; + } + + public function getOffset() + { + return $this->offset; + } + + public function getCount() + { + return $this->count; + } + + public static function count(int $count) + { + return new static(0, $count); + } +} diff --git a/src/Modifiers/Modifier.php b/src/Modifiers/Modifier.php new file mode 100644 index 0000000..a8ddd7c --- /dev/null +++ b/src/Modifiers/Modifier.php @@ -0,0 +1,8 @@ +id = $id instanceof \Traversable ? iterator_to_array($id) : $id; + } + + public function getId() + { + return $this->id; + } + + public function isMultiple() + { + return is_array($this->id); + } +} diff --git a/src/Provider/Database/GenericLineRepository.php b/src/Provider/Database/GenericLineRepository.php index e9bab49..66d3cc0 100644 --- a/src/Provider/Database/GenericLineRepository.php +++ b/src/Provider/Database/GenericLineRepository.php @@ -3,8 +3,15 @@ namespace App\Provider\Database; use App\Entity\LineEntity; +use App\Event\HandleDatabaseModifierEvent; +use App\Handlers\Database\LimitDatabaseHandler; +use App\Handlers\Database\WithIdDatabaseHandler; +use App\Handlers\ModifierHandler; use App\Model\Line; +use App\Modifiers\Limit; +use App\Modifiers\WithId; use App\Provider\LineRepository; +use App\Modifiers\Modifier; use Tightenco\Collect\Support\Collection; use Kadet\Functional as f; @@ -12,25 +19,52 @@ class GenericLineRepository extends DatabaseRepository implements LineRepository { public function getAll(): Collection { - $repository = $this->em->getRepository(LineEntity::class); - $lines = $repository->findAll(); - - return collect($lines)->map(f\ref([$this, 'convert'])); + return $this->all(); } public function getById($id): ?Line { - $repository = $this->em->getRepository(LineEntity::class); - return $this->convert($repository->find($id)); + return $this->first(new WithId($id)); } public function getManyById($ids): Collection { - $ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider)); - - $repository = $this->em->getRepository(LineEntity::class); - $lines = $repository->findBy(['id' => $ids->all()]); - - return collect($lines)->map(f\ref([$this, 'convert'])); + return $this->all(new WithId($ids)); } -} \ No newline at end of file + + public function first(Modifier ...$modifiers) + { + return $this->all(Limit::count(1), ...$modifiers)->first(); + } + + public function all(Modifier ...$modifiers) + { + $builder = $this->em + ->createQueryBuilder() + ->from(LineEntity::class, 'line') + ->select('line') + ; + + foreach ($modifiers as $modifier) { + $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, [ + 'alias' => 'line', + 'provider' => $this->provider, + ]); + + $handler = $this->getHandlers()[get_class($modifier)]; + + $handler->process($event); + } + + return collect($builder->getQuery()->execute())->map(f\ref([$this, 'convert'])); + } + + /** @return ModifierHandler[] */ + private function getHandlers() + { + return [ + WithId::class => new WithIdDatabaseHandler($this->id), + Limit::class => new LimitDatabaseHandler(), + ]; + } +} diff --git a/src/Provider/FluentRepository.php b/src/Provider/FluentRepository.php new file mode 100644 index 0000000..e24545d --- /dev/null +++ b/src/Provider/FluentRepository.php @@ -0,0 +1,11 @@ + Date: Wed, 12 Feb 2020 20:08:56 +0100 Subject: [PATCH 2/7] #35 - Add service subscriber to database repository --- .../UnsupportedModifierException.php | 14 +++++ ...andler.php => IdFilterDatabaseHandler.php} | 7 +-- src/Modifiers/{WithId.php => IdFilter.php} | 2 +- src/Provider/Database/DatabaseRepository.php | 61 +++++++++++++++++-- .../Database/GenericLineRepository.php | 29 ++++----- .../Database/GenericOperatorRepository.php | 7 ++- .../Database/GenericScheduleRepository.php | 5 ++ .../Database/GenericStopRepository.php | 5 ++ .../Database/GenericTrackRepository.php | 7 ++- .../Database/GenericTripRepository.php | 5 ++ 10 files changed, 112 insertions(+), 30 deletions(-) create mode 100644 src/Exception/UnsupportedModifierException.php rename src/Handlers/Database/{WithIdDatabaseHandler.php => IdFilterDatabaseHandler.php} (86%) rename src/Modifiers/{WithId.php => IdFilter.php} (94%) diff --git a/src/Exception/UnsupportedModifierException.php b/src/Exception/UnsupportedModifierException.php new file mode 100644 index 0000000..4dbd2bf --- /dev/null +++ b/src/Exception/UnsupportedModifierException.php @@ -0,0 +1,14 @@ +getModifier(); $builder = $event->getBuilder(); $alias = $event->getMeta()['alias']; diff --git a/src/Modifiers/WithId.php b/src/Modifiers/IdFilter.php similarity index 94% rename from src/Modifiers/WithId.php rename to src/Modifiers/IdFilter.php index 883db0f..e2f570c 100644 --- a/src/Modifiers/WithId.php +++ b/src/Modifiers/IdFilter.php @@ -5,7 +5,7 @@ namespace App\Modifiers; use App\Exception\InvalidOptionException; use App\Modifiers\Modifier; -class WithId implements Modifier +class IdFilter implements Modifier { /** @var string|array */ private $id; diff --git a/src/Provider/Database/DatabaseRepository.php b/src/Provider/Database/DatabaseRepository.php index 3a97eff..4f4dce3 100644 --- a/src/Provider/Database/DatabaseRepository.php +++ b/src/Provider/Database/DatabaseRepository.php @@ -2,15 +2,19 @@ namespace App\Provider\Database; -use App\Entity\Entity; use App\Entity\ProviderEntity; +use App\Event\HandleDatabaseModifierEvent; +use App\Exception\UnsupportedModifierException; use App\Model\Referable; +use App\Provider\Repository; use App\Service\Converter; use App\Service\IdUtils; use Doctrine\ORM\EntityManagerInterface; -use Kadet\Functional as f; +use Doctrine\ORM\QueryBuilder; +use Psr\Container\ContainerInterface; +use Symfony\Contracts\Service\ServiceSubscriberInterface; -class DatabaseRepository +abstract class DatabaseRepository implements ServiceSubscriberInterface, Repository { /** @var EntityManagerInterface */ protected $em; @@ -24,22 +28,30 @@ class DatabaseRepository /** @var Converter */ protected $converter; + /** @var ContainerInterface */ + protected $handlers; + /** * DatabaseRepository constructor. * * @param EntityManagerInterface $em */ - public function __construct(EntityManagerInterface $em, IdUtils $id, Converter $converter) - { + public function __construct( + EntityManagerInterface $em, + IdUtils $id, + Converter $converter, + ContainerInterface $handlers + ) { $this->em = $em; $this->id = $id; $this->converter = $converter; + $this->handlers = $handlers; } /** @return static */ public function withProvider(ProviderEntity $provider) { - $result = clone $this; + $result = clone $this; $result->provider = $provider; return $result; @@ -56,4 +68,41 @@ class DatabaseRepository return $this->em->getReference($class, $id); } + + protected function processQueryBuilder(QueryBuilder $builder, iterable $modifiers, array $meta = []) + { + foreach ($modifiers as $modifier) { + $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([ + 'provider' => $this->provider, + ], $meta)); + + $class = get_class($modifier); + + if (!$this->handlers->has($class)) { + throw UnsupportedModifierException::createFromModifier($modifier, $this); + } + + $handler = $this->handlers->get($class); + + $handler->process($event); + } + } + + /** + * Returns array describing handlers for each modifier type. Syntax is as follows: + * [ IdFilter::class => IdFilterDatabaseHandler::class ] + * + * It is internally used as part of service subscriber. + * + * @return array + */ + protected abstract static function getHandlers(); + + /** + * @inheritDoc + */ + public static function getSubscribedServices() + { + return static::getHandlers(); + } } diff --git a/src/Provider/Database/GenericLineRepository.php b/src/Provider/Database/GenericLineRepository.php index 66d3cc0..6982cb1 100644 --- a/src/Provider/Database/GenericLineRepository.php +++ b/src/Provider/Database/GenericLineRepository.php @@ -5,11 +5,11 @@ namespace App\Provider\Database; use App\Entity\LineEntity; use App\Event\HandleDatabaseModifierEvent; use App\Handlers\Database\LimitDatabaseHandler; -use App\Handlers\Database\WithIdDatabaseHandler; +use App\Handlers\Database\IdFilterDatabaseHandler; use App\Handlers\ModifierHandler; use App\Model\Line; use App\Modifiers\Limit; -use App\Modifiers\WithId; +use App\Modifiers\IdFilter; use App\Provider\LineRepository; use App\Modifiers\Modifier; use Tightenco\Collect\Support\Collection; @@ -24,12 +24,12 @@ class GenericLineRepository extends DatabaseRepository implements LineRepository public function getById($id): ?Line { - return $this->first(new WithId($id)); + return $this->first(new IdFilter($id)); } public function getManyById($ids): Collection { - return $this->all(new WithId($ids)); + return $this->all(new IdFilter($ids)); } public function first(Modifier ...$modifiers) @@ -45,26 +45,21 @@ class GenericLineRepository extends DatabaseRepository implements LineRepository ->select('line') ; - foreach ($modifiers as $modifier) { - $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, [ - 'alias' => 'line', - 'provider' => $this->provider, - ]); - - $handler = $this->getHandlers()[get_class($modifier)]; - - $handler->process($event); - } + $this->processQueryBuilder($builder, $modifiers, [ + 'alias' => 'line', + 'entity' => LineEntity::class, + 'type' => Line::class, + ]); return collect($builder->getQuery()->execute())->map(f\ref([$this, 'convert'])); } /** @return ModifierHandler[] */ - private function getHandlers() + protected static function getHandlers() { return [ - WithId::class => new WithIdDatabaseHandler($this->id), - Limit::class => new LimitDatabaseHandler(), + IdFilter::class => IdFilterDatabaseHandler::class, + Limit::class => LimitDatabaseHandler::class, ]; } } diff --git a/src/Provider/Database/GenericOperatorRepository.php b/src/Provider/Database/GenericOperatorRepository.php index 946eff6..929d994 100644 --- a/src/Provider/Database/GenericOperatorRepository.php +++ b/src/Provider/Database/GenericOperatorRepository.php @@ -30,4 +30,9 @@ class GenericOperatorRepository extends DatabaseRepository implements OperatorRe return collect($operators); } -} \ No newline at end of file + + protected static function getHandlers() + { + return []; + } +} diff --git a/src/Provider/Database/GenericScheduleRepository.php b/src/Provider/Database/GenericScheduleRepository.php index 551c288..e1c2c29 100644 --- a/src/Provider/Database/GenericScheduleRepository.php +++ b/src/Provider/Database/GenericScheduleRepository.php @@ -70,4 +70,9 @@ class GenericScheduleRepository extends DatabaseRepository implements ScheduleRe ]); }); } + + protected static function getHandlers() + { + return []; + } } diff --git a/src/Provider/Database/GenericStopRepository.php b/src/Provider/Database/GenericStopRepository.php index 8dc1972..fe7b017 100644 --- a/src/Provider/Database/GenericStopRepository.php +++ b/src/Provider/Database/GenericStopRepository.php @@ -83,4 +83,9 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository $stop->setDestinations($destinations[$this->id->generate($this->provider, $stop->getId())]); }); } + + protected static function getHandlers() + { + return []; + } } diff --git a/src/Provider/Database/GenericTrackRepository.php b/src/Provider/Database/GenericTrackRepository.php index 9a25d44..53bd235 100644 --- a/src/Provider/Database/GenericTrackRepository.php +++ b/src/Provider/Database/GenericTrackRepository.php @@ -64,4 +64,9 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito return collect($tracks)->map(f\ref([$this, 'convert'])); } -} \ No newline at end of file + + protected static function getHandlers() + { + return []; + } +} diff --git a/src/Provider/Database/GenericTripRepository.php b/src/Provider/Database/GenericTripRepository.php index e85f178..3944e18 100644 --- a/src/Provider/Database/GenericTripRepository.php +++ b/src/Provider/Database/GenericTripRepository.php @@ -25,4 +25,9 @@ class GenericTripRepository extends DatabaseRepository implements TripRepository return $this->convert($trip); } + + protected static function getHandlers() + { + return []; + } } -- 2.45.2 From fd4bcc9c7045b3ce76a2d0e7518bfb3864611141 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Wed, 12 Feb 2020 20:16:26 +0100 Subject: [PATCH 3/7] #35 - Remove unnecessary methods from LineRepository --- src/Provider/Database/DatabaseRepository.php | 5 ++++- src/Provider/Database/GenericLineRepository.php | 17 +---------------- src/Provider/FluentRepository.php | 3 ++- src/Provider/LineRepository.php | 8 -------- .../ZtmGdansk/ZtmGdanskDepartureRepository.php | 4 +++- 5 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/Provider/Database/DatabaseRepository.php b/src/Provider/Database/DatabaseRepository.php index 4f4dce3..bcc596b 100644 --- a/src/Provider/Database/DatabaseRepository.php +++ b/src/Provider/Database/DatabaseRepository.php @@ -96,7 +96,10 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit * * @return array */ - protected abstract static function getHandlers(); + protected static function getHandlers() + { + return []; + } /** * @inheritDoc diff --git a/src/Provider/Database/GenericLineRepository.php b/src/Provider/Database/GenericLineRepository.php index 6982cb1..244b8e0 100644 --- a/src/Provider/Database/GenericLineRepository.php +++ b/src/Provider/Database/GenericLineRepository.php @@ -17,27 +17,12 @@ use Kadet\Functional as f; class GenericLineRepository extends DatabaseRepository implements LineRepository { - public function getAll(): Collection - { - return $this->all(); - } - - public function getById($id): ?Line - { - return $this->first(new IdFilter($id)); - } - - public function getManyById($ids): Collection - { - return $this->all(new IdFilter($ids)); - } - public function first(Modifier ...$modifiers) { return $this->all(Limit::count(1), ...$modifiers)->first(); } - public function all(Modifier ...$modifiers) + public function all(Modifier ...$modifiers): Collection { $builder = $this->em ->createQueryBuilder() diff --git a/src/Provider/FluentRepository.php b/src/Provider/FluentRepository.php index e24545d..d0644fe 100644 --- a/src/Provider/FluentRepository.php +++ b/src/Provider/FluentRepository.php @@ -3,9 +3,10 @@ namespace App\Provider; use App\Modifiers\Modifier; +use Tightenco\Collect\Support\Collection; interface FluentRepository extends Repository { public function first(Modifier ...$modifiers); - public function all(Modifier ...$modifiers); + public function all(Modifier ...$modifiers): Collection; } diff --git a/src/Provider/LineRepository.php b/src/Provider/LineRepository.php index 80bebbd..2ab6884 100644 --- a/src/Provider/LineRepository.php +++ b/src/Provider/LineRepository.php @@ -3,14 +3,6 @@ namespace App\Provider; - -use App\Model\Line; -use Tightenco\Collect\Support\Collection; - interface LineRepository extends FluentRepository { - public function getAll(): Collection; - - public function getById($id): ?Line; - public function getManyById($ids): Collection; } diff --git a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php index 0e956bd..165e571 100644 --- a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php +++ b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php @@ -6,6 +6,7 @@ use App\Model\Departure; use App\Model\Line; use App\Model\Stop; use App\Model\Vehicle; +use App\Modifiers\IdFilter; use App\Provider\Database\GenericScheduleRepository; use App\Provider\DepartureRepository; use App\Provider\LineRepository; @@ -65,7 +66,8 @@ class ZtmGdanskDepartureRepository implements DepartureRepository $lines = $estimates->map(function ($delay) { return $delay['routeId']; })->unique(); - $lines = $this->lines->getManyById($lines)->keyBy(t\property('id')); + + $lines = $this->lines->all(new IdFilter($lines))->keyBy(t\property('id')); return collect($estimates)->map(function ($delay) use ($stop, $lines) { $scheduled = (new Carbon($delay['theoreticalTime'], 'Europe/Warsaw'))->tz('UTC'); -- 2.45.2 From d72fcf777f220d9a19ca217d84afab20c7789f4e Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 16 Feb 2020 21:07:16 +0100 Subject: [PATCH 4/7] #35 - Make StopRepository more fluent --- config/services.yaml | 2 +- resources/ts/components/picker.ts | 2 +- resources/ts/urls.ts | 6 +- .../Api/v1/DeparturesController.php | 5 +- src/Controller/Api/v1/StopsController.php | 51 +++++++---- src/Event/HandleDatabaseModifierEvent.php | 2 +- src/Event/HandleModifierEvent.php | 2 +- src/Event/PostProcessEvent.php | 27 ++++++ src/Exception/NonExistentServiceException.php | 5 +- src/Exception/NotSupportedException.php | 4 +- .../UnsupportedModifierException.php | 4 +- .../Database/FieldFilterDatabaseHandler.php | 52 +++++++++++ .../Database/IdFilterDatabaseHandler.php | 6 +- .../IncludeDestinationsDatabaseHandler.php | 76 ++++++++++++++++ .../Database/LimitDatabaseHandler.php | 6 +- src/{Handlers => Handler}/ModifierHandler.php | 2 +- src/Handler/PostProcessingHandler.php | 11 +++ src/Modifier/FieldFilter.php | 37 ++++++++ src/{Modifiers => Modifier}/IdFilter.php | 4 +- src/Modifier/IncludeDestinations.php | 7 ++ src/{Modifiers => Modifier}/Limit.php | 2 +- src/{Modifiers => Modifier}/Modifier.php | 2 +- src/Provider/Database/DatabaseRepository.php | 74 ++++++++++++--- .../Database/GenericLineRepository.php | 30 ++----- .../Database/GenericStopRepository.php | 89 ++++--------------- src/Provider/Dummy/DummyStopRepository.php | 11 +++ src/Provider/FluentRepository.php | 2 +- src/Provider/StopRepository.php | 6 +- .../ZtmGdanskDepartureRepository.php | 2 +- src/Service/IdUtils.php | 3 +- 30 files changed, 374 insertions(+), 158 deletions(-) create mode 100644 src/Event/PostProcessEvent.php create mode 100644 src/Handler/Database/FieldFilterDatabaseHandler.php rename src/{Handlers => Handler}/Database/IdFilterDatabaseHandler.php (91%) create mode 100644 src/Handler/Database/IncludeDestinationsDatabaseHandler.php rename src/{Handlers => Handler}/Database/LimitDatabaseHandler.php (85%) rename src/{Handlers => Handler}/ModifierHandler.php (84%) create mode 100644 src/Handler/PostProcessingHandler.php create mode 100644 src/Modifier/FieldFilter.php rename src/{Modifiers => Modifier}/IdFilter.php (91%) create mode 100644 src/Modifier/IncludeDestinations.php rename src/{Modifiers => Modifier}/Limit.php (95%) rename src/{Modifiers => Modifier}/Modifier.php (56%) diff --git a/config/services.yaml b/config/services.yaml index 853ef0a..4337891 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -23,7 +23,7 @@ services: # this creates a service per class whose id is the fully-qualified class name App\: resource: '../src/*' - exclude: '../src/{DependencyInjection,Entity,Model,Migrations,Tests,Functions,Kernel.php}' + exclude: '../src/{DependencyInjection,Exception,Modifiers,Entity,Model,Migrations,Tests,Functions,Kernel.php}' # controllers are imported separately to make sure services can be injected # as action arguments even if you don't extend any base controller class diff --git a/resources/ts/components/picker.ts b/resources/ts/components/picker.ts index 476d07a..93493d1 100644 --- a/resources/ts/components/picker.ts +++ b/resources/ts/components/picker.ts @@ -80,7 +80,7 @@ export class FinderComponent extends Vue { this.state = 'fetching'; - const response = await fetch(urls.prepare(urls.stops.grouped, { name: this.filter })); + const response = await fetch(urls.prepare(urls.stops.grouped, { name: this.filter, 'include-destinations': true })); if (response.ok) { this.found = (await response.json()).reduce((accumulator, { name, stops }) => Object.assign(accumulator, { [name]: stops }), {}); diff --git a/resources/ts/urls.ts b/resources/ts/urls.ts index e3bb5d1..106a0a1 100644 --- a/resources/ts/urls.ts +++ b/resources/ts/urls.ts @@ -8,7 +8,11 @@ export function query(params: UrlParams = { }) { function *simplify(name: string, param: any): IterableIterator { if (typeof param === 'string') { yield [ name, param ]; - } else if (typeof param === 'number') { + } else if (typeof param === 'boolean') { + if (param) { + yield [ name, '1' ]; + } + } else if (typeof param === 'number') { yield [ name, param.toString() ]; } else if (param instanceof Array) { for (let entry of param) { diff --git a/src/Controller/Api/v1/DeparturesController.php b/src/Controller/Api/v1/DeparturesController.php index 410a753..da07d63 100644 --- a/src/Controller/Api/v1/DeparturesController.php +++ b/src/Controller/Api/v1/DeparturesController.php @@ -5,6 +5,7 @@ namespace App\Controller\Api\v1; use App\Controller\Controller; use App\Model\Departure; +use App\Modifier\IdFilter; use App\Provider\DepartureRepository; use App\Provider\StopRepository; use App\Service\SerializerContextFactory; @@ -34,7 +35,7 @@ class DeparturesController extends Controller */ public function stop(DepartureRepository $departures, StopRepository $stops, $stop) { - $stop = $stops->getById($stop); + $stop = $stops->first(new IdFilter($stop)); return $this->json($departures->getForStop($stop)); } @@ -65,7 +66,7 @@ class DeparturesController extends Controller public function stops(DepartureRepository $departures, StopRepository $stops, Request $request) { $stops = $stops - ->getManyById($request->query->get('stop')) + ->all(new IdFilter($request->query->get('stop'))) ->flatMap(ref([ $departures, 'getForStop' ])) ->sortBy(property('departure')); diff --git a/src/Controller/Api/v1/StopsController.php b/src/Controller/Api/v1/StopsController.php index 76c19a1..0fca3e4 100644 --- a/src/Controller/Api/v1/StopsController.php +++ b/src/Controller/Api/v1/StopsController.php @@ -7,6 +7,9 @@ use App\Controller\Controller; use App\Model\Stop; use App\Model\Track; use App\Model\StopGroup; +use App\Modifier\IdFilter; +use App\Modifier\FieldFilter; +use App\Modifier\IncludeDestinations; use App\Provider\StopRepository; use App\Provider\TrackRepository; use App\Service\Proxy\ReferenceFactory; @@ -46,16 +49,9 @@ class StopsController extends Controller */ public function index(Request $request, StopRepository $stops) { - switch (true) { - case $request->query->has('id'): - $result = $stops->getManyById($request->query->get('id')); - break; + $modifiers = $this->getModifiersFromRequest($request); - default: - $result = $stops->getAll(); - } - - return $this->json($result->all()); + return $this->json($stops->all(...$modifiers)->toArray()); } /** @@ -76,16 +72,9 @@ class StopsController extends Controller */ public function groups(Request $request, StopRepository $stops) { - switch (true) { - case $request->query->has('name'): - $result = $stops->findByName($request->query->get('name')); - break; + $modifiers = $this->getModifiersFromRequest($request); - default: - $result = $stops->getAll(); - } - - return $this->json(static::group($result)->all()); + return $this->json(static::group($stops->all(...$modifiers))->toArray()); } /** @@ -106,7 +95,7 @@ class StopsController extends Controller */ public function one(Request $request, StopRepository $stops, $id) { - return $this->json($stops->getById($id)); + return $this->json($stops->first(new IdFilter($id), new IncludeDestinations())); } /** @@ -145,4 +134,28 @@ class StopsController extends Controller return $group; })->values(); } + + /** + * @param Request $request + * + * @return array + */ + private function getModifiersFromRequest(Request $request): array + { + $modifiers = []; + + if ($request->query->has('name')) { + $modifiers[] = FieldFilter::contains('name', $request->query->get('name')); + } + + if ($request->query->has('id')) { + $modifiers[] = new IdFilter($request->query->get('id')); + } + + if ($request->query->has('include-destinations')) { + $modifiers[] = new IncludeDestinations(); + } + + return $modifiers; + } } diff --git a/src/Event/HandleDatabaseModifierEvent.php b/src/Event/HandleDatabaseModifierEvent.php index 74bbc81..c548d59 100644 --- a/src/Event/HandleDatabaseModifierEvent.php +++ b/src/Event/HandleDatabaseModifierEvent.php @@ -3,7 +3,7 @@ namespace App\Event; use App\Event\HandleModifierEvent; -use App\Modifiers\Modifier; +use App\Modifier\Modifier; use App\Provider\Repository; use Doctrine\ORM\QueryBuilder; diff --git a/src/Event/HandleModifierEvent.php b/src/Event/HandleModifierEvent.php index af28988..5544af4 100644 --- a/src/Event/HandleModifierEvent.php +++ b/src/Event/HandleModifierEvent.php @@ -2,7 +2,7 @@ namespace App\Event; -use App\Modifiers\Modifier; +use App\Modifier\Modifier; use App\Provider\Repository; class HandleModifierEvent diff --git a/src/Event/PostProcessEvent.php b/src/Event/PostProcessEvent.php new file mode 100644 index 0000000..5d23040 --- /dev/null +++ b/src/Event/PostProcessEvent.php @@ -0,0 +1,27 @@ +data = $data; + } + + public function getData() + { + return $this->data; + } + + public function setData($data): void + { + $this->data = $data; + } +} diff --git a/src/Exception/NonExistentServiceException.php b/src/Exception/NonExistentServiceException.php index c2c6ba3..b19aef2 100644 --- a/src/Exception/NonExistentServiceException.php +++ b/src/Exception/NonExistentServiceException.php @@ -4,7 +4,6 @@ namespace App\Exception; -class NonExistentServiceException extends \Exception +class NonExistentServiceException extends \LogicException { - -} \ No newline at end of file +} diff --git a/src/Exception/NotSupportedException.php b/src/Exception/NotSupportedException.php index 4ab7b89..1af7250 100644 --- a/src/Exception/NotSupportedException.php +++ b/src/Exception/NotSupportedException.php @@ -2,6 +2,6 @@ namespace App\Exception; -class NotSupportedException extends \RuntimeException +class NotSupportedException extends \LogicException { -} \ No newline at end of file +} diff --git a/src/Exception/UnsupportedModifierException.php b/src/Exception/UnsupportedModifierException.php index 4dbd2bf..f1e7145 100644 --- a/src/Exception/UnsupportedModifierException.php +++ b/src/Exception/UnsupportedModifierException.php @@ -2,10 +2,10 @@ namespace App\Exception; -use App\Modifiers\Modifier; +use App\Modifier\Modifier; use App\Provider\Repository; -class UnsupportedModifierException extends \Exception +class UnsupportedModifierException extends \LogicException { public static function createFromModifier(Modifier $modifier, Repository $repository) { diff --git a/src/Handler/Database/FieldFilterDatabaseHandler.php b/src/Handler/Database/FieldFilterDatabaseHandler.php new file mode 100644 index 0000000..cb77a66 --- /dev/null +++ b/src/Handler/Database/FieldFilterDatabaseHandler.php @@ -0,0 +1,52 @@ + [ + 'name' => 'name', + ], + ]; + + public function process(HandleModifierEvent $event) + { + if (!$event instanceof HandleDatabaseModifierEvent) { + return; + } + + /** @var FieldFilter $modifier */ + $modifier = $event->getModifier(); + $builder = $event->getBuilder(); + $alias = $event->getMeta()['alias']; + + $field = $this->mapFieldName($event->getMeta()['type'], $modifier->getField()); + $operator = $modifier->getOperator(); + $value = $modifier->getValue(); + + $parameter = sprintf(":%s_%s", $alias, $field); + + $builder + ->where(sprintf("%s.%s %s %s", $alias, $field, $operator, $parameter)) + ->setParameter($parameter, $value) + ; + } + + protected function mapFieldName(string $class, string $field) + { + if (!isset($this->mapping[$class][$field])) { + throw new \InvalidArgumentException( + sprintf("Unable to map field %s of %s into entity field.", $field, $class) + ); + } + + return $this->mapping[$class][$field]; + } +} diff --git a/src/Handlers/Database/IdFilterDatabaseHandler.php b/src/Handler/Database/IdFilterDatabaseHandler.php similarity index 91% rename from src/Handlers/Database/IdFilterDatabaseHandler.php rename to src/Handler/Database/IdFilterDatabaseHandler.php index 58a89db..10f124c 100644 --- a/src/Handlers/Database/IdFilterDatabaseHandler.php +++ b/src/Handler/Database/IdFilterDatabaseHandler.php @@ -1,9 +1,9 @@ em = $entityManager; + $this->converter = $converter; + $this->id = $id; + } + + public function process(PostProcessEvent $event) + { + $provider = $event->getMeta()['provider']; + $stops = $event + ->getData() + ->map(t\property('id')) + ->map(f\apply([$this->id, 'generate'], $provider)) + ->all(); + + $destinations = collect($this->em->createQueryBuilder() + ->select('t', 'tl', 'f', 'fs', 'ts') + ->from(TrackEntity::class, 't') + ->join('t.stopsInTrack', 'ts') + ->join('t.line', 'tl') + ->where('ts.stop IN (:stops)') + ->join('t.final', 'f') + ->join('f.stop', 'fs') + ->getQuery() + ->execute(['stops' => $stops])) + ->reduce(function ($grouped, TrackEntity $track) { + foreach ($track->getStopsInTrack()->map(t\property('stop'))->map(t\property('id')) as $stop) { + $grouped[$stop] = ($grouped[$stop] ?? collect())->add($track); + } + + return $grouped; + }, collect()) + ->map(function (Collection $tracks) { + return $tracks + ->groupBy(function (TrackEntity $track) { + return $track->getFinal()->getStop()->getId(); + })->map(function (Collection $tracks, $id) { + return Destination::createFromArray([ + 'stop' => $this->converter->convert($tracks->first()->getFinal()->getStop()), + 'lines' => $tracks + ->map(t\property('line')) + ->unique(t\property('id')) + ->map(f\ref([$this->converter, 'convert'])) + ->values(), + ]); + })->values(); + }); + + $event->getData()->each(function (Stop $stop) use ($provider, $destinations) { + $stop->setDestinations($destinations[$this->id->generate($provider, $stop->getId())]); + }); + } +} diff --git a/src/Handlers/Database/LimitDatabaseHandler.php b/src/Handler/Database/LimitDatabaseHandler.php similarity index 85% rename from src/Handlers/Database/LimitDatabaseHandler.php rename to src/Handler/Database/LimitDatabaseHandler.php index 202ddce..3f445a9 100644 --- a/src/Handlers/Database/LimitDatabaseHandler.php +++ b/src/Handler/Database/LimitDatabaseHandler.php @@ -1,11 +1,11 @@ field = $field; + $this->value = $value; + $this->operator = $operator; + } + + public static function contains(string $field, string $value) + { + return new static($field, "%$value%", 'LIKE'); + } + + public function getField(): string + { + return $this->field; + } + + public function getValue() + { + return $this->value; + } + + public function getOperator(): string + { + return $this->operator; + } +} diff --git a/src/Modifiers/IdFilter.php b/src/Modifier/IdFilter.php similarity index 91% rename from src/Modifiers/IdFilter.php rename to src/Modifier/IdFilter.php index e2f570c..f7b6a13 100644 --- a/src/Modifiers/IdFilter.php +++ b/src/Modifier/IdFilter.php @@ -1,9 +1,9 @@ $this->provider, - ], $meta)); + $handler = $this->getHandler($modifier); - $class = get_class($modifier); + switch (true) { + case $handler instanceof PostProcessingHandler: + $reducers[] = function ($result) use ($meta, $modifier, $handler) { + $event = new PostProcessEvent($result, $modifier, $this, array_merge([ + 'provider' => $this->provider, + ], $meta)); - if (!$this->handlers->has($class)) { - throw UnsupportedModifierException::createFromModifier($modifier, $this); + $handler->process($event); + + return $event->getData(); + }; + break; + + default: + $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([ + 'provider' => $this->provider, + ], $meta)); + + $handler->process($event); + break; } - - $handler = $this->handlers->get($class); - - $handler->process($event); } + + return collect($reducers); + } + + protected function allFromQueryBuilder(QueryBuilder $builder, iterable $modifiers, array $meta = []) + { + $reducers = $this->processQueryBuilder($builder, $modifiers, $meta); + + return $reducers->reduce(function ($result, $reducer) { + return $reducer($result); + }, collect($builder->getQuery()->execute())->map(\Closure::fromCallable([$this, 'convert']))); + } + + public function first(Modifier ...$modifiers) + { + return $this->all(Limit::count(1), ...$modifiers)->first(); + } + + protected function getHandler(Modifier $modifier) + { + $class = get_class($modifier); + + if (!$this->handlers->has($class)) { + throw UnsupportedModifierException::createFromModifier($modifier, $this); + } + + return $this->handlers->get($class); } /** @@ -106,6 +154,10 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit */ public static function getSubscribedServices() { - return static::getHandlers(); + return array_merge([ + IdFilter::class => IdFilterDatabaseHandler::class, + Limit::class => LimitDatabaseHandler::class, + FieldFilter::class => FieldFilterDatabaseHandler::class, + ], static::getHandlers()); } } diff --git a/src/Provider/Database/GenericLineRepository.php b/src/Provider/Database/GenericLineRepository.php index 244b8e0..9ca147d 100644 --- a/src/Provider/Database/GenericLineRepository.php +++ b/src/Provider/Database/GenericLineRepository.php @@ -4,24 +4,19 @@ namespace App\Provider\Database; use App\Entity\LineEntity; use App\Event\HandleDatabaseModifierEvent; -use App\Handlers\Database\LimitDatabaseHandler; -use App\Handlers\Database\IdFilterDatabaseHandler; -use App\Handlers\ModifierHandler; +use App\Handler\Database\LimitDatabaseHandler; +use App\Handler\Database\IdFilterDatabaseHandler; +use App\Handler\ModifierHandler; use App\Model\Line; -use App\Modifiers\Limit; -use App\Modifiers\IdFilter; +use App\Modifier\Limit; +use App\Modifier\IdFilter; use App\Provider\LineRepository; -use App\Modifiers\Modifier; +use App\Modifier\Modifier; use Tightenco\Collect\Support\Collection; use Kadet\Functional as f; class GenericLineRepository extends DatabaseRepository implements LineRepository { - public function first(Modifier ...$modifiers) - { - return $this->all(Limit::count(1), ...$modifiers)->first(); - } - public function all(Modifier ...$modifiers): Collection { $builder = $this->em @@ -30,21 +25,10 @@ class GenericLineRepository extends DatabaseRepository implements LineRepository ->select('line') ; - $this->processQueryBuilder($builder, $modifiers, [ + return $this->allFromQueryBuilder($builder, $modifiers, [ 'alias' => 'line', 'entity' => LineEntity::class, 'type' => Line::class, ]); - - return collect($builder->getQuery()->execute())->map(f\ref([$this, 'convert'])); - } - - /** @return ModifierHandler[] */ - protected static function getHandlers() - { - return [ - IdFilter::class => IdFilterDatabaseHandler::class, - Limit::class => LimitDatabaseHandler::class, - ]; } } diff --git a/src/Provider/Database/GenericStopRepository.php b/src/Provider/Database/GenericStopRepository.php index fe7b017..62a7a90 100644 --- a/src/Provider/Database/GenericStopRepository.php +++ b/src/Provider/Database/GenericStopRepository.php @@ -3,89 +3,34 @@ namespace App\Provider\Database; use App\Entity\StopEntity; -use App\Entity\TrackEntity; -use App\Model\Destination; +use App\Handler\Database\IncludeDestinationsDatabaseHandler; use App\Model\Stop; +use App\Modifier\Modifier; +use App\Modifier\IncludeDestinations; use App\Provider\StopRepository; -use Kadet\Functional as f; -use Kadet\Functional\Transforms as t; use Tightenco\Collect\Support\Collection; class GenericStopRepository extends DatabaseRepository implements StopRepository { - public function getAll(): Collection + public function all(Modifier ...$modifiers): Collection { - $stops = $this->em->getRepository(StopEntity::class)->findAll(); + $builder = $this->em + ->createQueryBuilder() + ->from(StopEntity::class, 'stop') + ->select('stop') + ; - return collect($stops)->map(f\ref([$this, 'convert'])); - } - - public function getById($id): ?Stop - { - $id = $this->id->generate($this->provider, $id); - $stop = $this->em->getRepository(StopEntity::class)->find($id); - - return $this->convert($stop); - } - - public function getManyById($ids): Collection - { - $ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider)); - $stops = $this->em->getRepository(StopEntity::class)->findBy(['id' => $ids->all()]); - - return collect($stops)->map(f\ref([$this, 'convert'])); - } - - public function findByName(string $name): Collection - { - $query = $this->em->createQueryBuilder() - ->select('s') - ->from(StopEntity::class, 's') - ->where('s.name LIKE :name') - ->getQuery(); - - $stops = collect($query->execute([':name' => "%$name%"])); - - $destinations = collect($this->em->createQueryBuilder() - ->select('t', 'tl', 'f', 'fs', 'ts') - ->from(TrackEntity::class, 't') - ->join('t.stopsInTrack', 'ts') - ->join('t.line', 'tl') - ->where('ts.stop IN (:stops)') - ->join('t.final', 'f') - ->join('f.stop', 'fs') - ->getQuery() - ->execute(['stops' => $stops->map(t\property('id'))->all()])) - ->reduce(function ($grouped, TrackEntity $track) { - foreach ($track->getStopsInTrack()->map(t\property('stop'))->map(t\property('id')) as $stop) { - $grouped[$stop] = ($grouped[$stop] ?? collect())->add($track); - } - - return $grouped; - }, collect()) - ->map(function (Collection $tracks) { - return $tracks - ->groupBy(function (TrackEntity $track) { - return $track->getFinal()->getStop()->getId(); - })->map(function (Collection $tracks, $id) { - return Destination::createFromArray([ - 'stop' => $this->convert($tracks->first()->getFinal()->getStop()), - 'lines' => $tracks - ->map(t\property('line')) - ->unique(t\property('id')) - ->map(f\ref([$this, 'convert'])) - ->values(), - ]); - })->values(); - }); - - return collect($stops)->map(f\ref([$this, 'convert']))->each(function (Stop $stop) use ($destinations) { - $stop->setDestinations($destinations[$this->id->generate($this->provider, $stop->getId())]); - }); + return $this->allFromQueryBuilder($builder, $modifiers, [ + 'alias' => 'stop', + 'entity' => StopEntity::class, + 'type' => Stop::class, + ]); } protected static function getHandlers() { - return []; + return array_merge(parent::getHandlers(), [ + IncludeDestinations::class => IncludeDestinationsDatabaseHandler::class, + ]); } } diff --git a/src/Provider/Dummy/DummyStopRepository.php b/src/Provider/Dummy/DummyStopRepository.php index caea318..959d5bd 100644 --- a/src/Provider/Dummy/DummyStopRepository.php +++ b/src/Provider/Dummy/DummyStopRepository.php @@ -3,6 +3,7 @@ namespace App\Provider\Dummy; use App\Model\Stop; +use App\Modifier\Modifier; use App\Provider\StopRepository; use App\Service\Proxy\ReferenceFactory; use Tightenco\Collect\Support\Collection; @@ -41,4 +42,14 @@ class DummyStopRepository implements StopRepository { return collect(); } + + public function first(Modifier ...$modifiers) + { + // TODO: Implement first() method. + } + + public function all(Modifier ...$modifiers): Collection + { + // TODO: Implement all() method. + } } diff --git a/src/Provider/FluentRepository.php b/src/Provider/FluentRepository.php index d0644fe..a02831c 100644 --- a/src/Provider/FluentRepository.php +++ b/src/Provider/FluentRepository.php @@ -2,7 +2,7 @@ namespace App\Provider; -use App\Modifiers\Modifier; +use App\Modifier\Modifier; use Tightenco\Collect\Support\Collection; interface FluentRepository extends Repository diff --git a/src/Provider/StopRepository.php b/src/Provider/StopRepository.php index ea87c03..bf6aa3f 100644 --- a/src/Provider/StopRepository.php +++ b/src/Provider/StopRepository.php @@ -7,10 +7,6 @@ namespace App\Provider; use App\Model\Stop; use Tightenco\Collect\Support\Collection; -interface StopRepository extends Repository +interface StopRepository extends FluentRepository { - public function getAll(): Collection; - public function getById($id): ?Stop; - public function getManyById($ids): Collection; - public function findByName(string $name): Collection; } diff --git a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php index 165e571..e7f7dc6 100644 --- a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php +++ b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php @@ -6,7 +6,7 @@ use App\Model\Departure; use App\Model\Line; use App\Model\Stop; use App\Model\Vehicle; -use App\Modifiers\IdFilter; +use App\Modifier\IdFilter; use App\Provider\Database\GenericScheduleRepository; use App\Provider\DepartureRepository; use App\Provider\LineRepository; diff --git a/src/Service/IdUtils.php b/src/Service/IdUtils.php index 61bc00b..e652ad4 100644 --- a/src/Service/IdUtils.php +++ b/src/Service/IdUtils.php @@ -11,6 +11,7 @@ class IdUtils public function generate(ProviderEntity $provider, $id) { + // todo: use array cache if not fast enough return sprintf('%s%s%s', $provider->getId(), self::DELIMITER, $id); } @@ -23,4 +24,4 @@ class IdUtils { return $this->strip($entity->getId()); } -} \ No newline at end of file +} -- 2.45.2 From c7dc90bfc5b878f8da955e0f1351c90c98c89f58 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 16 Feb 2020 21:59:38 +0100 Subject: [PATCH 5/7] #35 - Move trip and operator repository to fluent pattern --- src/Controller/Api/v1/TripController.php | 3 +- .../Database/GenericOperatorRepository.php | 37 +++++++------------ .../Database/GenericTripRepository.php | 29 ++++++--------- src/Provider/OperatorRepository.php | 7 +--- src/Provider/TripRepository.php | 3 +- 5 files changed, 30 insertions(+), 49 deletions(-) diff --git a/src/Controller/Api/v1/TripController.php b/src/Controller/Api/v1/TripController.php index 87b87cd..031f39a 100644 --- a/src/Controller/Api/v1/TripController.php +++ b/src/Controller/Api/v1/TripController.php @@ -4,6 +4,7 @@ namespace App\Controller\Api\v1; use App\Controller\Controller; use App\Model\Trip; +use App\Modifier\IdFilter; use App\Provider\TripRepository; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -18,7 +19,7 @@ class TripController extends Controller */ public function one($id, TripRepository $repository) { - $trip = $repository->getById($id); + $trip = $repository->all(new IdFilter($id)); return $this->json($trip, Response::HTTP_OK, [], $this->serializerContextFactory->create(Trip::class)); } diff --git a/src/Provider/Database/GenericOperatorRepository.php b/src/Provider/Database/GenericOperatorRepository.php index 929d994..2af85f7 100644 --- a/src/Provider/Database/GenericOperatorRepository.php +++ b/src/Provider/Database/GenericOperatorRepository.php @@ -2,37 +2,26 @@ namespace App\Provider\Database; +use App\Entity\OperatorEntity; use App\Model\Operator; +use App\Modifier\Modifier; use App\Provider\OperatorRepository; use Tightenco\Collect\Support\Collection; class GenericOperatorRepository extends DatabaseRepository implements OperatorRepository { - public function getAll(): Collection + public function all(Modifier ...$modifiers): Collection { - $repository = $this->em->getRepository(Operator::class); - $operators = $repository->findAll(); + $builder = $this->em + ->createQueryBuilder() + ->from(OperatorEntity::class, 'operator') + ->select('operator') + ; - return collect($operators); - } - - public function getById($id): ?Operator - { - $repository = $this->em->getRepository(Operator::class); - - return $repository->find($id); - } - - public function getManyById($ids): Collection - { - $repository = $this->em->getRepository(Operator::class); - $operators = $repository->findBy(['id' => $ids]); - - return collect($operators); - } - - protected static function getHandlers() - { - return []; + return $this->allFromQueryBuilder($builder, $modifiers, [ + 'alias' => 'operator', + 'entity' => OperatorEntity::class, + 'type' => Operator::class, + ]); } } diff --git a/src/Provider/Database/GenericTripRepository.php b/src/Provider/Database/GenericTripRepository.php index 3944e18..00626ad 100644 --- a/src/Provider/Database/GenericTripRepository.php +++ b/src/Provider/Database/GenericTripRepository.php @@ -4,30 +4,25 @@ namespace App\Provider\Database; use App\Entity\TripEntity; use App\Model\Trip; +use App\Modifier\Modifier; use App\Provider\TripRepository; +use Tightenco\Collect\Support\Collection; class GenericTripRepository extends DatabaseRepository implements TripRepository { - public function getById(string $id): Trip + public function all(Modifier ...$modifiers): Collection { - $id = $this->id->generate($this->provider, $id); - - $trip = $this->em + $builder = $this->em ->createQueryBuilder() - ->from(TripEntity::class, 't') - ->join('t.stops', 'ts') + ->from(TripEntity::class, 'trip') + ->join('trip.stops', 'ts') ->join('ts.stop', 's') - ->select('t', 'ts') - ->where('t.id = :id') - ->getQuery() - ->setParameter('id', $id) - ->getOneOrNullResult(); + ->select('t', 'ts'); - return $this->convert($trip); - } - - protected static function getHandlers() - { - return []; + return $this->allFromQueryBuilder($builder, $modifiers, [ + 'alias' => 'operator', + 'entity' => TripEntity::class, + 'type' => Trip::class, + ]); } } diff --git a/src/Provider/OperatorRepository.php b/src/Provider/OperatorRepository.php index 79f8b20..70d88c7 100644 --- a/src/Provider/OperatorRepository.php +++ b/src/Provider/OperatorRepository.php @@ -7,9 +7,6 @@ namespace App\Provider; use App\Model\Operator; use Tightenco\Collect\Support\Collection; -interface OperatorRepository +interface OperatorRepository extends FluentRepository { - public function getAll(): Collection; - public function getById($id): ?Operator; - public function getManyById($ids): Collection; -} \ No newline at end of file +} diff --git a/src/Provider/TripRepository.php b/src/Provider/TripRepository.php index 4a82521..17459d8 100644 --- a/src/Provider/TripRepository.php +++ b/src/Provider/TripRepository.php @@ -4,7 +4,6 @@ namespace App\Provider; use App\Model\Trip; -interface TripRepository +interface TripRepository extends FluentRepository { - public function getById(string $id): Trip; } -- 2.45.2 From 9d0e4fdb2a3f1682d1837f3305023795138c58b1 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 17 Feb 2020 21:48:00 +0100 Subject: [PATCH 6/7] #35 - Rewrite Track Repository into fluent pattern --- src/Controller/Api/v1/StopsController.php | 1 + src/Controller/Api/v1/TracksController.php | 11 ++- .../Database/FieldFilterDatabaseHandler.php | 8 +- .../Database/IdFilterDatabaseHandler.php | 2 +- .../IncludeDestinationsDatabaseHandler.php | 2 +- .../RelatedFilterDatabaseGenericHandler.php | 96 +++++++++++++++++++ src/Handler/PostProcessingHandler.php | 3 +- src/Modifier/RelatedFilter.php | 27 ++++++ src/Provider/Database/DatabaseRepository.php | 39 ++++---- .../Database/GenericTrackRepository.php | 49 +++------- .../Database/GenericTripRepository.php | 2 +- src/Provider/TrackRepository.php | 10 +- 12 files changed, 177 insertions(+), 73 deletions(-) create mode 100644 src/Handler/Database/RelatedFilterDatabaseGenericHandler.php create mode 100644 src/Modifier/RelatedFilter.php diff --git a/src/Controller/Api/v1/StopsController.php b/src/Controller/Api/v1/StopsController.php index 0fca3e4..64c4107 100644 --- a/src/Controller/Api/v1/StopsController.php +++ b/src/Controller/Api/v1/StopsController.php @@ -10,6 +10,7 @@ use App\Model\StopGroup; use App\Modifier\IdFilter; use App\Modifier\FieldFilter; use App\Modifier\IncludeDestinations; +use App\Modifier\RelatedFilter; use App\Provider\StopRepository; use App\Provider\TrackRepository; use App\Service\Proxy\ReferenceFactory; diff --git a/src/Controller/Api/v1/TracksController.php b/src/Controller/Api/v1/TracksController.php index 069a338..6f9fbad 100644 --- a/src/Controller/Api/v1/TracksController.php +++ b/src/Controller/Api/v1/TracksController.php @@ -3,8 +3,11 @@ namespace App\Controller\Api\v1; use App\Controller\Controller; +use App\Model\Line; use App\Model\Stop; use App\Model\Track; +use App\Modifier\IdFilter; +use App\Modifier\RelatedFilter; use App\Provider\TrackRepository; use Nelmio\ApiDocBundle\Annotation\Model; use Swagger\Annotations as SWG; @@ -49,7 +52,7 @@ class TracksController extends Controller { $id = encapsulate($request->query->get('id')); - return $this->json($repository->getManyById($id)); + return $this->json($repository->all(new IdFilter($id))); } private function byStop(Request $request, TrackRepository $repository) @@ -63,8 +66,8 @@ class TracksController extends Controller private function byLine(Request $request, TrackRepository $repository) { $line = $request->query->get('line'); - $line = array_map([Stop::class, 'reference'], encapsulate($line)); + $line = Line::reference($line); - return $this->json($repository->getByLine($line)); + return $this->json($repository->all(new RelatedFilter($line))); } -} \ No newline at end of file +} diff --git a/src/Handler/Database/FieldFilterDatabaseHandler.php b/src/Handler/Database/FieldFilterDatabaseHandler.php index cb77a66..0bfc83c 100644 --- a/src/Handler/Database/FieldFilterDatabaseHandler.php +++ b/src/Handler/Database/FieldFilterDatabaseHandler.php @@ -7,6 +7,7 @@ use App\Event\HandleModifierEvent; use App\Handler\ModifierHandler; use App\Model\Stop; use App\Modifier\FieldFilter; +use function App\Functions\encapsulate; class FieldFilterDatabaseHandler implements ModifierHandler { @@ -33,8 +34,13 @@ class FieldFilterDatabaseHandler implements ModifierHandler $parameter = sprintf(":%s_%s", $alias, $field); + if ($operator === 'in' || $operator === 'not in') { + $parameter = "($parameter)"; + $value = encapsulate($value); + } + $builder - ->where(sprintf("%s.%s %s %s", $alias, $field, $operator, $parameter)) + ->andWhere(sprintf("%s.%s %s %s", $alias, $field, $operator, $parameter)) ->setParameter($parameter, $value) ; } diff --git a/src/Handler/Database/IdFilterDatabaseHandler.php b/src/Handler/Database/IdFilterDatabaseHandler.php index 10f124c..9f95d94 100644 --- a/src/Handler/Database/IdFilterDatabaseHandler.php +++ b/src/Handler/Database/IdFilterDatabaseHandler.php @@ -37,7 +37,7 @@ class IdFilterDatabaseHandler implements ModifierHandler $mapper = apply([$this->id, 'generate'], $provider); $builder - ->where($modifier->isMultiple() ? "{$alias} in (:id)" : "{$alias} = :id") + ->andWhere($modifier->isMultiple() ? "{$alias} in (:id)" : "{$alias} = :id") ->setParameter(':id', $modifier->isMultiple() ? array_map($mapper, $id) : $mapper($id)); ; } diff --git a/src/Handler/Database/IncludeDestinationsDatabaseHandler.php b/src/Handler/Database/IncludeDestinationsDatabaseHandler.php index 4d5e1f2..b58287f 100644 --- a/src/Handler/Database/IncludeDestinationsDatabaseHandler.php +++ b/src/Handler/Database/IncludeDestinationsDatabaseHandler.php @@ -27,7 +27,7 @@ class IncludeDestinationsDatabaseHandler implements PostProcessingHandler $this->id = $id; } - public function process(PostProcessEvent $event) + public function postProcess(PostProcessEvent $event) { $provider = $event->getMeta()['provider']; $stops = $event diff --git a/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php b/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php new file mode 100644 index 0000000..cb4627e --- /dev/null +++ b/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php @@ -0,0 +1,96 @@ + [ + Line::class => 'line', + ], + ]; + + protected $references = [ + Line::class => LineEntity::class, + ]; + + private $em; + private $inner; + private $id; + + public function __construct(ContainerInterface $inner, EntityManagerInterface $em, IdUtils $idUtils) + { + $this->inner = $inner; + $this->em = $em; + $this->id = $idUtils; + } + + public function process(HandleModifierEvent $event) + { + if (!$event instanceof HandleDatabaseModifierEvent) { + return; + } + + /** @var RelatedFilter $modifier */ + $modifier = $event->getModifier(); + $builder = $event->getBuilder(); + $alias = $event->getMeta()['alias']; + $type = $event->getMeta()['type']; + + if (!array_key_exists($type, $this->mapping)) { + throw new \InvalidArgumentException( + sprintf("Relationship filtering for %s is not supported.", $type) + ); + } + + if (!array_key_exists($modifier->getRelationship(), $this->mapping[$type])) { + throw new \InvalidArgumentException( + sprintf("Relationship %s is not supported for .", $type) + ); + } + + $relationship = $this->mapping[$type][$modifier->getRelationship()]; + + $parameter = sprintf(":%s_%s", $alias, $relationship); + $reference = $this->getEntityReference($modifier->getRelated(), $event->getMeta()['provider']); + + $builder + ->join(sprintf('%s.%s', $alias, $relationship), $relationship) + ->andWhere(sprintf("%s = %s", $relationship, $parameter)) + ->setParameter($parameter, $reference) + ; + } + + // todo: extract that to separate service + private function getEntityReference(Referable $object, ProviderEntity $provider) + { + return $this->em->getReference( + $this->references[get_class($object)], + $this->id->generate($provider, $object->getId()) + ); + } + + /** + * @inheritDoc + */ + public static function getSubscribedServices() + { + return [ + TrackRelatedFilterDatabaseHandler::class, + ]; + } +} diff --git a/src/Handler/PostProcessingHandler.php b/src/Handler/PostProcessingHandler.php index 4da8a89..a18f0d8 100644 --- a/src/Handler/PostProcessingHandler.php +++ b/src/Handler/PostProcessingHandler.php @@ -2,10 +2,9 @@ namespace App\Handler; -use App\Event\HandleModifierEvent; use App\Event\PostProcessEvent; interface PostProcessingHandler { - public function process(PostProcessEvent $event); + public function postProcess(PostProcessEvent $event); } diff --git a/src/Modifier/RelatedFilter.php b/src/Modifier/RelatedFilter.php new file mode 100644 index 0000000..f3e9038 --- /dev/null +++ b/src/Modifier/RelatedFilter.php @@ -0,0 +1,27 @@ +object = $object; + $this->relationship = $relation ?: get_class($object); + } + + public function getRelationship(): string + { + return $this->relationship; + } + + public function getRelated(): Referable + { + return $this->object; + } +} diff --git a/src/Provider/Database/DatabaseRepository.php b/src/Provider/Database/DatabaseRepository.php index e7e7529..60ad5ca 100644 --- a/src/Provider/Database/DatabaseRepository.php +++ b/src/Provider/Database/DatabaseRepository.php @@ -9,12 +9,15 @@ use App\Exception\UnsupportedModifierException; use App\Handler\Database\IdFilterDatabaseHandler; use App\Handler\Database\LimitDatabaseHandler; use App\Handler\Database\FieldFilterDatabaseHandler; +use App\Handler\Database\RelatedFilterDatabaseGenericHandler; +use App\Handler\ModifierHandler; use App\Handler\PostProcessingHandler; use App\Model\Referable; use App\Modifier\IdFilter; use App\Modifier\Limit; use App\Modifier\Modifier; use App\Modifier\FieldFilter; +use App\Modifier\RelatedFilter; use App\Provider\Repository; use App\Service\Converter; use App\Service\IdUtils; @@ -85,27 +88,26 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit foreach ($modifiers as $modifier) { $handler = $this->getHandler($modifier); - switch (true) { - case $handler instanceof PostProcessingHandler: - $reducers[] = function ($result) use ($meta, $modifier, $handler) { - $event = new PostProcessEvent($result, $modifier, $this, array_merge([ - 'provider' => $this->provider, - ], $meta)); + if ($handler instanceof ModifierHandler) { + $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([ + 'provider' => $this->provider, + ], $meta)); - $handler->process($event); + $handler->process($event); + } - return $event->getData(); - }; - break; - - default: - $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([ + if ($handler instanceof PostProcessingHandler) { + $reducers[] = function ($result) use ($meta, $modifier, $handler) { + $event = new PostProcessEvent($result, $modifier, $this, array_merge([ 'provider' => $this->provider, ], $meta)); - $handler->process($event); - break; + $handler->postProcess($event); + + return $event->getData(); + }; } + } return collect($reducers); @@ -155,9 +157,10 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit public static function getSubscribedServices() { return array_merge([ - IdFilter::class => IdFilterDatabaseHandler::class, - Limit::class => LimitDatabaseHandler::class, - FieldFilter::class => FieldFilterDatabaseHandler::class, + IdFilter::class => IdFilterDatabaseHandler::class, + Limit::class => LimitDatabaseHandler::class, + FieldFilter::class => FieldFilterDatabaseHandler::class, + RelatedFilter::class => RelatedFilterDatabaseGenericHandler::class, ], static::getHandlers()); } } diff --git a/src/Provider/Database/GenericTrackRepository.php b/src/Provider/Database/GenericTrackRepository.php index 53bd235..82699d7 100644 --- a/src/Provider/Database/GenericTrackRepository.php +++ b/src/Provider/Database/GenericTrackRepository.php @@ -2,36 +2,18 @@ namespace App\Provider\Database; -use App\Entity\LineEntity; use App\Entity\StopEntity; use App\Entity\StopInTrack; use App\Entity\TrackEntity; -use function App\Functions\encapsulate; -use App\Model\Stop; +use App\Modifier\Modifier; use App\Model\Track; use App\Provider\TrackRepository; use Tightenco\Collect\Support\Collection; use Kadet\Functional as f; +use function App\Functions\encapsulate; class GenericTrackRepository extends DatabaseRepository implements TrackRepository { - public function getAll(): Collection - { - $tracks = $this->em->getRepository(TrackEntity::class)->findAll(); - - return collect($tracks)->map(f\ref([$this, 'convert'])); - } - - public function getById($id): Track - { - // TODO: Implement getById() method. - } - - public function getManyById($ids): Collection - { - // TODO: Implement getManyById() method. - } - public function getByStop($stop): Collection { $reference = f\apply(f\ref([$this, 'reference']), StopEntity::class); @@ -49,24 +31,17 @@ class GenericTrackRepository extends DatabaseRepository implements TrackReposito }); } - public function getByLine($line): Collection + public function all(Modifier ...$modifiers): Collection { - $reference = f\apply(f\ref([$this, 'reference']), LineEntity::class); + $builder = $this->em + ->createQueryBuilder() + ->from(TrackEntity::class, 'track') + ->select('track'); - $tracks = $this->em->createQueryBuilder() - ->from(StopInTrack::class, 'st') - ->join('st.track', 't') - ->join('t.stops', 's') - ->where('st.line in (:line)') - ->select(['st', 't', 's']) - ->getQuery() - ->execute(['stop' => array_map($reference, encapsulate($line))]); - - return collect($tracks)->map(f\ref([$this, 'convert'])); - } - - protected static function getHandlers() - { - return []; + return $this->allFromQueryBuilder($builder, $modifiers, [ + 'alias' => 'track', + 'entity' => TrackEntity::class, + 'type' => Track::class, + ]); } } diff --git a/src/Provider/Database/GenericTripRepository.php b/src/Provider/Database/GenericTripRepository.php index 00626ad..6ffd652 100644 --- a/src/Provider/Database/GenericTripRepository.php +++ b/src/Provider/Database/GenericTripRepository.php @@ -20,7 +20,7 @@ class GenericTripRepository extends DatabaseRepository implements TripRepository ->select('t', 'ts'); return $this->allFromQueryBuilder($builder, $modifiers, [ - 'alias' => 'operator', + 'alias' => 'trip', 'entity' => TripEntity::class, 'type' => Trip::class, ]); diff --git a/src/Provider/TrackRepository.php b/src/Provider/TrackRepository.php index 708e329..3e70a03 100644 --- a/src/Provider/TrackRepository.php +++ b/src/Provider/TrackRepository.php @@ -5,13 +5,7 @@ namespace App\Provider; use App\Model\Track; use Tightenco\Collect\Support\Collection; -interface TrackRepository +interface TrackRepository extends FluentRepository { - public function getAll(): Collection; - - public function getById($id): Track; - public function getManyById($ids): Collection; - public function getByStop($stop): Collection; - public function getByLine($line): Collection; -} \ No newline at end of file +} -- 2.45.2 From f4e3ee6f556dea810dfa927223c3cc3db2299fb6 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Tue, 18 Feb 2020 22:45:16 +0100 Subject: [PATCH 7/7] #35 - Add stop filter for tracks --- src/Controller/Api/v1/StopsController.php | 21 ++----- src/Controller/Api/v1/TracksController.php | 63 ++++++++----------- .../RelatedFilterDatabaseGenericHandler.php | 40 ++++++------ .../Database/TrackByStopDatabaseHandler.php | 43 +++++++++++++ src/Service/ReferenceFactory.php | 42 +++++++++++++ 5 files changed, 137 insertions(+), 72 deletions(-) create mode 100644 src/Handler/Database/TrackByStopDatabaseHandler.php create mode 100644 src/Service/ReferenceFactory.php diff --git a/src/Controller/Api/v1/StopsController.php b/src/Controller/Api/v1/StopsController.php index 64c4107..669e58f 100644 --- a/src/Controller/Api/v1/StopsController.php +++ b/src/Controller/Api/v1/StopsController.php @@ -1,6 +1,5 @@ values(); } - /** - * @param Request $request - * - * @return array - */ - private function getModifiersFromRequest(Request $request): array + private function getModifiersFromRequest(Request $request) { - $modifiers = []; - if ($request->query->has('name')) { - $modifiers[] = FieldFilter::contains('name', $request->query->get('name')); + yield FieldFilter::contains('name', $request->query->get('name')); } if ($request->query->has('id')) { - $modifiers[] = new IdFilter($request->query->get('id')); + yield new IdFilter($request->query->get('id')); } if ($request->query->has('include-destinations')) { - $modifiers[] = new IncludeDestinations(); + yield new IncludeDestinations(); } - - return $modifiers; } } diff --git a/src/Controller/Api/v1/TracksController.php b/src/Controller/Api/v1/TracksController.php index 6f9fbad..71b7ae8 100644 --- a/src/Controller/Api/v1/TracksController.php +++ b/src/Controller/Api/v1/TracksController.php @@ -9,6 +9,7 @@ use App\Model\Track; use App\Modifier\IdFilter; use App\Modifier\RelatedFilter; use App\Provider\TrackRepository; +use App\Service\IterableUtils; use Nelmio\ApiDocBundle\Annotation\Model; use Swagger\Annotations as SWG; use Symfony\Component\HttpFoundation\Request; @@ -31,43 +32,31 @@ class TracksController extends Controller */ public function index(Request $request, TrackRepository $repository) { - switch (true) { - case $request->query->has('stop'): - return $this->byStop($request, $repository); - case $request->query->has('line'): - return $this->byLine($request, $repository); - case $request->query->has('id'): - return $this->byId($request, $repository); - default: - throw new BadRequestHttpException( - sprintf( - 'At least one parameter of %s must be set.', - implode(', ', ['stop', 'line', 'id']) - ) - ); + $modifiers = $this->getModifiersFromRequest($request); + + return $this->json($repository->all(...$modifiers)); + } + + private function getModifiersFromRequest(Request $request) + { + if ($request->query->has('stop')) { + $stop = $request->query->get('stop'); + $stop = Stop::reference($stop); + + yield new RelatedFilter($stop); + } + + if ($request->query->has('line')) { + $line = $request->query->get('line'); + $line = Line::reference($line); + + yield new RelatedFilter($line); + } + + if ($request->query->has('id')) { + $id = encapsulate($request->query->get('id')); + + yield new IdFilter($id); } } - - private function byId(Request $request, TrackRepository $repository) - { - $id = encapsulate($request->query->get('id')); - - return $this->json($repository->all(new IdFilter($id))); - } - - private function byStop(Request $request, TrackRepository $repository) - { - $stop = $request->query->get('stop'); - $stop = array_map([Stop::class, 'reference'], encapsulate($stop)); - - return $this->json($repository->getByStop($stop)); - } - - private function byLine(Request $request, TrackRepository $repository) - { - $line = $request->query->get('line'); - $line = Line::reference($line); - - return $this->json($repository->all(new RelatedFilter($line))); - } } diff --git a/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php b/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php index cb4627e..6a2683d 100644 --- a/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php +++ b/src/Handler/Database/RelatedFilterDatabaseGenericHandler.php @@ -2,16 +2,15 @@ namespace App\Handler\Database; -use App\Entity\LineEntity; -use App\Entity\ProviderEntity; use App\Event\HandleDatabaseModifierEvent; use App\Event\HandleModifierEvent; use App\Handler\ModifierHandler; use App\Model\Line; -use App\Model\Referable; +use App\Model\Stop; use App\Model\Track; use App\Modifier\RelatedFilter; use App\Service\IdUtils; +use App\Service\ReferenceFactory; use Doctrine\ORM\EntityManagerInterface; use Psr\Container\ContainerInterface; use Symfony\Contracts\Service\ServiceSubscriberInterface; @@ -21,22 +20,25 @@ class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSub protected $mapping = [ Track::class => [ Line::class => 'line', + Stop::class => TrackByStopDatabaseHandler::class, ], ]; - protected $references = [ - Line::class => LineEntity::class, - ]; - private $em; private $inner; private $id; + private $references; - public function __construct(ContainerInterface $inner, EntityManagerInterface $em, IdUtils $idUtils) - { + public function __construct( + ContainerInterface $inner, + EntityManagerInterface $em, + IdUtils $idUtils, + ReferenceFactory $references + ) { $this->inner = $inner; $this->em = $em; $this->id = $idUtils; + $this->references = $references; } public function process(HandleModifierEvent $event) @@ -65,8 +67,15 @@ class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSub $relationship = $this->mapping[$type][$modifier->getRelationship()]; + if ($this->inner->has($relationship)) { + /** @var ModifierHandler $inner */ + $inner = $this->inner->get($relationship); + $inner->process($event); + return; + } + $parameter = sprintf(":%s_%s", $alias, $relationship); - $reference = $this->getEntityReference($modifier->getRelated(), $event->getMeta()['provider']); + $reference = $this->references->create($modifier->getRelated(), $event->getMeta()['provider']); $builder ->join(sprintf('%s.%s', $alias, $relationship), $relationship) @@ -75,22 +84,13 @@ class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSub ; } - // todo: extract that to separate service - private function getEntityReference(Referable $object, ProviderEntity $provider) - { - return $this->em->getReference( - $this->references[get_class($object)], - $this->id->generate($provider, $object->getId()) - ); - } - /** * @inheritDoc */ public static function getSubscribedServices() { return [ - TrackRelatedFilterDatabaseHandler::class, + TrackByStopDatabaseHandler::class, ]; } } diff --git a/src/Handler/Database/TrackByStopDatabaseHandler.php b/src/Handler/Database/TrackByStopDatabaseHandler.php new file mode 100644 index 0000000..12789e5 --- /dev/null +++ b/src/Handler/Database/TrackByStopDatabaseHandler.php @@ -0,0 +1,43 @@ +references = $references; + } + + public function process(HandleModifierEvent $event) + { + if (!$event instanceof HandleDatabaseModifierEvent) { + return; + } + + /** @var RelatedFilter $modifier */ + $modifier = $event->getModifier(); + $builder = $event->getBuilder(); + $alias = $event->getMeta()['alias']; + + $relationship = 'stopsInTrack'; + + $parameter = sprintf(":%s_%s", $alias, $relationship); + $reference = $this->references->create($modifier->getRelated(), $event->getMeta()['provider']); + + $builder + ->join(sprintf("%s.%s", $alias, $relationship), 'stop_in_track') + ->andWhere(sprintf("stop_in_track.stop = %s", $parameter)) + ->setParameter($parameter, $reference) + ; + } +} diff --git a/src/Service/ReferenceFactory.php b/src/Service/ReferenceFactory.php new file mode 100644 index 0000000..3d91cd9 --- /dev/null +++ b/src/Service/ReferenceFactory.php @@ -0,0 +1,42 @@ + LineEntity::class, + Stop::class => StopEntity::class, + ]; + + private $em; + private $id; + + public function __construct(EntityManagerInterface $em, IdUtils $id) + { + $this->em = $em; + $this->id = $id; + } + + public function create(Referable $object, ProviderEntity $provider) + { + $class = get_class($object); + + if (!array_key_exists($class, $this->mapping)) { + throw new \InvalidArgumentException(sprintf("Cannot make entity reference of %s.", $class)); + } + + return $this->em->getReference( + $this->mapping[$class], + $this->id->generate($provider, $object->getId()) + ); + } +} -- 2.45.2