Merge branch '35_new_repository_pattern_with_filters_and_modifiers'
This commit is contained in:
commit
ea283a86e7
@ -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,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
|
||||
|
@ -1,4 +1,4 @@
|
||||
version: '2'
|
||||
version: '3.4'
|
||||
|
||||
services:
|
||||
nginx:
|
||||
@ -11,9 +11,15 @@ services:
|
||||
|
||||
php:
|
||||
build: docker/php
|
||||
mem_limit: 2g
|
||||
env_file:
|
||||
- ./docker/php/.env
|
||||
volumes:
|
||||
- ./:/var/www:cached
|
||||
- ./docker/php/log.conf:/usr/local/etc/php-fpm.d/zz-log.conf
|
||||
|
||||
blackfire:
|
||||
image: blackfire/blackfire
|
||||
ports: ["8707"]
|
||||
environment:
|
||||
- BLACKFIRE_SERVER_ID
|
||||
- BLACKFIRE_SERVER_TOKEN
|
||||
|
@ -1,2 +1 @@
|
||||
XDEBUG_CONFIG=remote_host=172.17.0.1 remote_port=9001
|
||||
PHP_IDE_CONFIG=serverName=czydojade
|
||||
|
@ -1,22 +1,36 @@
|
||||
FROM php:7.3-fpm
|
||||
|
||||
ARG XDEBUG_REMOTE_HOST="172.17.0.1"
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends git zip libzip-dev
|
||||
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
# XDebug
|
||||
RUN pecl install xdebug-2.9.0 && docker-php-ext-enable xdebug
|
||||
RUN echo "xdebug.remote_enable = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \
|
||||
echo "xdebug.remote_host = ${XDEBUG_REMOTE_HOST}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini;
|
||||
|
||||
RUN echo "xdebug.remote_enable = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini;
|
||||
RUN echo "date.timezone = Europe/Warsaw" >> /usr/local/etc/php/conf.d/datetime.ini;
|
||||
# Blackfire
|
||||
RUN version=$(php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;") \
|
||||
&& curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/$version \
|
||||
&& mkdir -p /tmp/blackfire \
|
||||
&& tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp/blackfire \
|
||||
&& mv /tmp/blackfire/blackfire-*.so $(php -r "echo ini_get ('extension_dir');")/blackfire.so \
|
||||
&& printf "extension=blackfire.so\nblackfire.agent_socket=tcp://blackfire:8707\n" > $PHP_INI_DIR/conf.d/blackfire.ini \
|
||||
&& rm -rf /tmp/blackfire /tmp/blackfire-probe.tar.gz
|
||||
|
||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
|
||||
RUN php composer-setup.php
|
||||
RUN php -r "unlink('composer-setup.php');"
|
||||
#Composer
|
||||
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||
&& php composer-setup.php \
|
||||
&& php -r "unlink('composer-setup.php');" \
|
||||
&& mv composer.phar /usr/local/bin/composer \
|
||||
&& chmod +x /usr/local/bin/composer
|
||||
|
||||
RUN mv composer.phar /usr/local/bin/composer
|
||||
RUN chmod +x /usr/local/bin/composer
|
||||
# Timezone
|
||||
RUN ln -snf /usr/share/zoneinfo/Europe/Warsaw /etc/localtime
|
||||
RUN echo "date.timezone = Europe/Warsaw" >> /usr/local/etc/php/conf.d/datetime.ini;
|
||||
|
||||
WORKDIR /var/www
|
||||
|
||||
|
@ -80,7 +80,7 @@ export class FinderComponent extends Vue {
|
||||
|
||||
this.state = 'fetching';
|
||||
|
||||
const response = await fetch(urls.prepare(urls.stops.grouped, { name: this.filter }));
|
||||
const response = await fetch(urls.prepare(urls.stops.grouped, { name: this.filter, 'include-destinations': true }));
|
||||
|
||||
if (response.ok) {
|
||||
this.found = (await response.json()).reduce((accumulator, { name, stops }) => Object.assign(accumulator, { [name]: stops }), {});
|
||||
|
@ -8,6 +8,10 @@ export function query(params: UrlParams = { }) {
|
||||
function *simplify(name: string, param: any): IterableIterator<ParamValuePair> {
|
||||
if (typeof param === 'string') {
|
||||
yield [ name, param ];
|
||||
} else if (typeof param === 'boolean') {
|
||||
if (param) {
|
||||
yield [ name, '1' ];
|
||||
}
|
||||
} else if (typeof param === 'number') {
|
||||
yield [ name, param.toString() ];
|
||||
} else if (param instanceof Array) {
|
||||
|
@ -5,6 +5,10 @@ namespace App\Controller\Api\v1;
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Model\Departure;
|
||||
use App\Modifier\FieldFilter;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Modifier\Limit;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\DepartureRepository;
|
||||
use App\Provider\StopRepository;
|
||||
use App\Service\SerializerContextFactory;
|
||||
@ -32,11 +36,11 @@ class DeparturesController extends Controller
|
||||
* @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=Departure::class)))
|
||||
* )
|
||||
*/
|
||||
public function stop(DepartureRepository $departures, StopRepository $stops, $stop)
|
||||
public function stop(DepartureRepository $departures, StopRepository $stops, $stop, Request $request)
|
||||
{
|
||||
$stop = $stops->getById($stop);
|
||||
$stop = $stops->first(new IdFilter($stop));
|
||||
|
||||
return $this->json($departures->getForStop($stop));
|
||||
return $this->json($departures->current(collect($stop), ...$this->getModifiersFromRequest($request)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,16 +68,21 @@ class DeparturesController extends Controller
|
||||
*/
|
||||
public function stops(DepartureRepository $departures, StopRepository $stops, Request $request)
|
||||
{
|
||||
$stops = $stops
|
||||
->getManyById($request->query->get('stop'))
|
||||
->flatMap(ref([ $departures, 'getForStop' ]))
|
||||
->sortBy(property('departure'));
|
||||
$stops = $stops->all(new IdFilter($request->query->get('stop')));
|
||||
$result = $departures->current($stops, ...$this->getModifiersFromRequest($request));
|
||||
|
||||
return $this->json(
|
||||
$stops->values()->slice(0, (int)$request->query->get('limit', 8)),
|
||||
$result->values()->slice(0, (int)$request->query->get('limit', 8)),
|
||||
200,
|
||||
[],
|
||||
$this->serializerContextFactory->create(Departure::class, ['Default'])
|
||||
);
|
||||
}
|
||||
|
||||
private function getModifiersFromRequest(Request $request)
|
||||
{
|
||||
if ($request->query->has('limit')) {
|
||||
yield Limit::count($request->query->getInt('limit'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace App\Controller\Api\v1;
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Model\Stop;
|
||||
use App\Model\Track;
|
||||
use App\Model\StopGroup;
|
||||
use App\Model\TrackStop;
|
||||
use App\Modifier\FieldFilter;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Modifier\RelatedFilter;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\StopRepository;
|
||||
use App\Provider\TrackRepository;
|
||||
use App\Service\Proxy\ReferenceFactory;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Swagger\Annotations as SWG;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@ -38,7 +40,8 @@ class StopsController extends Controller
|
||||
* name="id",
|
||||
* in="query",
|
||||
* type="array",
|
||||
* description="Stop identificators to retrieve at once. Can be used to bulk load data. If not specified will return all data.",
|
||||
* description="Stop identificators to retrieve at once. Can be used to bulk load data. If not specified will
|
||||
* return all data.",
|
||||
* @SWG\Items(type="string")
|
||||
* )
|
||||
*
|
||||
@ -46,16 +49,9 @@ class StopsController extends Controller
|
||||
*/
|
||||
public function index(Request $request, StopRepository $stops)
|
||||
{
|
||||
switch (true) {
|
||||
case $request->query->has('id'):
|
||||
$result = $stops->getManyById($request->query->get('id'));
|
||||
break;
|
||||
$modifiers = $this->getModifiersFromRequest($request);
|
||||
|
||||
default:
|
||||
$result = $stops->getAll();
|
||||
}
|
||||
|
||||
return $this->json($result->all());
|
||||
return $this->json($stops->all(...$modifiers)->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,16 +72,9 @@ class StopsController extends Controller
|
||||
*/
|
||||
public function groups(Request $request, StopRepository $stops)
|
||||
{
|
||||
switch (true) {
|
||||
case $request->query->has('name'):
|
||||
$result = $stops->findByName($request->query->get('name'));
|
||||
break;
|
||||
$modifiers = $this->getModifiersFromRequest($request);
|
||||
|
||||
default:
|
||||
$result = $stops->getAll();
|
||||
}
|
||||
|
||||
return $this->json(static::group($result)->all());
|
||||
return $this->json(static::group($stops->all(...$modifiers))->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +95,7 @@ class StopsController extends Controller
|
||||
*/
|
||||
public function one(Request $request, StopRepository $stops, $id)
|
||||
{
|
||||
return $this->json($stops->getById($id));
|
||||
return $this->json($stops->first(new IdFilter($id), new With("destinations")));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,21 +104,12 @@ class StopsController extends Controller
|
||||
* @SWG\Response(
|
||||
* response=200,
|
||||
* description="Returns specific stop referenced via identificator.",
|
||||
* @SWG\Schema(type="object", properties={
|
||||
* @SWG\Property(property="track", type="object", ref=@Model(type=Track::class)),
|
||||
* @SWG\Property(property="order", type="integer", minimum="0")
|
||||
* })
|
||||
* @SWG\Schema(ref=@Model(type=TrackStop::class))
|
||||
* )
|
||||
*
|
||||
* @SWG\Tag(name="Tracks")
|
||||
*/
|
||||
public function tracks(ReferenceFactory $reference, TrackRepository $tracks, $id)
|
||||
public function tracks(TrackRepository $tracks, $id)
|
||||
{
|
||||
$stop = $reference->get(Stop::class, $id);
|
||||
|
||||
return $this->json($tracks->getByStop($stop)->map(function ($tuple) {
|
||||
return array_combine(['track', 'order'], $tuple);
|
||||
}));
|
||||
return $this->json($tracks->stops(new RelatedFilter(Stop::reference($id))));
|
||||
}
|
||||
|
||||
public static function group(Collection $stops)
|
||||
@ -145,4 +125,19 @@ class StopsController extends Controller
|
||||
return $group;
|
||||
})->values();
|
||||
}
|
||||
|
||||
private function getModifiersFromRequest(Request $request)
|
||||
{
|
||||
if ($request->query->has('name')) {
|
||||
yield FieldFilter::contains('name', $request->query->get('name'));
|
||||
}
|
||||
|
||||
if ($request->query->has('id')) {
|
||||
yield new IdFilter($request->query->get('id'));
|
||||
}
|
||||
|
||||
if ($request->query->has('include-destinations')) {
|
||||
yield new With("destinations");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,18 +3,24 @@
|
||||
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 App\Service\IterableUtils;
|
||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||
use Swagger\Annotations as SWG;
|
||||
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")
|
||||
* @SWG\Tag(name="Tracks")
|
||||
*/
|
||||
class TracksController extends Controller
|
||||
{
|
||||
@ -23,48 +29,69 @@ class TracksController extends Controller
|
||||
* response=200,
|
||||
* description="Returns all tracks for specific provider, e.g. ZTM Gdańsk.",
|
||||
* )
|
||||
* @SWG\Tag(name="Tracks")
|
||||
* @Route("/", methods={"GET"})
|
||||
*/
|
||||
public function index(Request $request, TrackRepository $repository)
|
||||
{
|
||||
switch (true) {
|
||||
case $request->query->has('stop'):
|
||||
return $this->byStop($request, $repository);
|
||||
case $request->query->has('line'):
|
||||
return $this->byLine($request, $repository);
|
||||
case $request->query->has('id'):
|
||||
return $this->byId($request, $repository);
|
||||
default:
|
||||
throw new BadRequestHttpException(
|
||||
sprintf(
|
||||
'At least one parameter of %s must be set.',
|
||||
implode(', ', ['stop', 'line', 'id'])
|
||||
)
|
||||
);
|
||||
}
|
||||
$modifiers = $this->getModifiersFromRequest($request);
|
||||
|
||||
return $this->json($repository->all(...$modifiers));
|
||||
}
|
||||
|
||||
private function byId(Request $request, TrackRepository $repository)
|
||||
/**
|
||||
* @Route("/stops", methods={"GET"})
|
||||
* @Route("/{track}/stops", methods={"GET"})
|
||||
*/
|
||||
public function stops(Request $request, TrackRepository $repository)
|
||||
{
|
||||
$modifiers = $this->getStopsModifiersFromRequest($request);
|
||||
|
||||
return $this->json($repository->stops(...$modifiers));
|
||||
}
|
||||
|
||||
private function getModifiersFromRequest(Request $request)
|
||||
{
|
||||
if ($request->query->has('stop')) {
|
||||
$stop = encapsulate($request->query->get('stop'));
|
||||
$stop = collect($stop)->map([Stop::class, 'reference']);
|
||||
|
||||
yield new RelatedFilter($stop, Stop::class);
|
||||
}
|
||||
|
||||
if ($request->query->has('line')) {
|
||||
$line = encapsulate($request->query->get('line'));
|
||||
$line = collect($line)->map([Line::class, 'reference']);
|
||||
|
||||
yield new RelatedFilter($line, Line::class);
|
||||
}
|
||||
|
||||
if ($request->query->has('id')) {
|
||||
$id = encapsulate($request->query->get('id'));
|
||||
|
||||
return $this->json($repository->getManyById($id));
|
||||
yield new IdFilter($id);
|
||||
}
|
||||
}
|
||||
|
||||
private function byStop(Request $request, TrackRepository $repository)
|
||||
private function getStopsModifiersFromRequest(Request $request)
|
||||
{
|
||||
$stop = $request->query->get('stop');
|
||||
$stop = array_map([Stop::class, 'reference'], encapsulate($stop));
|
||||
if ($request->query->has('stop')) {
|
||||
$stop = encapsulate($request->query->get('stop'));
|
||||
$stop = collect($stop)->map(ref([Stop::class, 'reference']));
|
||||
|
||||
return $this->json($repository->getByStop($stop));
|
||||
yield new RelatedFilter($stop);
|
||||
}
|
||||
|
||||
private function byLine(Request $request, TrackRepository $repository)
|
||||
{
|
||||
$line = $request->query->get('line');
|
||||
$line = array_map([Stop::class, 'reference'], encapsulate($line));
|
||||
if ($request->query->has('track') || $request->attributes->has('track')) {
|
||||
$track = $request->get('track');
|
||||
$track = Track::reference($track);
|
||||
|
||||
return $this->json($repository->getByLine($line));
|
||||
yield new RelatedFilter($track);
|
||||
}
|
||||
|
||||
if ($request->query->has('id')) {
|
||||
$id = encapsulate($request->query->get('id'));
|
||||
|
||||
yield new IdFilter($id);
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ namespace App\Controller\Api\v1;
|
||||
|
||||
use App\Controller\Controller;
|
||||
use App\Model\Trip;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\TripRepository;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
@ -18,7 +20,7 @@ class TripController extends Controller
|
||||
*/
|
||||
public function one($id, TripRepository $repository)
|
||||
{
|
||||
$trip = $repository->getById($id);
|
||||
$trip = $repository->first(new IdFilter($id), new With('schedule'));
|
||||
|
||||
return $this->json($trip, Response::HTTP_OK, [], $this->serializerContextFactory->create(Trip::class));
|
||||
}
|
||||
|
@ -45,16 +45,18 @@ class TrackEntity implements Entity, Fillable
|
||||
|
||||
/**
|
||||
* Stops in track
|
||||
* @var StopInTrack[]|Collection
|
||||
* @ORM\OneToMany(targetEntity=StopInTrack::class, fetch="LAZY", mappedBy="track", cascade={"persist"})
|
||||
*
|
||||
* @var TrackStopEntity[]|Collection
|
||||
* @ORM\OneToMany(targetEntity=TrackStopEntity::class, fetch="LAZY", mappedBy="track", cascade={"persist"})
|
||||
* @ORM\OrderBy({"order": "ASC"})
|
||||
*/
|
||||
private $stopsInTrack;
|
||||
|
||||
/**
|
||||
* Final stop in this track.
|
||||
* @var StopInTrack
|
||||
* @ORM\OneToOne(targetEntity=StopInTrack::class, fetch="LAZY")
|
||||
*
|
||||
* @var TrackStopEntity
|
||||
* @ORM\OneToOne(targetEntity=TrackStopEntity::class, fetch="LAZY")
|
||||
*/
|
||||
private $final;
|
||||
|
||||
@ -114,7 +116,7 @@ class TrackEntity implements Entity, Fillable
|
||||
$this->final = $this->stopsInTrack->last();
|
||||
}
|
||||
|
||||
public function getFinal(): StopInTrack
|
||||
public function getFinal(): TrackStopEntity
|
||||
{
|
||||
return $this->final;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
* @ORM\UniqueConstraint(name="stop_in_track_idx", columns={"stop_id", "track_id", "sequence"})
|
||||
* })
|
||||
*/
|
||||
class StopInTrack implements Fillable, Referable
|
||||
class TrackStopEntity implements Fillable, Referable
|
||||
{
|
||||
use FillTrait, ReferableEntityTrait;
|
||||
|
34
src/Event/HandleDatabaseModifierEvent.php
Normal file
34
src/Event/HandleDatabaseModifierEvent.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\Repository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
class HandleDatabaseModifierEvent extends HandleModifierEvent
|
||||
{
|
||||
private $builder;
|
||||
|
||||
public function __construct(
|
||||
Modifier $modifier,
|
||||
Repository $repository,
|
||||
QueryBuilder $builder,
|
||||
array $meta = []
|
||||
) {
|
||||
parent::__construct($modifier, $repository, $meta);
|
||||
|
||||
$this->builder = $builder;
|
||||
}
|
||||
|
||||
public function getBuilder(): QueryBuilder
|
||||
{
|
||||
return $this->builder;
|
||||
}
|
||||
|
||||
public function replaceBuilder(QueryBuilder $builder): void
|
||||
{
|
||||
$this->builder = $builder;
|
||||
}
|
||||
}
|
35
src/Event/HandleModifierEvent.php
Normal file
35
src/Event/HandleModifierEvent.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\Repository;
|
||||
|
||||
class HandleModifierEvent
|
||||
{
|
||||
private $repository;
|
||||
private $modifier;
|
||||
private $meta = [];
|
||||
|
||||
public function __construct(Modifier $modifier, Repository $repository, array $meta = [])
|
||||
{
|
||||
$this->repository = $repository;
|
||||
$this->modifier = $modifier;
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
public function getModifier(): Modifier
|
||||
{
|
||||
return $this->modifier;
|
||||
}
|
||||
|
||||
public function getRepository()
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
public function getMeta(): array
|
||||
{
|
||||
return $this->meta;
|
||||
}
|
||||
}
|
27
src/Event/PostProcessEvent.php
Normal file
27
src/Event/PostProcessEvent.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Event;
|
||||
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\Repository;
|
||||
|
||||
class PostProcessEvent extends HandleModifierEvent
|
||||
{
|
||||
private $data;
|
||||
|
||||
public function __construct($data, Modifier $modifier, Repository $repository, array $meta = [])
|
||||
{
|
||||
parent::__construct($modifier, $repository, $meta);
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData()
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData($data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
13
src/Exception/InvalidArgumentException.php
Normal file
13
src/Exception/InvalidArgumentException.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
public static function invalidType($parameter, $value, array $expected = [])
|
||||
{
|
||||
return new static(
|
||||
sprintf('Expected %s to be of type: %s. %s given.', $parameter, implode(', ', $expected), gettype($value))
|
||||
);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
namespace App\Exception;
|
||||
|
||||
|
||||
class NonExistentServiceException extends \Exception
|
||||
class NonExistentServiceException extends \LogicException
|
||||
{
|
||||
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
class NotSupportedException extends \RuntimeException
|
||||
class NotSupportedException extends \LogicException
|
||||
{
|
||||
}
|
13
src/Exception/UnsupportedModifierException.php
Normal file
13
src/Exception/UnsupportedModifierException.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exception;
|
||||
|
||||
use App\Modifier\Modifier;
|
||||
|
||||
class UnsupportedModifierException extends \LogicException
|
||||
{
|
||||
public static function createFromModifier(Modifier $modifier)
|
||||
{
|
||||
return new static(sprintf("Modifier %s is not supported.", get_class($modifier)));
|
||||
}
|
||||
}
|
@ -13,3 +13,9 @@ function encapsulate($value)
|
||||
return [ $value ];
|
||||
}
|
||||
}
|
||||
|
||||
function setup($value, $callback)
|
||||
{
|
||||
$callback($value);
|
||||
return $value;
|
||||
}
|
||||
|
63
src/Handler/Database/FieldFilterDatabaseHandler.php
Normal file
63
src/Handler/Database/FieldFilterDatabaseHandler.php
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\Stop;
|
||||
use App\Modifier\FieldFilter;
|
||||
use function App\Functions\encapsulate;
|
||||
|
||||
class FieldFilterDatabaseHandler implements ModifierHandler
|
||||
{
|
||||
protected $mapping = [
|
||||
Stop::class => [
|
||||
'name' => 'name',
|
||||
],
|
||||
ScheduledStop::class => [
|
||||
'departure' => 'departure',
|
||||
'arrival' => 'arrival',
|
||||
]
|
||||
];
|
||||
|
||||
public function process(HandleModifierEvent $event)
|
||||
{
|
||||
if (!$event instanceof HandleDatabaseModifierEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var FieldFilter $modifier */
|
||||
$modifier = $event->getModifier();
|
||||
$builder = $event->getBuilder();
|
||||
$alias = $event->getMeta()['alias'];
|
||||
|
||||
$field = $this->mapFieldName($event->getMeta()['type'], $modifier->getField());
|
||||
$operator = $modifier->getOperator();
|
||||
$value = $modifier->getValue();
|
||||
|
||||
$parameter = sprintf(":%s_%s", $alias, $field);
|
||||
|
||||
if ($operator === 'in' || $operator === 'not in') {
|
||||
$parameter = "($parameter)";
|
||||
$value = encapsulate($value);
|
||||
}
|
||||
|
||||
$builder
|
||||
->andWhere(sprintf("%s.%s %s %s", $alias, $field, $operator, $parameter))
|
||||
->setParameter($parameter, $value)
|
||||
;
|
||||
}
|
||||
|
||||
protected function mapFieldName(string $class, string $field)
|
||||
{
|
||||
if (!isset($this->mapping[$class][$field])) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Unable to map field %s of %s into entity field.", $field, $class)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->mapping[$class][$field];
|
||||
}
|
||||
}
|
103
src/Handler/Database/GenericWithDatabaseHandler.php
Normal file
103
src/Handler/Database/GenericWithDatabaseHandler.php
Normal file
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\Track;
|
||||
use App\Model\TrackStop;
|
||||
use App\Model\Trip;
|
||||
use App\Modifier\RelatedFilter;
|
||||
use App\Service\EntityReferenceFactory;
|
||||
use App\Service\IdUtils;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use function Kadet\Functional\Transforms\property;
|
||||
|
||||
class GenericWithDatabaseHandler implements ModifierHandler
|
||||
{
|
||||
protected $mapping = [
|
||||
Track::class => [
|
||||
'line' => 'line',
|
||||
'stops' => 'stopsInTrack',
|
||||
],
|
||||
Trip::class => [
|
||||
'schedule' => 'stops.stop',
|
||||
],
|
||||
TrackStop::class => [
|
||||
'track' => 'track',
|
||||
],
|
||||
ScheduledStop::class => [
|
||||
'trip' => 'trip',
|
||||
'track' => 'trip.track',
|
||||
'destination' => 'trip.track.final',
|
||||
],
|
||||
];
|
||||
|
||||
private $em;
|
||||
private $id;
|
||||
private $references;
|
||||
|
||||
public function __construct(
|
||||
EntityManagerInterface $em,
|
||||
IdUtils $idUtils,
|
||||
EntityReferenceFactory $references
|
||||
) {
|
||||
$this->em = $em;
|
||||
$this->id = $idUtils;
|
||||
$this->references = $references;
|
||||
}
|
||||
|
||||
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($modifier->getRelationship(), $this->mapping[$type])) {
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf("Relationship %s is not supported for .", $type)
|
||||
);
|
||||
}
|
||||
|
||||
$relationship = $this->mapping[$type][$modifier->getRelationship()];
|
||||
|
||||
foreach ($this->getRelationships($relationship, $alias) as [$relationshipPath, $relationshipAlias]) {
|
||||
$selected = collect($builder->getDQLPart('select'))->flatMap(property('parts'));
|
||||
|
||||
if ($selected->contains($relationshipAlias)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$builder
|
||||
->join($relationshipPath, $relationshipAlias)
|
||||
->addSelect($relationshipAlias);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return [
|
||||
TrackByStopDatabaseHandler::class,
|
||||
];
|
||||
}
|
||||
|
||||
private function getRelationships($relationship, $alias)
|
||||
{
|
||||
$relationships = explode('.', $relationship);
|
||||
|
||||
foreach ($relationships as $current) {
|
||||
yield [sprintf("%s.%s", $alias, $current), $alias = sprintf('%s_%s', $alias, $current)];
|
||||
}
|
||||
}
|
||||
}
|
44
src/Handler/Database/IdFilterDatabaseHandler.php
Normal file
44
src/Handler/Database/IdFilterDatabaseHandler.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Service\IdUtils;
|
||||
use function Kadet\Functional\apply;
|
||||
|
||||
class IdFilterDatabaseHandler implements ModifierHandler
|
||||
{
|
||||
/**
|
||||
* @var IdUtils
|
||||
*/
|
||||
private $id;
|
||||
|
||||
public function __construct(IdUtils $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function process(HandleModifierEvent $event)
|
||||
{
|
||||
if (!$event instanceof HandleDatabaseModifierEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var IdFilter $modifier */
|
||||
$modifier = $event->getModifier();
|
||||
$builder = $event->getBuilder();
|
||||
$alias = $event->getMeta()['alias'];
|
||||
$provider = $event->getMeta()['provider'];
|
||||
|
||||
$id = $modifier->getId();
|
||||
$mapper = apply([$this->id, 'generate'], $provider);
|
||||
|
||||
$builder
|
||||
->andWhere($modifier->isMultiple() ? "{$alias} in (:id)" : "{$alias} = :id")
|
||||
->setParameter(':id', $modifier->isMultiple() ? array_map($mapper, $id) : $mapper($id));
|
||||
;
|
||||
}
|
||||
}
|
27
src/Handler/Database/LimitDatabaseHandler.php
Normal file
27
src/Handler/Database/LimitDatabaseHandler.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Modifier\Limit;
|
||||
|
||||
class LimitDatabaseHandler implements ModifierHandler
|
||||
{
|
||||
public function process(HandleModifierEvent $event)
|
||||
{
|
||||
if (!$event instanceof HandleDatabaseModifierEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Limit $modifier */
|
||||
$modifier = $event->getModifier();
|
||||
$builder = $event->getBuilder();
|
||||
|
||||
$builder
|
||||
->setFirstResult($modifier->getOffset())
|
||||
->setMaxResults($modifier->getCount())
|
||||
;
|
||||
}
|
||||
}
|
107
src/Handler/Database/RelatedFilterDatabaseGenericHandler.php
Normal file
107
src/Handler/Database/RelatedFilterDatabaseGenericHandler.php
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Model\Line;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\Stop;
|
||||
use App\Model\Track;
|
||||
use App\Model\TrackStop;
|
||||
use App\Model\Trip;
|
||||
use App\Modifier\RelatedFilter;
|
||||
use App\Service\IdUtils;
|
||||
use App\Service\EntityReferenceFactory;
|
||||
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',
|
||||
Stop::class => TrackByStopDatabaseHandler::class,
|
||||
],
|
||||
TrackStop::class => [
|
||||
Stop::class => 'stop',
|
||||
Track::class => 'track',
|
||||
],
|
||||
ScheduledStop::class => [
|
||||
Stop::class => 'stop',
|
||||
Trip::class => 'trip',
|
||||
],
|
||||
];
|
||||
|
||||
private $em;
|
||||
private $inner;
|
||||
private $id;
|
||||
private $references;
|
||||
|
||||
public function __construct(
|
||||
ContainerInterface $inner,
|
||||
EntityManagerInterface $em,
|
||||
IdUtils $idUtils,
|
||||
EntityReferenceFactory $references
|
||||
) {
|
||||
$this->inner = $inner;
|
||||
$this->em = $em;
|
||||
$this->id = $idUtils;
|
||||
$this->references = $references;
|
||||
}
|
||||
|
||||
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 %s.", $modifier->getRelationship(), $type)
|
||||
);
|
||||
}
|
||||
|
||||
$relationship = $this->mapping[$type][$modifier->getRelationship()];
|
||||
|
||||
if ($this->inner->has($relationship)) {
|
||||
/** @var ModifierHandler $inner */
|
||||
$inner = $this->inner->get($relationship);
|
||||
$inner->process($event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$parameter = sprintf(":%s_%s", $alias, $relationship);
|
||||
$reference = $this->references->create($modifier->getRelated(), $event->getMeta()['provider']);
|
||||
|
||||
$builder
|
||||
->join(sprintf('%s.%s', $alias, $relationship), $relationship)
|
||||
->andWhere(sprintf($modifier->isMultiple() ? "%s in (%s)" : "%s = %s", $relationship, $parameter))
|
||||
->setParameter($parameter, $reference);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function getSubscribedServices()
|
||||
{
|
||||
return [
|
||||
TrackByStopDatabaseHandler::class,
|
||||
];
|
||||
}
|
||||
}
|
45
src/Handler/Database/TrackByStopDatabaseHandler.php
Normal file
45
src/Handler/Database/TrackByStopDatabaseHandler.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Entity\TrackStopEntity;
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\HandleModifierEvent;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Modifier\RelatedFilter;
|
||||
use App\Service\EntityReferenceFactory;
|
||||
|
||||
class TrackByStopDatabaseHandler implements ModifierHandler
|
||||
{
|
||||
private $references;
|
||||
|
||||
public function __construct(EntityReferenceFactory $references)
|
||||
{
|
||||
$this->references = $references;
|
||||
}
|
||||
|
||||
public function process(HandleModifierEvent $event)
|
||||
{
|
||||
if (!$event instanceof HandleDatabaseModifierEvent) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var RelatedFilter $modifier */
|
||||
$modifier = $event->getModifier();
|
||||
$builder = $event->getBuilder();
|
||||
$alias = $event->getMeta()['alias'];
|
||||
|
||||
$relationship = 'stopsInTrack';
|
||||
|
||||
$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($condition, $parameter))
|
||||
->setParameter($parameter, $reference)
|
||||
;
|
||||
}
|
||||
}
|
76
src/Handler/Database/WithDestinationsDatabaseHandler.php
Normal file
76
src/Handler/Database/WithDestinationsDatabaseHandler.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler\Database;
|
||||
|
||||
use App\Entity\TrackEntity;
|
||||
use App\Event\PostProcessEvent;
|
||||
use App\Handler\PostProcessingHandler;
|
||||
use App\Model\Destination;
|
||||
use App\Model\Stop;
|
||||
use App\Service\Converter;
|
||||
use App\Service\IdUtils;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Kadet\Functional as f;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class WithDestinationsDatabaseHandler implements PostProcessingHandler
|
||||
{
|
||||
private $em;
|
||||
private $converter;
|
||||
private $id;
|
||||
|
||||
public function __construct(EntityManagerInterface $entityManager, Converter $converter, IdUtils $id)
|
||||
{
|
||||
$this->em = $entityManager;
|
||||
$this->converter = $converter;
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
public function postProcess(PostProcessEvent $event)
|
||||
{
|
||||
$provider = $event->getMeta()['provider'];
|
||||
$stops = $event
|
||||
->getData()
|
||||
->map(t\property('id'))
|
||||
->map(f\apply([$this->id, 'generate'], $provider))
|
||||
->all();
|
||||
|
||||
$destinations = collect($this->em->createQueryBuilder()
|
||||
->select('t', 'tl', 'f', 'fs', 'ts')
|
||||
->from(TrackEntity::class, 't')
|
||||
->join('t.stopsInTrack', 'ts')
|
||||
->join('t.line', 'tl')
|
||||
->where('ts.stop IN (:stops)')
|
||||
->join('t.final', 'f')
|
||||
->join('f.stop', 'fs')
|
||||
->getQuery()
|
||||
->execute(['stops' => $stops]))
|
||||
->reduce(function ($grouped, TrackEntity $track) {
|
||||
foreach ($track->getStopsInTrack()->map(t\property('stop'))->map(t\property('id')) as $stop) {
|
||||
$grouped[$stop] = ($grouped[$stop] ?? collect())->add($track);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}, collect())
|
||||
->map(function (Collection $tracks) {
|
||||
return $tracks
|
||||
->groupBy(function (TrackEntity $track) {
|
||||
return $track->getFinal()->getStop()->getId();
|
||||
})->map(function (Collection $tracks, $id) {
|
||||
return Destination::createFromArray([
|
||||
'stop' => $this->converter->convert($tracks->first()->getFinal()->getStop()),
|
||||
'lines' => $tracks
|
||||
->map(t\property('line'))
|
||||
->unique(t\property('id'))
|
||||
->map(f\ref([$this->converter, 'convert']))
|
||||
->values(),
|
||||
]);
|
||||
})->values();
|
||||
});
|
||||
|
||||
$event->getData()->each(function (Stop $stop) use ($provider, $destinations) {
|
||||
$stop->setDestinations($destinations[$this->id->generate($provider, $stop->getId())]);
|
||||
});
|
||||
}
|
||||
}
|
10
src/Handler/ModifierHandler.php
Normal file
10
src/Handler/ModifierHandler.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler;
|
||||
|
||||
use App\Event\HandleModifierEvent;
|
||||
|
||||
interface ModifierHandler
|
||||
{
|
||||
public function process(HandleModifierEvent $event);
|
||||
}
|
10
src/Handler/PostProcessingHandler.php
Normal file
10
src/Handler/PostProcessingHandler.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handler;
|
||||
|
||||
use App\Event\PostProcessEvent;
|
||||
|
||||
interface PostProcessingHandler
|
||||
{
|
||||
public function postProcess(PostProcessEvent $event);
|
||||
}
|
@ -4,53 +4,25 @@ namespace App\Model;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ScheduledStop implements Fillable
|
||||
class ScheduledStop extends TrackStop
|
||||
{
|
||||
use FillTrait;
|
||||
|
||||
/**
|
||||
* Stop (as a place) related to that scheduled bus stop
|
||||
* @var Stop
|
||||
*/
|
||||
private $stop;
|
||||
|
||||
/**
|
||||
* Order in trip
|
||||
* @var int
|
||||
*/
|
||||
private $order;
|
||||
|
||||
/**
|
||||
* Arrival time
|
||||
* Arrival time.
|
||||
* @var Carbon
|
||||
*/
|
||||
private $arrival;
|
||||
|
||||
/**
|
||||
* Departure time
|
||||
* Departure time.
|
||||
* @var Carbon
|
||||
*/
|
||||
private $departure;
|
||||
|
||||
public function getStop()
|
||||
{
|
||||
return $this->stop;
|
||||
}
|
||||
|
||||
public function setStop($stop): void
|
||||
{
|
||||
$this->stop = $stop;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function setOrder(int $order): void
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
/**
|
||||
* Exact trip that this scheduled stop is part of.
|
||||
* @var Trip|null
|
||||
*/
|
||||
private $trip;
|
||||
|
||||
public function getArrival(): Carbon
|
||||
{
|
||||
@ -71,4 +43,14 @@ class ScheduledStop implements Fillable
|
||||
{
|
||||
$this->departure = $departure;
|
||||
}
|
||||
|
||||
public function getTrip(): ?Trip
|
||||
{
|
||||
return $this->trip;
|
||||
}
|
||||
|
||||
public function setTrip(?Trip $trip): void
|
||||
{
|
||||
$this->trip = $trip;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,14 @@ class Track implements Referable, Fillable
|
||||
*/
|
||||
private $stops;
|
||||
|
||||
/**
|
||||
* Destination stop of this track
|
||||
* @var Stop|null
|
||||
* @Serializer\Type(Stop::class)
|
||||
* @SWG\Property(ref=@Model(type=Stop::class))
|
||||
*/
|
||||
private $destination;
|
||||
|
||||
/**
|
||||
* Track constructor.
|
||||
*/
|
||||
@ -89,4 +97,14 @@ class Track implements Referable, Fillable
|
||||
{
|
||||
return $this->stops = collect($stops);
|
||||
}
|
||||
|
||||
public function getDestination(): ?Stop
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
public function setDestination(?Stop $destination): void
|
||||
{
|
||||
$this->destination = $destination;
|
||||
}
|
||||
}
|
58
src/Model/TrackStop.php
Normal file
58
src/Model/TrackStop.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use JMS\Serializer\Annotation as Serializer;
|
||||
|
||||
class TrackStop implements Fillable
|
||||
{
|
||||
use FillTrait;
|
||||
|
||||
/**
|
||||
* Order in trip
|
||||
* @var int
|
||||
*/
|
||||
private $order;
|
||||
|
||||
/**
|
||||
* Stop (as a place) related to that scheduled bus stop
|
||||
* @var Stop
|
||||
*/
|
||||
private $stop;
|
||||
|
||||
/**
|
||||
* Track that this stop is part of.
|
||||
* @var Track|null
|
||||
*/
|
||||
private $track;
|
||||
|
||||
public function getStop()
|
||||
{
|
||||
return $this->stop;
|
||||
}
|
||||
|
||||
public function setStop($stop): void
|
||||
{
|
||||
$this->stop = $stop;
|
||||
}
|
||||
|
||||
public function getOrder(): int
|
||||
{
|
||||
return $this->order;
|
||||
}
|
||||
|
||||
public function setOrder(int $order): void
|
||||
{
|
||||
$this->order = $order;
|
||||
}
|
||||
|
||||
public function getTrack(): ?Track
|
||||
{
|
||||
return $this->track;
|
||||
}
|
||||
|
||||
public function setTrack(?Track $track): void
|
||||
{
|
||||
$this->track = $track;
|
||||
}
|
||||
}
|
@ -40,6 +40,12 @@ class Trip implements Referable, Fillable
|
||||
*/
|
||||
private $schedule;
|
||||
|
||||
/**
|
||||
* Destination stop of this trip
|
||||
* @var Stop|null
|
||||
*/
|
||||
private $destination;
|
||||
|
||||
/**
|
||||
* Track constructor.
|
||||
*/
|
||||
@ -87,4 +93,14 @@ class Trip implements Referable, Fillable
|
||||
{
|
||||
return $this->schedule = collect($schedule);
|
||||
}
|
||||
|
||||
public function getDestination(): ?Stop
|
||||
{
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
public function setDestination(?Stop $destination): void
|
||||
{
|
||||
$this->destination = $destination;
|
||||
}
|
||||
}
|
||||
|
37
src/Modifier/FieldFilter.php
Normal file
37
src/Modifier/FieldFilter.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modifier;
|
||||
|
||||
class FieldFilter implements Modifier
|
||||
{
|
||||
private $field;
|
||||
private $value;
|
||||
private $operator;
|
||||
|
||||
public function __construct(string $field, $value, string $operator = '=')
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->value = $value;
|
||||
$this->operator = $operator;
|
||||
}
|
||||
|
||||
public static function contains(string $field, string $value)
|
||||
{
|
||||
return new static($field, "%$value%", 'LIKE');
|
||||
}
|
||||
|
||||
public function getField(): string
|
||||
{
|
||||
return $this->field;
|
||||
}
|
||||
|
||||
public function getValue()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getOperator(): string
|
||||
{
|
||||
return $this->operator;
|
||||
}
|
||||
}
|
32
src/Modifier/IdFilter.php
Normal file
32
src/Modifier/IdFilter.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modifier;
|
||||
|
||||
use App\Exception\InvalidArgumentException;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Service\IterableUtils;
|
||||
|
||||
class IdFilter implements Modifier
|
||||
{
|
||||
/** @var string|array */
|
||||
private $id;
|
||||
|
||||
public function __construct($id)
|
||||
{
|
||||
if (!is_iterable($id) && !is_string($id)) {
|
||||
throw InvalidArgumentException::invalidType('id', $id, ['string', 'array']);
|
||||
}
|
||||
|
||||
$this->id = is_iterable($id) ? IterableUtils::toArray($id) : $id;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function isMultiple()
|
||||
{
|
||||
return is_array($this->id);
|
||||
}
|
||||
}
|
30
src/Modifier/Limit.php
Normal file
30
src/Modifier/Limit.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modifier;
|
||||
|
||||
class Limit implements Modifier
|
||||
{
|
||||
private $offset;
|
||||
private $count;
|
||||
|
||||
public function __construct(int $offset = 0, ?int $count = null)
|
||||
{
|
||||
$this->offset = $offset;
|
||||
$this->count = $count;
|
||||
}
|
||||
|
||||
public function getOffset()
|
||||
{
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
public function getCount()
|
||||
{
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
public static function count(int $count)
|
||||
{
|
||||
return new static(0, $count);
|
||||
}
|
||||
}
|
8
src/Modifier/Modifier.php
Normal file
8
src/Modifier/Modifier.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modifier;
|
||||
|
||||
interface Modifier
|
||||
{
|
||||
|
||||
}
|
38
src/Modifier/RelatedFilter.php
Normal file
38
src/Modifier/RelatedFilter.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modifier;
|
||||
|
||||
use App\Exception\InvalidArgumentException;
|
||||
use App\Model\Referable;
|
||||
use App\Service\IterableUtils;
|
||||
|
||||
class RelatedFilter implements Modifier
|
||||
{
|
||||
private $relationship;
|
||||
private $reference;
|
||||
|
||||
public function __construct($reference, ?string $relation = null)
|
||||
{
|
||||
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
|
||||
{
|
||||
return $this->relationship;
|
||||
}
|
||||
|
||||
public function getRelated()
|
||||
{
|
||||
return $this->reference;
|
||||
}
|
||||
|
||||
public function isMultiple()
|
||||
{
|
||||
return is_array($this->reference);
|
||||
}
|
||||
}
|
18
src/Modifier/With.php
Normal file
18
src/Modifier/With.php
Normal 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;
|
||||
}
|
||||
}
|
@ -2,16 +2,35 @@
|
||||
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\Entity;
|
||||
use App\Entity\ProviderEntity;
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Event\PostProcessEvent;
|
||||
use App\Handler\Database\FieldFilterDatabaseHandler;
|
||||
use App\Handler\Database\IdFilterDatabaseHandler;
|
||||
use App\Handler\Database\LimitDatabaseHandler;
|
||||
use App\Handler\Database\RelatedFilterDatabaseGenericHandler;
|
||||
use App\Handler\Database\GenericWithDatabaseHandler;
|
||||
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\RelatedFilter;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\Repository;
|
||||
use App\Service\Converter;
|
||||
use App\Service\HandlerProvider;
|
||||
use App\Service\IdUtils;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Kadet\Functional as f;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
|
||||
class DatabaseRepository
|
||||
abstract class DatabaseRepository implements Repository
|
||||
{
|
||||
const DEFAULT_LIMIT = 100;
|
||||
|
||||
/** @var EntityManagerInterface */
|
||||
protected $em;
|
||||
|
||||
@ -24,16 +43,32 @@ class DatabaseRepository
|
||||
/** @var Converter */
|
||||
protected $converter;
|
||||
|
||||
/** @var HandlerProvider */
|
||||
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,
|
||||
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,
|
||||
With::class => GenericWithDatabaseHandler::class,
|
||||
], static::getHandlers()));
|
||||
}
|
||||
|
||||
/** @return static */
|
||||
@ -56,4 +91,69 @@ class DatabaseRepository
|
||||
|
||||
return $this->em->getReference($class, $id);
|
||||
}
|
||||
|
||||
protected function processQueryBuilder(QueryBuilder $builder, iterable $modifiers, array $meta = [])
|
||||
{
|
||||
$reducers = [];
|
||||
|
||||
foreach ($modifiers as $modifier) {
|
||||
$handler = $this->handlers->get($modifier);
|
||||
|
||||
if ($handler instanceof ModifierHandler) {
|
||||
$event = new HandleDatabaseModifierEvent($modifier, $this, $builder, array_merge([
|
||||
'provider' => $this->provider,
|
||||
], $meta));
|
||||
|
||||
$handler->process($event);
|
||||
}
|
||||
|
||||
if ($handler instanceof PostProcessingHandler) {
|
||||
$reducers[] = function ($result) use ($meta, $modifier, $handler) {
|
||||
$event = new PostProcessEvent($result, $modifier, $this, array_merge([
|
||||
'provider' => $this->provider,
|
||||
], $meta));
|
||||
|
||||
$handler->postProcess($event);
|
||||
|
||||
return $event->getData();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return collect($reducers);
|
||||
}
|
||||
|
||||
protected function allFromQueryBuilder(QueryBuilder $builder, iterable $modifiers, array $meta = [])
|
||||
{
|
||||
$builder->setMaxResults(self::DEFAULT_LIMIT);
|
||||
|
||||
$reducers = $this->processQueryBuilder($builder, $modifiers, $meta);
|
||||
$query = $builder->getQuery();
|
||||
|
||||
$paginator = new Paginator($query);
|
||||
$result = collect($paginator)->map(\Closure::fromCallable([$this, 'convert']));
|
||||
|
||||
return $reducers->reduce(function ($result, $reducer) {
|
||||
return $reducer($result);
|
||||
}, $result);
|
||||
}
|
||||
|
||||
public function first(Modifier ...$modifiers)
|
||||
{
|
||||
return $this->all(Limit::count(1), ...$modifiers)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 static function getHandlers()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@ -3,34 +3,32 @@
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\LineEntity;
|
||||
use App\Event\HandleDatabaseModifierEvent;
|
||||
use App\Handler\Database\LimitDatabaseHandler;
|
||||
use App\Handler\Database\IdFilterDatabaseHandler;
|
||||
use App\Handler\ModifierHandler;
|
||||
use App\Model\Line;
|
||||
use App\Modifier\Limit;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Provider\LineRepository;
|
||||
use App\Modifier\Modifier;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
use Kadet\Functional as f;
|
||||
|
||||
class GenericLineRepository extends DatabaseRepository implements LineRepository
|
||||
{
|
||||
public function getAll(): Collection
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$repository = $this->em->getRepository(LineEntity::class);
|
||||
$lines = $repository->findAll();
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(LineEntity::class, 'line')
|
||||
->select('line')
|
||||
;
|
||||
|
||||
return collect($lines)->map(f\ref([$this, 'convert']));
|
||||
}
|
||||
|
||||
public function getById($id): ?Line
|
||||
{
|
||||
$repository = $this->em->getRepository(LineEntity::class);
|
||||
return $this->convert($repository->find($id));
|
||||
}
|
||||
|
||||
public function getManyById($ids): Collection
|
||||
{
|
||||
$ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider));
|
||||
|
||||
$repository = $this->em->getRepository(LineEntity::class);
|
||||
$lines = $repository->findBy(['id' => $ids->all()]);
|
||||
|
||||
return collect($lines)->map(f\ref([$this, 'convert']));
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'line',
|
||||
'entity' => LineEntity::class,
|
||||
'type' => Line::class,
|
||||
]);
|
||||
}
|
||||
}
|
@ -2,32 +2,26 @@
|
||||
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\OperatorEntity;
|
||||
use App\Model\Operator;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\OperatorRepository;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class GenericOperatorRepository extends DatabaseRepository implements OperatorRepository
|
||||
{
|
||||
public function getAll(): Collection
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$repository = $this->em->getRepository(Operator::class);
|
||||
$operators = $repository->findAll();
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(OperatorEntity::class, 'operator')
|
||||
->select('operator')
|
||||
;
|
||||
|
||||
return collect($operators);
|
||||
}
|
||||
|
||||
public function getById($id): ?Operator
|
||||
{
|
||||
$repository = $this->em->getRepository(Operator::class);
|
||||
|
||||
return $repository->find($id);
|
||||
}
|
||||
|
||||
public function getManyById($ids): Collection
|
||||
{
|
||||
$repository = $this->em->getRepository(Operator::class);
|
||||
$operators = $repository->findBy(['id' => $ids]);
|
||||
|
||||
return collect($operators);
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'operator',
|
||||
'entity' => OperatorEntity::class,
|
||||
'type' => Operator::class,
|
||||
]);
|
||||
}
|
||||
}
|
@ -3,14 +3,16 @@
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\StopEntity;
|
||||
use App\Entity\StopInTrack;
|
||||
use App\Entity\TrackStopEntity;
|
||||
use App\Entity\TrackEntity;
|
||||
use App\Entity\TripEntity;
|
||||
use App\Entity\TripStopEntity;
|
||||
use App\Model\Departure;
|
||||
use App\Model\Line;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\Stop;
|
||||
use App\Model\Vehicle;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\ScheduleRepository;
|
||||
use Carbon\Carbon;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
@ -70,4 +72,20 @@ class GenericScheduleRepository extends DatabaseRepository implements ScheduleRe
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->select('trip_stop')
|
||||
->from(TripStopEntity::class, 'trip_stop')
|
||||
->orderBy('trip_stop.departure', 'ASC')
|
||||
;
|
||||
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'trip_stop',
|
||||
'type' => ScheduledStop::class,
|
||||
'entity' => TripStopEntity::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,84 +3,39 @@
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\StopEntity;
|
||||
use App\Entity\TrackEntity;
|
||||
use App\Model\Destination;
|
||||
use App\Handler\Database\GenericWithDatabaseHandler;
|
||||
use App\Handler\Database\WithDestinationsDatabaseHandler;
|
||||
use App\Model\Stop;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\StopRepository;
|
||||
use Kadet\Functional as f;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class GenericStopRepository extends DatabaseRepository implements StopRepository
|
||||
{
|
||||
public function getAll(): Collection
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$stops = $this->em->getRepository(StopEntity::class)->findAll();
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(StopEntity::class, 'stop')
|
||||
->select('stop')
|
||||
;
|
||||
|
||||
return collect($stops)->map(f\ref([$this, 'convert']));
|
||||
}
|
||||
|
||||
public function getById($id): ?Stop
|
||||
{
|
||||
$id = $this->id->generate($this->provider, $id);
|
||||
$stop = $this->em->getRepository(StopEntity::class)->find($id);
|
||||
|
||||
return $this->convert($stop);
|
||||
}
|
||||
|
||||
public function getManyById($ids): Collection
|
||||
{
|
||||
$ids = collect($ids)->map(f\apply(f\ref([$this->id, 'generate']), $this->provider));
|
||||
$stops = $this->em->getRepository(StopEntity::class)->findBy(['id' => $ids->all()]);
|
||||
|
||||
return collect($stops)->map(f\ref([$this, 'convert']));
|
||||
}
|
||||
|
||||
public function findByName(string $name): Collection
|
||||
{
|
||||
$query = $this->em->createQueryBuilder()
|
||||
->select('s')
|
||||
->from(StopEntity::class, 's')
|
||||
->where('s.name LIKE :name')
|
||||
->getQuery();
|
||||
|
||||
$stops = collect($query->execute([':name' => "%$name%"]));
|
||||
|
||||
$destinations = collect($this->em->createQueryBuilder()
|
||||
->select('t', 'tl', 'f', 'fs', 'ts')
|
||||
->from(TrackEntity::class, 't')
|
||||
->join('t.stopsInTrack', 'ts')
|
||||
->join('t.line', 'tl')
|
||||
->where('ts.stop IN (:stops)')
|
||||
->join('t.final', 'f')
|
||||
->join('f.stop', 'fs')
|
||||
->getQuery()
|
||||
->execute(['stops' => $stops->map(t\property('id'))->all()]))
|
||||
->reduce(function ($grouped, TrackEntity $track) {
|
||||
foreach ($track->getStopsInTrack()->map(t\property('stop'))->map(t\property('id')) as $stop) {
|
||||
$grouped[$stop] = ($grouped[$stop] ?? collect())->add($track);
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}, collect())
|
||||
->map(function (Collection $tracks) {
|
||||
return $tracks
|
||||
->groupBy(function (TrackEntity $track) {
|
||||
return $track->getFinal()->getStop()->getId();
|
||||
})->map(function (Collection $tracks, $id) {
|
||||
return Destination::createFromArray([
|
||||
'stop' => $this->convert($tracks->first()->getFinal()->getStop()),
|
||||
'lines' => $tracks
|
||||
->map(t\property('line'))
|
||||
->unique(t\property('id'))
|
||||
->map(f\ref([$this, 'convert']))
|
||||
->values(),
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'stop',
|
||||
'entity' => StopEntity::class,
|
||||
'type' => Stop::class,
|
||||
]);
|
||||
})->values();
|
||||
});
|
||||
}
|
||||
|
||||
return collect($stops)->map(f\ref([$this, 'convert']))->each(function (Stop $stop) use ($destinations) {
|
||||
$stop->setDestinations($destinations[$this->id->generate($this->provider, $stop->getId())]);
|
||||
});
|
||||
protected static function getHandlers()
|
||||
{
|
||||
return array_merge(parent::getHandlers(), [
|
||||
With::class => function (With $modifier) {
|
||||
return $modifier->getRelationship() === 'destinations'
|
||||
? WithDestinationsDatabaseHandler::class
|
||||
: GenericWithDatabaseHandler::class;
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -2,66 +2,41 @@
|
||||
|
||||
namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\LineEntity;
|
||||
use App\Entity\StopEntity;
|
||||
use App\Entity\StopInTrack;
|
||||
use App\Entity\TrackStopEntity;
|
||||
use App\Entity\TrackEntity;
|
||||
use function App\Functions\encapsulate;
|
||||
use App\Model\Stop;
|
||||
use App\Model\TrackStop;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Model\Track;
|
||||
use App\Provider\TrackRepository;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
use Kadet\Functional as f;
|
||||
|
||||
class GenericTrackRepository extends DatabaseRepository implements TrackRepository
|
||||
{
|
||||
public function getAll(): Collection
|
||||
public function stops(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$tracks = $this->em->getRepository(TrackEntity::class)->findAll();
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(TrackStopEntity::class, 'track_stop')
|
||||
->select(['track_stop']);
|
||||
|
||||
return collect($tracks)->map(f\ref([$this, 'convert']));
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'track_stop',
|
||||
'entity' => TrackStopEntity::class,
|
||||
'type' => TrackStop::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getById($id): Track
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
// TODO: Implement getById() method.
|
||||
}
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(TrackEntity::class, 'track')
|
||||
->select('track');
|
||||
|
||||
public function getManyById($ids): Collection
|
||||
{
|
||||
// TODO: Implement getManyById() method.
|
||||
}
|
||||
|
||||
public function getByStop($stop): Collection
|
||||
{
|
||||
$reference = f\apply(f\ref([$this, 'reference']), StopEntity::class);
|
||||
|
||||
$tracks = $this->em->createQueryBuilder()
|
||||
->from(StopInTrack::class, 'st')
|
||||
->join('st.track', 't')
|
||||
->where('st.stop in (:stop)')
|
||||
->select(['st', 't'])
|
||||
->getQuery()
|
||||
->execute(['stop' => array_map($reference, encapsulate($stop))]);
|
||||
|
||||
return collect($tracks)->map(function (StopInTrack $entity) {
|
||||
return [ $this->convert($entity->getTrack()), $entity->getOrder() ];
|
||||
});
|
||||
}
|
||||
|
||||
public function getByLine($line): Collection
|
||||
{
|
||||
$reference = f\apply(f\ref([$this, 'reference']), LineEntity::class);
|
||||
|
||||
$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']));
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'track',
|
||||
'entity' => TrackEntity::class,
|
||||
'type' => Track::class,
|
||||
]);
|
||||
}
|
||||
}
|
@ -4,25 +4,23 @@ namespace App\Provider\Database;
|
||||
|
||||
use App\Entity\TripEntity;
|
||||
use App\Model\Trip;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\TripRepository;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class GenericTripRepository extends DatabaseRepository implements TripRepository
|
||||
{
|
||||
public function getById(string $id): Trip
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
$id = $this->id->generate($this->provider, $id);
|
||||
|
||||
$trip = $this->em
|
||||
$builder = $this->em
|
||||
->createQueryBuilder()
|
||||
->from(TripEntity::class, 't')
|
||||
->join('t.stops', 'ts')
|
||||
->join('ts.stop', 's')
|
||||
->select('t', 'ts')
|
||||
->where('t.id = :id')
|
||||
->getQuery()
|
||||
->setParameter('id', $id)
|
||||
->getOneOrNullResult();
|
||||
->from(TripEntity::class, 'trip')
|
||||
->select('trip');
|
||||
|
||||
return $this->convert($trip);
|
||||
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||
'alias' => 'trip',
|
||||
'entity' => TripEntity::class,
|
||||
'type' => Trip::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -5,9 +5,10 @@ namespace App\Provider;
|
||||
|
||||
|
||||
use App\Model\Stop;
|
||||
use App\Modifier\Modifier;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface DepartureRepository extends Repository
|
||||
{
|
||||
public function getForStop(Stop $stop): Collection;
|
||||
public function current(iterable $stops, Modifier ...$modifiers);
|
||||
}
|
@ -6,6 +6,7 @@ use App\Model\Departure;
|
||||
use App\Model\Line;
|
||||
use App\Model\Stop;
|
||||
use App\Model\Vehicle;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\DepartureRepository;
|
||||
use App\Service\Proxy\ReferenceFactory;
|
||||
use Carbon\Carbon;
|
||||
@ -25,21 +26,21 @@ class DummyDepartureRepository implements DepartureRepository
|
||||
$this->reference = $reference;
|
||||
}
|
||||
|
||||
public function getForStop(Stop $stop): Collection
|
||||
public function current(iterable $stops, Modifier ...$modifiers)
|
||||
{
|
||||
return collect([
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[ 1, Line::TYPE_TRAM, 'lorem ipsum', 2137 ],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
[1, Line::TYPE_TRAM, 'lorem ipsum', 2137],
|
||||
])->map(function ($departure) use ($stop) {
|
||||
list($symbol, $type, $display, $vehicle) = $departure;
|
||||
[$symbol, $type, $display, $vehicle] = $departure;
|
||||
$scheduled = new Carbon();
|
||||
$estimated = (clone $scheduled)->addSeconds(40);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Provider\Dummy;
|
||||
|
||||
use App\Model\Stop;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Provider\StopRepository;
|
||||
use App\Service\Proxy\ReferenceFactory;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
@ -41,4 +42,14 @@ class DummyStopRepository implements StopRepository
|
||||
{
|
||||
return collect();
|
||||
}
|
||||
|
||||
public function first(Modifier ...$modifiers)
|
||||
{
|
||||
// TODO: Implement first() method.
|
||||
}
|
||||
|
||||
public function all(Modifier ...$modifiers): Collection
|
||||
{
|
||||
// TODO: Implement all() method.
|
||||
}
|
||||
}
|
||||
|
12
src/Provider/FluentRepository.php
Normal file
12
src/Provider/FluentRepository.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Provider;
|
||||
|
||||
use App\Modifier\Modifier;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface FluentRepository extends Repository
|
||||
{
|
||||
public function first(Modifier ...$modifiers);
|
||||
public function all(Modifier ...$modifiers): Collection;
|
||||
}
|
@ -3,14 +3,6 @@
|
||||
|
||||
namespace App\Provider;
|
||||
|
||||
|
||||
use App\Model\Line;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface LineRepository extends Repository
|
||||
interface LineRepository extends FluentRepository
|
||||
{
|
||||
public function getAll(): Collection;
|
||||
|
||||
public function getById($id): ?Line;
|
||||
public function getManyById($ids): Collection;
|
||||
}
|
@ -7,9 +7,6 @@ namespace App\Provider;
|
||||
use App\Model\Operator;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface OperatorRepository
|
||||
interface OperatorRepository extends FluentRepository
|
||||
{
|
||||
public function getAll(): Collection;
|
||||
public function getById($id): ?Operator;
|
||||
public function getManyById($ids): Collection;
|
||||
}
|
@ -6,5 +6,4 @@ namespace App\Provider;
|
||||
|
||||
interface Repository
|
||||
{
|
||||
|
||||
}
|
@ -6,7 +6,7 @@ use App\Model\Stop;
|
||||
use Carbon\Carbon;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface ScheduleRepository
|
||||
interface ScheduleRepository extends FluentRepository
|
||||
{
|
||||
const DEFAULT_DEPARTURES_COUNT = 16;
|
||||
|
||||
|
@ -7,10 +7,6 @@ namespace App\Provider;
|
||||
use App\Model\Stop;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
interface StopRepository extends Repository
|
||||
interface StopRepository extends FluentRepository
|
||||
{
|
||||
public function getAll(): Collection;
|
||||
public function getById($id): ?Stop;
|
||||
public function getManyById($ids): Collection;
|
||||
public function findByName(string $name): Collection;
|
||||
}
|
||||
|
@ -3,15 +3,10 @@
|
||||
namespace App\Provider;
|
||||
|
||||
use App\Model\Track;
|
||||
use App\Modifier\Modifier;
|
||||
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;
|
||||
public function stops(Modifier ...$modifiers): Collection;
|
||||
}
|
@ -4,7 +4,6 @@ namespace App\Provider;
|
||||
|
||||
use App\Model\Trip;
|
||||
|
||||
interface TripRepository
|
||||
interface TripRepository extends FluentRepository
|
||||
{
|
||||
public function getById(string $id): Trip;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use App\Entity\LineEntity;
|
||||
use App\Entity\OperatorEntity;
|
||||
use App\Entity\ProviderEntity;
|
||||
use App\Entity\StopEntity;
|
||||
use App\Entity\StopInTrack;
|
||||
use App\Entity\TrackStopEntity;
|
||||
use App\Entity\TrackEntity;
|
||||
use App\Entity\TripEntity;
|
||||
use App\Entity\TripStopEntity;
|
||||
@ -216,7 +216,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
|
||||
return !in_array($stop['stopId'], $this->stopBlacklist);
|
||||
})
|
||||
->map(function ($stop) use ($entity, $provider) {
|
||||
return StopInTrack::createFromArray([
|
||||
return TrackStopEntity::createFromArray([
|
||||
'stop' => $this->em->getReference(
|
||||
StopEntity::class,
|
||||
$this->ids->generate($provider, $stop['stopId'])
|
||||
|
@ -4,17 +4,28 @@ namespace App\Provider\ZtmGdansk;
|
||||
|
||||
use App\Model\Departure;
|
||||
use App\Model\Line;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\Stop;
|
||||
use App\Model\Vehicle;
|
||||
use App\Modifier\FieldFilter;
|
||||
use App\Modifier\IdFilter;
|
||||
use App\Modifier\Limit;
|
||||
use App\Modifier\Modifier;
|
||||
use App\Modifier\RelatedFilter;
|
||||
use App\Modifier\With;
|
||||
use App\Provider\Database\GenericScheduleRepository;
|
||||
use App\Provider\DepartureRepository;
|
||||
use App\Provider\LineRepository;
|
||||
use App\Provider\ScheduleRepository;
|
||||
use App\Service\IterableUtils;
|
||||
use App\Service\ModifierUtils;
|
||||
use App\Service\Proxy\ReferenceFactory;
|
||||
use Carbon\Carbon;
|
||||
use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use function App\Functions\setup;
|
||||
use function Kadet\Functional\ref;
|
||||
|
||||
class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||
{
|
||||
@ -39,16 +50,22 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||
$this->schedule = $schedule;
|
||||
}
|
||||
|
||||
public function getForStop(Stop $stop): Collection
|
||||
public function current(iterable $stops, Modifier ...$modifiers)
|
||||
{
|
||||
$real = $this->getRealDepartures($stop);
|
||||
$real = IterableUtils::toCollection($stops)
|
||||
->flatMap(ref([$this, 'getRealDepartures']))
|
||||
->sortBy(t\property('estimated'))
|
||||
;
|
||||
|
||||
$now = Carbon::now()->second(0);
|
||||
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
|
||||
$scheduled = $this->getScheduledDepartures($stop, $first);
|
||||
$scheduled = $this->getScheduledDepartures($stops, $first, ...$this->extractModifiers($modifiers));
|
||||
|
||||
return $this->pair($scheduled, $real)->filter(function (Departure $departure) use ($now) {
|
||||
$result = $this->pair($scheduled, $real)->filter(function (Departure $departure) use ($now) {
|
||||
return $departure->getDeparture() > $now;
|
||||
});
|
||||
|
||||
return $this->processResultWithModifiers($result, $modifiers);
|
||||
}
|
||||
|
||||
private function getRealDepartures(Stop $stop)
|
||||
@ -65,7 +82,8 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||
$lines = $estimates->map(function ($delay) {
|
||||
return $delay['routeId'];
|
||||
})->unique();
|
||||
$lines = $this->lines->getManyById($lines)->keyBy(t\property('id'));
|
||||
|
||||
$lines = $this->lines->all(new IdFilter($lines))->keyBy(t\property('id'));
|
||||
|
||||
return collect($estimates)->map(function ($delay) use ($stop, $lines) {
|
||||
$scheduled = (new Carbon($delay['theoreticalTime'], 'Europe/Warsaw'))->tz('UTC');
|
||||
@ -86,15 +104,35 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||
})->values();
|
||||
}
|
||||
|
||||
private function getScheduledDepartures(Stop $stop, Carbon $time)
|
||||
private function getScheduledDepartures($stop, Carbon $time, Modifier ...$modifiers)
|
||||
{
|
||||
return $this->schedule->getDeparturesForStop($stop, $time);
|
||||
return $this->schedule->all(
|
||||
new RelatedFilter($stop, Stop::class),
|
||||
new FieldFilter('departure', $time, '>='),
|
||||
new With('track'),
|
||||
new With('destination'),
|
||||
...$modifiers
|
||||
);
|
||||
}
|
||||
|
||||
private function pair(Collection $schedule, Collection $real)
|
||||
{
|
||||
$key = function (Departure $departure) {
|
||||
return sprintf("%s::%s", $departure->getLine()->getSymbol(), $departure->getScheduled()->format("H:i"));
|
||||
$key = function ($departure) {
|
||||
if ($departure instanceof Departure) {
|
||||
return sprintf(
|
||||
"%s::%s",
|
||||
$departure->getLine()->getId(),
|
||||
$departure->getScheduled()->format("H:i")
|
||||
);
|
||||
} elseif ($departure instanceof ScheduledStop) {
|
||||
return sprintf(
|
||||
"%s::%s",
|
||||
$departure->getTrack()->getLine()->getId(),
|
||||
$departure->getDeparture()->format("H:i")
|
||||
);
|
||||
} else {
|
||||
throw new \Exception();
|
||||
}
|
||||
};
|
||||
|
||||
$schedule = $schedule->keyBy($key)->all();
|
||||
@ -108,32 +146,81 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||
unset($schedule[$key]);
|
||||
}
|
||||
|
||||
return [ $real, $scheduled ];
|
||||
})->merge(collect($schedule)->map(function (Departure $scheduled) {
|
||||
return [ null, $scheduled ];
|
||||
return [
|
||||
'estimated' => $real,
|
||||
'scheduled' => $scheduled,
|
||||
];
|
||||
})->merge(collect($schedule)->map(function (ScheduledStop $scheduled) {
|
||||
return [
|
||||
'estimated' => null,
|
||||
'scheduled' => $scheduled,
|
||||
];
|
||||
}))->map(function ($pair) {
|
||||
return $this->merge(...$pair);
|
||||
return $this->merge($pair['estimated'], $pair['scheduled']);
|
||||
})->sortBy(function (Departure $departure) {
|
||||
$time = $departure->getEstimated() ?? $departure->getScheduled();
|
||||
return $time->getTimestamp();
|
||||
});
|
||||
}
|
||||
|
||||
private function merge(?Departure $real, ?Departure $scheduled)
|
||||
private function merge(?Departure $real, ?ScheduledStop $scheduled)
|
||||
{
|
||||
if (!$real) {
|
||||
return $scheduled;
|
||||
return $this->convertScheduledStopToDeparture($scheduled);
|
||||
}
|
||||
|
||||
if (!$scheduled) {
|
||||
return $real;
|
||||
}
|
||||
|
||||
$departure = clone $real;
|
||||
$departure->setDisplay($scheduled->getDisplay());
|
||||
return setup(clone $real, function (Departure $departure) use ($scheduled, $real) {
|
||||
$departure->setDisplay($this->extractDisplayFromScheduledStop($scheduled));
|
||||
$departure->setTrack($scheduled->getTrack());
|
||||
$departure->setTrip($scheduled->getTrip());
|
||||
});
|
||||
}
|
||||
|
||||
return $departure;
|
||||
private function convertScheduledStopToDeparture(ScheduledStop $stop): Departure
|
||||
{
|
||||
return setup(new Departure(), function (Departure $converted) use ($stop) {
|
||||
$converted->setDisplay($this->extractDisplayFromScheduledStop($stop));
|
||||
$converted->setLine($stop->getTrack()->getLine());
|
||||
$converted->setTrack($stop->getTrack());
|
||||
$converted->setTrip($stop->getTrip());
|
||||
$converted->setScheduled($stop->getDeparture());
|
||||
$converted->setStop($stop->getStop());
|
||||
});
|
||||
}
|
||||
|
||||
private function extractDisplayFromScheduledStop(ScheduledStop $stop)
|
||||
{
|
||||
return $stop->getTrack()->getDestination()->getName();
|
||||
}
|
||||
|
||||
private function extractModifiers(iterable $modifiers)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
/** @var Limit $limit */
|
||||
if ($limit = ModifierUtils::getOfType($modifiers, Limit::class)) {
|
||||
$result[] = new Limit($limit->getOffset(), $limit->getCount() * 2);
|
||||
} else {
|
||||
$result[] = Limit::count(16);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function processResultWithModifiers(Collection $result, iterable $modifiers)
|
||||
{
|
||||
foreach ($modifiers as $modifier) {
|
||||
switch (true) {
|
||||
case $modifier instanceof Limit:
|
||||
$result = $result->slice($modifier->getOffset(), $modifier->getCount());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,7 @@
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Hoa\Iterator\Recursive\Recursive;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
use function Kadet\Functional\Predicates\equals;
|
||||
use function Kadet\Functional\Predicates\method;
|
||||
use Tightenco\Collect\Support\Collection;
|
||||
|
||||
class AggregateConverter implements Converter
|
||||
{
|
||||
@ -40,4 +37,9 @@ class AggregateConverter implements Converter
|
||||
return $converter->supports($entity);
|
||||
});
|
||||
}
|
||||
|
||||
public function getConverters(): Collection
|
||||
{
|
||||
return clone $this->converters;
|
||||
}
|
||||
}
|
||||
|
8
src/Service/CacheableConverter.php
Normal file
8
src/Service/CacheableConverter.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
interface CacheableConverter extends Converter
|
||||
{
|
||||
public function flushCache();
|
||||
}
|
@ -11,17 +11,19 @@ use Kadet\Functional as f;
|
||||
use Kadet\Functional\Transforms as t;
|
||||
use const Kadet\Functional\_;
|
||||
|
||||
final class EntityConverter implements Converter, RecursiveConverter
|
||||
final class EntityConverter implements Converter, RecursiveConverter, CacheableConverter
|
||||
{
|
||||
use RecursiveConverterTrait;
|
||||
|
||||
private $id;
|
||||
private $reference;
|
||||
private $cache;
|
||||
|
||||
public function __construct(IdUtils $id, ReferenceFactory $reference)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->reference = $reference;
|
||||
$this->cache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,10 +32,10 @@ final class EntityConverter implements Converter, RecursiveConverter
|
||||
*
|
||||
* @return Line|Track|Stop|Operator|Trip|ScheduledStop
|
||||
*/
|
||||
public function convert($entity, array $cache = [])
|
||||
public function convert($entity)
|
||||
{
|
||||
if (array_key_exists($key = get_class($entity).':'.$this->getId($entity), $cache)) {
|
||||
return $cache[$key];
|
||||
if (array_key_exists($key = get_class($entity) . ':' . $this->getId($entity), $this->cache)) {
|
||||
return $this->cache[$key];
|
||||
}
|
||||
|
||||
if ($entity instanceof Proxy && !$entity->__isInitialized()) {
|
||||
@ -41,10 +43,11 @@ final class EntityConverter implements Converter, RecursiveConverter
|
||||
}
|
||||
|
||||
$result = $this->create($entity);
|
||||
$cache = $cache + [$key => $result];
|
||||
$convert = function ($entity) use ($cache) {
|
||||
$this->cache[$key] = $result;
|
||||
|
||||
$convert = function ($entity) {
|
||||
return $this->supports($entity)
|
||||
? $this->convert($entity, $cache)
|
||||
? $this->convert($entity)
|
||||
: $this->parent->convert($entity);
|
||||
};
|
||||
|
||||
@ -78,6 +81,7 @@ final class EntityConverter implements Converter, RecursiveConverter
|
||||
->map(t\property('stop'))
|
||||
->map($convert),
|
||||
'line' => $convert($entity->getLine()),
|
||||
'destination' => $convert($entity->getFinal()->getStop()),
|
||||
]);
|
||||
break;
|
||||
|
||||
@ -172,4 +176,9 @@ final class EntityConverter implements Converter, RecursiveConverter
|
||||
{
|
||||
return $entity instanceof Entity;
|
||||
}
|
||||
|
||||
public function flushCache()
|
||||
{
|
||||
$this->cache = [];
|
||||
}
|
||||
}
|
||||
|
68
src/Service/EntityReferenceFactory.php
Normal file
68
src/Service/EntityReferenceFactory.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
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
|
||||
{
|
||||
protected $mapping = [
|
||||
Line::class => LineEntity::class,
|
||||
Stop::class => StopEntity::class,
|
||||
Track::class => TrackEntity::class,
|
||||
];
|
||||
|
||||
private $em;
|
||||
private $id;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, IdUtils $id)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!array_key_exists($class, $this->mapping)) {
|
||||
throw new \InvalidArgumentException(sprintf("Cannot make entity reference of %s.", $class));
|
||||
}
|
||||
|
||||
return $this->em->getReference(
|
||||
$this->mapping[$class],
|
||||
$this->id->generate($provider, $object->getId())
|
||||
);
|
||||
}
|
||||
}
|
44
src/Service/HandlerProvider.php
Normal file
44
src/Service/HandlerProvider.php
Normal 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;
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ class IdUtils
|
||||
|
||||
public function generate(ProviderEntity $provider, $id)
|
||||
{
|
||||
// todo: use array cache if not fast enough
|
||||
return sprintf('%s%s%s', $provider->getId(), self::DELIMITER, $id);
|
||||
}
|
||||
|
||||
|
29
src/Service/ModifierUtils.php
Normal file
29
src/Service/ModifierUtils.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Kadet\Functional\Predicate;
|
||||
use function Kadet\Functional\Predicates\instance;
|
||||
|
||||
final class ModifierUtils
|
||||
{
|
||||
public static function get(iterable $modifiers, Predicate $predicate)
|
||||
{
|
||||
return collect($modifiers)->first($predicate);
|
||||
}
|
||||
|
||||
public static function getOfType(iterable $modifiers, $class)
|
||||
{
|
||||
return self::get($modifiers, instance($class));
|
||||
}
|
||||
|
||||
public static function hasAny(iterable $modifiers, Predicate $predicate)
|
||||
{
|
||||
return collect($modifiers)->contains($predicate);
|
||||
}
|
||||
|
||||
public static function hasAnyOfType(iterable $modifiers, $class)
|
||||
{
|
||||
return collect($modifiers)->contains(instance($class));
|
||||
}
|
||||
}
|
@ -2,8 +2,10 @@
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\TrackStopEntity;
|
||||
use App\Entity\TripStopEntity;
|
||||
use App\Model\ScheduledStop;
|
||||
use App\Model\TrackStop;
|
||||
|
||||
class ScheduledStopConverter implements Converter, RecursiveConverter
|
||||
{
|
||||
@ -11,18 +13,29 @@ class ScheduledStopConverter implements Converter, RecursiveConverter
|
||||
|
||||
public function convert($entity)
|
||||
{
|
||||
/** @var ScheduledStop $entity */
|
||||
if ($entity instanceof TrackStopEntity) {
|
||||
return TrackStop::createFromArray([
|
||||
'stop' => $this->parent->convert($entity->getStop()),
|
||||
'track' => $this->parent->convert($entity->getTrack()),
|
||||
'order' => $entity->getOrder(),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($entity instanceof TripStopEntity) {
|
||||
return ScheduledStop::createFromArray([
|
||||
'arrival' => $entity->getArrival(),
|
||||
'departure' => $entity->getDeparture(),
|
||||
'stop' => $this->parent->convert($entity->getStop()),
|
||||
'order' => $entity->getOrder(),
|
||||
'track' => $this->parent->convert($entity->getTrip()->getTrack()),
|
||||
'trip' => $this->parent->convert($entity->getTrip()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function supports($entity)
|
||||
{
|
||||
return $entity instanceof TripStopEntity;
|
||||
return $entity instanceof TripStopEntity
|
||||
|| $entity instanceof TrackStopEntity;
|
||||
}
|
||||
}
|
||||
|
@ -66,8 +66,11 @@ const config = {
|
||||
new GenerateSW({
|
||||
navigationPreload: true,
|
||||
runtimeCaching: [{
|
||||
urlPattern: ({event}) => event.request.mode === 'navigate',
|
||||
urlPattern: ({ event }) => event.request.mode === 'navigate',
|
||||
handler: 'NetworkFirst',
|
||||
}, {
|
||||
urlPattern: /^https?:\/\/api\.maptiler\.com\//,
|
||||
handler: 'CacheFirst',
|
||||
}],
|
||||
swDest: '../service-worker.js'
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user