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
App\:
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
# as action arguments even if you don't extend any base controller class
@ -35,6 +35,10 @@ services:
resource: '../src/Provider'
public: true
App\Handler\:
resource: '../src/Handler'
tags: [ app.handler ]
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
@ -90,3 +94,7 @@ services:
# other servces
App\Service\ProviderResolver:
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\Model\Stop;
use App\Model\TrackStop;
use App\Model\StopGroup;
use App\Modifier\IdFilter;
use App\Model\TrackStop;
use App\Modifier\FieldFilter;
use App\Modifier\IncludeDestinations;
use App\Modifier\IdFilter;
use App\Modifier\RelatedFilter;
use App\Modifier\With;
use App\Provider\StopRepository;
use App\Provider\TrackRepository;
use Nelmio\ApiDocBundle\Annotation\Model;
@ -95,7 +95,7 @@ class StopsController extends Controller
*/
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')) {
yield new IncludeDestinations();
yield new With("destinations");
}
}
}

View File

@ -3,12 +3,11 @@
namespace App\Exception;
use App\Modifier\Modifier;
use App\Provider\Repository;
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\IdUtils;
use Doctrine\ORM\EntityManagerInterface;
use Tightenco\Collect\Support\Collection;
use Kadet\Functional as f;
use Kadet\Functional\Transforms as t;
use Tightenco\Collect\Support\Collection;
class IncludeDestinationsDatabaseHandler implements PostProcessingHandler
class WithDestinationsDatabaseHandler implements PostProcessingHandler
{
private $em;
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\Event\HandleDatabaseModifierEvent;
use App\Event\PostProcessEvent;
use App\Exception\UnsupportedModifierException;
use App\Handler\Database\FieldFilterDatabaseHandler;
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\FieldFilter;
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\HandlerProvider;
use App\Service\IdUtils;
use Doctrine\ORM\EntityManagerInterface;
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;
@ -42,7 +40,7 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
/** @var Converter */
protected $converter;
/** @var ContainerInterface */
/** @var HandlerProvider */
protected $handlers;
/**
@ -54,12 +52,19 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
EntityManagerInterface $em,
IdUtils $id,
Converter $converter,
ContainerInterface $handlers
HandlerProvider $handlers
) {
$this->em = $em;
$this->id = $id;
$this->converter = $converter;
$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 */
@ -88,7 +93,7 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
$reducers = [];
foreach ($modifiers as $modifier) {
$handler = $this->getHandler($modifier);
$handler = $this->handlers->get($modifier);
if ($handler instanceof ModifierHandler) {
$event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([
@ -120,10 +125,11 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
$builder->setMaxResults(self::DEFAULT_LIMIT);
$reducers = $this->processQueryBuilder($builder, $modifiers, $meta);
$result = collect($builder->getQuery()->execute())->map(\Closure::fromCallable([$this, 'convert']));
return $reducers->reduce(function ($result, $reducer) {
return $reducer($result);
}, collect($builder->getQuery()->execute())->map(\Closure::fromCallable([$this, 'convert'])));
}, $result);
}
public function first(Modifier ...$modifiers)
@ -131,17 +137,6 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
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:
* [ IdFilter::class => IdFilterDatabaseHandler::class ]
@ -154,17 +149,4 @@ abstract class DatabaseRepository implements ServiceSubscriberInterface, Reposit
{
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;
use App\Entity\StopEntity;
use App\Handler\Database\IncludeDestinationsDatabaseHandler;
use App\Handler\Database\WithDestinationsDatabaseHandler;
use App\Model\Stop;
use App\Modifier\Modifier;
use App\Modifier\IncludeDestinations;
use App\Modifier\With;
use App\Provider\StopRepository;
use Tightenco\Collect\Support\Collection;
@ -30,7 +30,11 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
protected static function 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;
}
}