From 6bd71f0ec574ab3e720dc89e08c2ad5725a884d3 Mon Sep 17 00:00:00 2001
From: Kacper Donat <kadet1090@gmail.com>
Date: Wed, 12 Feb 2020 20:08:56 +0100
Subject: [PATCH] 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 @@
+<?php
+
+namespace App\Exception;
+
+use App\Modifiers\Modifier;
+use App\Provider\Repository;
+
+class UnsupportedModifierException extends \Exception
+{
+    public static function createFromModifier(Modifier $modifier, Repository $repository)
+    {
+        return new static(sprintf("Modifier %s is not supported by %s.", get_class($modifier), get_class($repository)));
+    }
+}
diff --git a/src/Handlers/Database/WithIdDatabaseHandler.php b/src/Handlers/Database/IdFilterDatabaseHandler.php
similarity index 86%
rename from src/Handlers/Database/WithIdDatabaseHandler.php
rename to src/Handlers/Database/IdFilterDatabaseHandler.php
index 1cbb324..58a89db 100644
--- a/src/Handlers/Database/WithIdDatabaseHandler.php
+++ b/src/Handlers/Database/IdFilterDatabaseHandler.php
@@ -3,14 +3,13 @@
 namespace App\Handlers\Database;
 
 use App\Handlers\ModifierHandler;
-use App\Modifiers\WithId;
+use App\Modifiers\IdFilter;
 use App\Event\HandleDatabaseModifierEvent;
 use App\Event\HandleModifierEvent;
 use App\Service\IdUtils;
 use function Kadet\Functional\apply;
-use function Kadet\Functional\ref;
 
-class WithIdDatabaseHandler implements ModifierHandler
+class IdFilterDatabaseHandler implements ModifierHandler
 {
     /**
      * @var IdUtils
@@ -28,7 +27,7 @@ class WithIdDatabaseHandler implements ModifierHandler
             return;
         }
 
-        /** @var WithId $modifier */
+        /** @var IdFilter $modifier */
         $modifier = $event->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 [];
+    }
 }