From 9d0e4fdb2a3f1682d1837f3305023795138c58b1 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Mon, 17 Feb 2020 21:48:00 +0100 Subject: [PATCH] #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 +}