Add support for multiple related objects filtering

This commit is contained in:
Kacper Donat 2020-02-23 17:12:23 +01:00
parent 45004444e6
commit ee0cde0400
7 changed files with 60 additions and 22 deletions

View File

@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use function App\Functions\encapsulate;
use function Kadet\Functional\ref;
/**
* @Route("/tracks")
@ -51,17 +52,17 @@ class TracksController extends Controller
private function getModifiersFromRequest(Request $request)
{
if ($request->query->has('stop')) {
$stop = $request->query->get('stop');
$stop = Stop::reference($stop);
$stop = encapsulate($request->query->get('stop'));
$stop = collect($stop)->map([Stop::class, 'reference']);
yield new RelatedFilter($stop);
yield new RelatedFilter($stop, Stop::class);
}
if ($request->query->has('line')) {
$line = $request->query->get('line');
$line = Line::reference($line);
$line = encapsulate($request->query->get('line'));
$line = collect($line)->map([Line::class, 'reference']);
yield new RelatedFilter($line);
yield new RelatedFilter($line, Line::class);
}
if ($request->query->has('id')) {
@ -74,8 +75,8 @@ class TracksController extends Controller
private function getStopsModifiersFromRequest(Request $request)
{
if ($request->query->has('stop')) {
$stop = $request->query->get('stop');
$stop = Stop::reference($stop);
$stop = encapsulate($request->query->get('stop'));
$stop = collect($stop)->map(ref([Stop::class, 'reference']));
yield new RelatedFilter($stop);
}

View File

@ -2,11 +2,11 @@
namespace App\Exception;
class InvalidOptionException extends \InvalidArgumentException
class InvalidArgumentException extends \InvalidArgumentException
{
public static function invalidType($parameter, $value, array $expected = [])
{
return new \InvalidArgumentException(
return new static(
sprintf('Expected %s to be of type: %s. %s given.', $parameter, implode(', ', $expected), gettype($value))
);
}

View File

@ -85,7 +85,7 @@ class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSub
$builder
->join(sprintf('%s.%s', $alias, $relationship), $relationship)
->andWhere(sprintf("%s = %s", $relationship, $parameter))
->andWhere(sprintf($modifier->isMultiple() ? "%s in (%s)" : "%s = %s", $relationship, $parameter))
->setParameter($parameter, $reference);
}

View File

@ -34,9 +34,11 @@ class TrackByStopDatabaseHandler implements ModifierHandler
$parameter = sprintf(":%s_%s", $alias, $relationship);
$reference = $this->references->create($modifier->getRelated(), $event->getMeta()['provider']);
$condition = $modifier->isMultiple() ? 'stop_in_track.stop IN (%s)' : 'stop_in_track.stop = %s';
$builder
->join(sprintf("%s.%s", $alias, $relationship), 'stop_in_track')
->andWhere(sprintf("stop_in_track.stop = %s", $parameter))
->andWhere(sprintf($condition, $parameter))
->setParameter($parameter, $reference)
;
}

View File

@ -2,8 +2,9 @@
namespace App\Modifier;
use App\Exception\InvalidOptionException;
use App\Exception\InvalidArgumentException;
use App\Modifier\Modifier;
use App\Service\IterableUtils;
class IdFilter implements Modifier
{
@ -13,10 +14,10 @@ class IdFilter implements Modifier
public function __construct($id)
{
if (!is_iterable($id) && !is_string($id)) {
throw InvalidOptionException::invalidType('id', $id, ['string', 'array']);
throw InvalidArgumentException::invalidType('id', $id, ['string', 'array']);
}
$this->id = $id instanceof \Traversable ? iterator_to_array($id) : $id;
$this->id = is_iterable($id) ? IterableUtils::toArray($id) : $id;
}
public function getId()

View File

@ -2,17 +2,23 @@
namespace App\Modifier;
use App\Exception\InvalidArgumentException;
use App\Model\Referable;
use App\Service\IterableUtils;
class RelatedFilter implements Modifier
{
private $relationship;
private $object;
private $reference;
public function __construct(Referable $object, ?string $relation = null)
public function __construct($reference, ?string $relation = null)
{
$this->object = $object;
$this->relationship = $relation ?: get_class($object);
if (!is_iterable($reference) && !$reference instanceof Referable) {
throw InvalidArgumentException::invalidType('object', $reference, [Referable::class, 'iterable']);
}
$this->reference = is_iterable($reference) ? IterableUtils::toArray($reference) : $reference;
$this->relationship = $relation ?: get_class($reference);
}
public function getRelationship(): string
@ -20,8 +26,13 @@ class RelatedFilter implements Modifier
return $this->relationship;
}
public function getRelated(): Referable
public function getRelated()
{
return $this->object;
return $this->reference;
}
public function isMultiple()
{
return is_array($this->reference);
}
}

View File

@ -6,11 +6,16 @@ use App\Entity\LineEntity;
use App\Entity\ProviderEntity;
use App\Entity\StopEntity;
use App\Entity\TrackEntity;
use App\Exception\InvalidArgumentException;
use App\Model\Line;
use App\Model\Referable;
use App\Model\Stop;
use App\Model\Track;
use Doctrine\ORM\EntityManagerInterface;
use Tightenco\Collect\Support\Collection;
use function Kadet\Functional\partial;
use function Kadet\Functional\ref;
use const Kadet\Functional\_;
final class EntityReferenceFactory
{
@ -29,7 +34,25 @@ final class EntityReferenceFactory
$this->id = $id;
}
public function create(Referable $object, ProviderEntity $provider)
public function create($object, ProviderEntity $provider)
{
switch (true) {
case $object instanceof Referable:
return $this->createEntityReference($object, $provider);
case is_array($object):
return array_map(partial(ref([$this, 'createEntityReference']), _, $provider), $object);
case $object instanceof Collection:
return $object->map(partial(ref([$this, 'createEntityReference']), _, $provider));
default:
throw InvalidArgumentException::invalidType(
'object',
$object,
[Referable::class, Collection::class, 'array']
);
}
}
private function createEntityReference(Referable $object, ProviderEntity $provider)
{
$class = get_class($object);