Extract HandlerProvider to own class

This commit is contained in:
Kacper Donat 2020-03-14 17:19:43 +01:00
parent 87255eaf13
commit 7ffd3c02cd
9 changed files with 103 additions and 55 deletions

View File

@ -23,7 +23,7 @@ services:
# this creates a service per class whose id is the fully-qualified class name # this creates a service per class whose id is the fully-qualified class name
App\: App\:
resource: '../src/*' resource: '../src/*'
exclude: '../src/{DependencyInjection,Exception,Modifiers,Entity,Model,Migrations,Tests,Functions,Kernel.php}' exclude: '../src/{DependencyInjection,Exception,Modifie,Entity,Model,Migrations,Tests,Functions,Handler,Kernel.php}'
# controllers are imported separately to make sure services can be injected # controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class # as action arguments even if you don't extend any base controller class
@ -35,6 +35,10 @@ services:
resource: '../src/Provider' resource: '../src/Provider'
public: true public: true
App\Handler\:
resource: '../src/Handler'
tags: [ app.handler ]
# add more service definitions when explicit configuration is needed # add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones # please note that last definitions always *replace* previous ones
@ -90,3 +94,7 @@ services:
# other servces # other servces
App\Service\ProviderResolver: App\Service\ProviderResolver:
arguments: [!tagged app.provider, '%kernel.debug%'] arguments: [!tagged app.provider, '%kernel.debug%']
App\Service\HandlerProvider:
arguments: [!tagged_locator app.handler]
shared: false

View File

