Rewrite Track Repository into fluent pattern

This commit is contained in:
Kacper Donat 2020-02-17 21:48:00 +01:00
parent 950e310096
commit a3de2b244f
12 changed files with 177 additions and 73 deletions

View File

@ -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;

View File

@ -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)));
}
}
}

View File

@ -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)
;
}

View File

@ -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));
;
}

View File

@ -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

View File

@ -0,0 +1,96 @@
<?php
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\Track;
use App\Modifier\RelatedFilter;
use App\Service\IdUtils;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Container\ContainerInterface;
use Symfony\Contracts\Service\ServiceSubscriberInterface;
class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSubscriberInterface
{
protected $mapping = [
Track::class => [
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,
];
}
}

View File

@ -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);
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Modifier;
use App\Model\Referable;
class RelatedFilter implements Modifier
{
private $relationship;
private $object;
public function __construct(Referable $object, ?string $relation = null)
{
$this->object = $object;
$this->relationship = $relation ?: get_class($object);
}
public function getRelationship(): string
{
return $this->relationship;
}
public function getRelated(): Referable
{
return $this->object;
}
}

View File

@ -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());
}
}

View File

@ -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,
]);
}
}

View File

@ -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,
]);

View File

@ -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;
}
}