@ -4,12 +4,12 @@ namespace App\Controller\Api\v1;
use App\Controller\Controller; use App\Controller\Controller;
use App\Model\Stop; use App\Model\Stop;
use App\Model\TrackStop;
use App\Model\StopGroup; use App\Model\StopGroup;
use App\Modifier\IdFilter; use App\Model\TrackStop;
use App\Modifier\FieldFilter; use App\Modifier\FieldFilter;
use App\Modifier\IncludeDestinations; use App\Modifier\IdFilter;
use App\Modifier\RelatedFilter; use App\Modifier\RelatedFilter;
use App\Modifier\With;
use App\Provider\StopRepository; use App\Provider\StopRepository;
use App\Provider\TrackRepository; use App\Provider\TrackRepository;
use Nelmio\ApiDocBundle\Annotation\Model; use Nelmio\ApiDocBundle\Annotation\Model;
@ -95,7 +95,7 @@ class StopsController extends Controller
*/ */
public function one(Request $request, StopRepository $stops, $id) public function one(Request $request, StopRepository $stops, $id)
{ {
return $this->json($stops->first(new IdFilter($id), new IncludeDestinations())); return $this->json($stops->first(new IdFilter($id), new With("destinations")));
} }
/** /**
@ -137,7 +137,7 @@ class StopsController extends Controller
} }
if ($request->query->has('include-destinations')) { if ($request->query->has('include-destinations')) {
yield new IncludeDestinations(); yield new With("destinations");
} }
} }
} }

View File

@ -3,12 +3,11 @@
namespace App\Exception; namespace App\Exception;
use App\Modifier\Modifier; use App\Modifier\Modifier;
use App\Provider\Repository;
class UnsupportedModifierException extends \LogicException class UnsupportedModifierException extends \LogicException
{ {
public static function createFromModifier(Modifier $modifier, Repository $repository) public static function createFromModifier(Modifier $modifier)
{ {
return new static(sprintf("Modifier %s is not supported by %s.", get_class($modifier), get_class($repository))); return new static(sprintf("Modifier %s is not supported.", get_class($modifier)));
} }
} }

View File

@ -10,11 +10,11 @@ use App\Model\Stop;
use App\Service\Converter; use App\Service\Converter;
use App\Service\IdUtils; use App\Service\IdUtils;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Tightenco\Collect\Support\Collection;
use Kadet\Functional as f; use Kadet\Functional as f;
use Kadet\Functional\Transforms as t; use Kadet\Functional\Transforms as t;
use Tightenco\Collect\Support\Collection;
class IncludeDestinationsDatabaseHandler implements PostProcessingHandler class WithDestinationsDatabaseHandler implements PostProcessingHandler
{ {
private $em; private $em;
private $converter; private $converter;

View File

@ -1,7 +0,0 @@
<?php
namespace App\Modifier;
class IncludeDestinations implements Modifier
{
}

18
src/Modifier/With.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace App\Modifier;
class With implements Modifier
{
private $relationship;
public function __construct(string $relationship)
{
$this->relationship = $relationship;
}
public function getRelationship(): string
{
return $this->relationship;
}
}

View File

@ -5,28 +5,26 @@ namespace App\Provider\Database;
use App\Entity\ProviderEntity; use App\Entity\ProviderEntity;
use App\Event\HandleDatabaseModifierEvent; use App\Event\HandleDatabaseModifierEvent;
use App\Event\PostProcessEvent; use App\Event\PostProcessEvent;
use App\Exception\UnsupportedModifierException; use App\Handler\Database\FieldFilterDatabaseHandler;
use App\Handler\Database\IdFilterDatabaseHandler; use App\Handler\Database\IdFilterDatabaseHandler;
use App\Handler\Database\LimitDatabaseHandler; use App\Handler\Database\LimitDatabaseHandler;
use App\Handler\Database\FieldFilterDatabaseHandler;
use App\Handler\Database\RelatedFilterDatabaseGenericHandler; use App\Handler\Database\RelatedFilterDatabaseGenericHandler;
use App\Handler\ModifierHandler; use App\Handler\ModifierHandler;
use App\Handler\PostProcessingHandler; use App\Handler\PostProcessingHandler;
use App\Model\Referable; use App\Model\Referable;
use App\Modifier\FieldFilter;
use App\Modifier\IdFilter; use App\Modifier\IdFilter;
use App\Modifier\Limit; use App\Modifier\Limit;
use App\Modifier\Modifier; use App\Modifier\Modifier;
use App\Modifier\FieldFilter;
use App\Modifier\RelatedFilter; use App\Modifier\RelatedFilter;
use App\Provider\Repository; use App\Provider\Repository;
use App\Service\Converter; use App\Service\Converter;
use App\Service\HandlerProvider;
use App\Service\IdUtils; use App\Service\IdUtils;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
abstract class DatabaseRepository implements ServiceSubscriberInterface, Repository abstract class DatabaseRepository implements Repository
{ {
const DEFAULT_LIMIT = 100; const DEFAULT_LIMIT = 100;
@ -42,7 +40,7 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
/** @var Converter */ /** @var Converter */
protected $converter; protected $converter;
/** @var ContainerInterface */ /** @var HandlerProvider */
protected $handlers; protected $handlers;
/** /**
@ -54,12 +52,19 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
EntityManagerInterface $em, EntityManagerInterface $em,
IdUtils $id, IdUtils $id,
Converter $converter, Converter $converter,
ContainerInterface $handlers HandlerProvider $handlers
) { ) {
$this->em = $em; $this->em = $em;
$this->id = $id; $this->id = $id;
$this->converter = $converter; $this->converter = $converter;
$this->handlers = $handlers; $this->handlers = $handlers;
$this->handlers->loadConfiguration(array_merge([
IdFilter::class => IdFilterDatabaseHandler::class,
Limit::class => LimitDatabaseHandler::class,
FieldFilter::class => FieldFilterDatabaseHandler::class,
RelatedFilter::class => RelatedFilterDatabaseGenericHandler::class,
], static::getHandlers()));
} }
/** @return static */ /** @return static */
@ -88,7 +93,7 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
$reducers = []; $reducers = [];
foreach ($modifiers as $modifier) { foreach ($modifiers as $modifier) {
$handler = $this->getHandler($modifier); $handler = $this->handlers->get($modifier);
if ($handler instanceof ModifierHandler) { if ($handler instanceof ModifierHandler) {
$event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([ $event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([
@ -120,10 +125,11 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
$builder->setMaxResults(self::DEFAULT_LIMIT); $builder->setMaxResults(self::DEFAULT_LIMIT);
$reducers = $this->processQueryBuilder($builder, $modifiers, $meta); $reducers = $this->processQueryBuilder($builder, $modifiers, $meta);
$result = collect($builder->getQuery()->execute())->map(\Closure::fromCallable([$this, 'convert']));
return $reducers->reduce(function ($result, $reducer) { return $reducers->reduce(function ($result, $reducer) {
return $reducer($result); return $reducer($result);
}, collect($builder->getQuery()->execute())->map(\Closure::fromCallable([$this, 'convert']))); }, $result);
} }
public function first(Modifier ...$modifiers) public function first(Modifier ...$modifiers)
@ -131,17 +137,6 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
return $this->all(Limit::count(1), ...$modifiers)->first(); 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);
}
/** /**
* Returns array describing handlers for each modifier type. Syntax is as follows: * Returns array describing handlers for each modifier type. Syntax is as follows:
* [ IdFilter::class => IdFilterDatabaseHandler::class ] * [ IdFilter::class => IdFilterDatabaseHandler::class ]
@ -154,17 +149,4 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
{ {
return []; return [];
} }
/**
* @inheritDoc
*/
public static function getSubscribedServices()
{
return array_merge([
IdFilter::class => IdFilterDatabaseHandler::class,
Limit::class => LimitDatabaseHandler::class,
FieldFilter::class => FieldFilterDatabaseHandler::class,
RelatedFilter::class => RelatedFilterDatabaseGenericHandler::class,
], static::getHandlers());
}
} }

View File

@ -3,10 +3,10 @@
namespace App\Provider\Database; namespace App\Provider\Database;
use App\Entity\StopEntity; use App\Entity\StopEntity;
use App\Handler\Database\IncludeDestinationsDatabaseHandler; use App\Handler\Database\WithDestinationsDatabaseHandler;
use App\Model\Stop; use App\Model\Stop;
use App\Modifier\Modifier; use App\Modifier\Modifier;
use App\Modifier\IncludeDestinations; use App\Modifier\With;
use App\Provider\StopRepository; use App\Provider\StopRepository;
use Tightenco\Collect\Support\Collection; use Tightenco\Collect\Support\Collection;
@ -30,7 +30,11 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
protected static function getHandlers() protected static function getHandlers()
{ {
return array_merge(parent::getHandlers(), [ return array_merge(parent::getHandlers(), [
IncludeDestinations::class => IncludeDestinationsDatabaseHandler::class, With::class => function (With $modifier) {
return $modifier->getRelationship() === 'destinations'
? WithDestinationsDatabaseHandler::class
: GenericWithHandler::class;
},
]); ]);
} }
} }

View File

@ -0,0 +1,44 @@
<?php
namespace App\Service;
use App\Exception\UnsupportedModifierException;
use App\Modifier\Modifier;
use Symfony\Component\DependencyInjection\ServiceLocator;
class HandlerProvider
{
private $configuration = [];
private $handlerLocator;
public function __construct(ServiceLocator $handlerLocator)
{
$this->handlerLocator = $handlerLocator;
}
public function loadConfiguration(array $providers)
{
$this->configuration = $providers;
}
public function get(Modifier $modifier)
{
$class = get_class($modifier);
if (!array_key_exists($class, $this->configuration)) {
throw UnsupportedModifierException::createFromModifier($modifier);
}
$handler = $this->configuration[$class];
if (is_callable($handler)) {
$handler = $handler($modifier);
}
if (is_string($handler)) {
return $this->handlerLocator->get($handler);
}
return $handler;
}
}