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
|
# this creates a service per class whose id is the fully-qualified class name
|
||||||
App\:
|
App\:
|
||||||
resource: '../src/*'
|
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
|
# controllers are imported separately to make sure services can be injected
|
||||||
# as action arguments even if you don't extend any base controller class
|
# as action arguments even if you don't extend any base controller class
|
||||||
@ -35,6 +35,10 @@ services:
|
|||||||
resource: '../src/Provider'
|
resource: '../src/Provider'
|
||||||
public: true
|
public: true
|
||||||
|
|
||||||
|
App\Handler\:
|
||||||
|
resource: '../src/Handler'
|
||||||
|
tags: [ app.handler ]
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
# add more service definitions when explicit configuration is needed
|
||||||
# please note that last definitions always *replace* previous ones
|
# please note that last definitions always *replace* previous ones
|
||||||
|
|
||||||
@ -90,3 +94,7 @@ services:
|
|||||||
# other servces
|
# other servces
|
||||||
App\Service\ProviderResolver:
|
App\Service\ProviderResolver:
|
||||||
arguments: [!tagged app.provider, '%kernel.debug%']
|
arguments: [!tagged app.provider, '%kernel.debug%']
|
||||||
|
|
||||||
|
App\Service\HandlerProvider:
|
||||||
|
arguments: [!tagged_locator app.handler]
|
||||||
|
shared: false
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
version: '2'
|
version: '3.4'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
nginx:
|
nginx:
|
||||||
@ -11,9 +11,15 @@ services:
|
|||||||
|
|
||||||
php:
|
php:
|
||||||
build: docker/php
|
build: docker/php
|
||||||
mem_limit: 2g
|
|
||||||
env_file:
|
env_file:
|
||||||
- ./docker/php/.env
|
- ./docker/php/.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/var/www:cached
|
- ./:/var/www:cached
|
||||||
- ./docker/php/log.conf:/usr/local/etc/php-fpm.d/zz-log.conf
|
- ./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
|
PHP_IDE_CONFIG=serverName=czydojade
|
||||||
|
@ -1,22 +1,36 @@
|
|||||||
FROM php:7.3-fpm
|
FROM php:7.3-fpm
|
||||||
|
|
||||||
|
ARG XDEBUG_REMOTE_HOST="172.17.0.1"
|
||||||
|
|
||||||
RUN apt-get update && \
|
RUN apt-get update && \
|
||||||
apt-get install -y --no-install-recommends git zip libzip-dev
|
apt-get install -y --no-install-recommends git zip libzip-dev
|
||||||
|
|
||||||
RUN docker-php-ext-install zip
|
RUN docker-php-ext-install zip
|
||||||
|
|
||||||
|
# XDebug
|
||||||
RUN pecl install xdebug-2.9.0 && docker-php-ext-enable 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;
|
# Blackfire
|
||||||
RUN echo "date.timezone = Europe/Warsaw" >> /usr/local/etc/php/conf.d/datetime.ini;
|
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');"
|
#Composer
|
||||||
RUN php composer-setup.php
|
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
|
||||||
RUN php -r "unlink('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
|
# Timezone
|
||||||
RUN chmod +x /usr/local/bin/composer
|
|
||||||
RUN ln -snf /usr/share/zoneinfo/Europe/Warsaw /etc/localtime
|
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
|
WORKDIR /var/www
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ export class FinderComponent extends Vue {
|
|||||||
|
|
||||||
this.state = 'fetching';
|
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) {
|
if (response.ok) {
|
||||||
this.found = (await response.json()).reduce((accumulator, { name, stops }) => Object.assign(accumulator, { [name]: stops }), {});
|
this.found = (await response.json()).reduce((accumulator, { name, stops }) => Object.assign(accumulator, { [name]: stops }), {});
|
||||||
|
@ -8,7 +8,11 @@ export function query(params: UrlParams = { }) {
|
|||||||
function *simplify(name: string, param: any): IterableIterator<ParamValuePair> {
|
function *simplify(name: string, param: any): IterableIterator<ParamValuePair> {
|
||||||
if (typeof param === 'string') {
|
if (typeof param === 'string') {
|
||||||
yield [ name, param ];
|
yield [ name, param ];
|
||||||
} else if (typeof param === 'number') {
|
} else if (typeof param === 'boolean') {
|
||||||
|
if (param) {
|
||||||
|
yield [ name, '1' ];
|
||||||
|
}
|
||||||
|
} else if (typeof param === 'number') {
|
||||||
yield [ name, param.toString() ];
|
yield [ name, param.toString() ];
|
||||||
} else if (param instanceof Array) {
|
} else if (param instanceof Array) {
|
||||||
for (let entry of param) {
|
for (let entry of param) {
|
||||||
|
@ -5,6 +5,10 @@ namespace App\Controller\Api\v1;
|
|||||||
|
|
||||||
use App\Controller\Controller;
|
use App\Controller\Controller;
|
||||||
use App\Model\Departure;
|
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\DepartureRepository;
|
||||||
use App\Provider\StopRepository;
|
use App\Provider\StopRepository;
|
||||||
use App\Service\SerializerContextFactory;
|
use App\Service\SerializerContextFactory;
|
||||||
@ -32,11 +36,11 @@ class DeparturesController extends Controller
|
|||||||
* @SWG\Schema(type="array", @SWG\Items(ref=@Model(type=Departure::class)))
|
* @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)
|
public function stops(DepartureRepository $departures, StopRepository $stops, Request $request)
|
||||||
{
|
{
|
||||||
$stops = $stops
|
$stops = $stops->all(new IdFilter($request->query->get('stop')));
|
||||||
->getManyById($request->query->get('stop'))
|
$result = $departures->current($stops, ...$this->getModifiersFromRequest($request));
|
||||||
->flatMap(ref([ $departures, 'getForStop' ]))
|
|
||||||
->sortBy(property('departure'));
|
|
||||||
|
|
||||||
return $this->json(
|
return $this->json(
|
||||||
$stops->values()->slice(0, (int)$request->query->get('limit', 8)),
|
$result->values()->slice(0, (int)$request->query->get('limit', 8)),
|
||||||
200,
|
200,
|
||||||
[],
|
[],
|
||||||
$this->serializerContextFactory->create(Departure::class, ['Default'])
|
$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
|
<?php
|
||||||
|
|
||||||
|
|
||||||
namespace App\Controller\Api\v1;
|
namespace App\Controller\Api\v1;
|
||||||
|
|
||||||
use App\Controller\Controller;
|
use App\Controller\Controller;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use App\Model\Track;
|
|
||||||
use App\Model\StopGroup;
|
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\StopRepository;
|
||||||
use App\Provider\TrackRepository;
|
use App\Provider\TrackRepository;
|
||||||
use App\Service\Proxy\ReferenceFactory;
|
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||||
use Swagger\Annotations as SWG;
|
use Swagger\Annotations as SWG;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -38,7 +40,8 @@ class StopsController extends Controller
|
|||||||
* name="id",
|
* name="id",
|
||||||
* in="query",
|
* in="query",
|
||||||
* type="array",
|
* 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")
|
* @SWG\Items(type="string")
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
@ -46,16 +49,9 @@ class StopsController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function index(Request $request, StopRepository $stops)
|
public function index(Request $request, StopRepository $stops)
|
||||||
{
|
{
|
||||||
switch (true) {
|
$modifiers = $this->getModifiersFromRequest($request);
|
||||||
case $request->query->has('id'):
|
|
||||||
$result = $stops->getManyById($request->query->get('id'));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
return $this->json($stops->all(...$modifiers)->toArray());
|
||||||
$result = $stops->getAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->json($result->all());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,16 +72,9 @@ class StopsController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function groups(Request $request, StopRepository $stops)
|
public function groups(Request $request, StopRepository $stops)
|
||||||
{
|
{
|
||||||
switch (true) {
|
$modifiers = $this->getModifiersFromRequest($request);
|
||||||
case $request->query->has('name'):
|
|
||||||
$result = $stops->findByName($request->query->get('name'));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
return $this->json(static::group($stops->all(...$modifiers))->toArray());
|
||||||
$result = $stops->getAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->json(static::group($result)->all());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,7 +95,7 @@ class StopsController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function one(Request $request, StopRepository $stops, $id)
|
public function one(Request $request, StopRepository $stops, $id)
|
||||||
{
|
{
|
||||||
return $this->json($stops->getById($id));
|
return $this->json($stops->first(new IdFilter($id), new With("destinations")));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -115,21 +104,12 @@ class StopsController extends Controller
|
|||||||
* @SWG\Response(
|
* @SWG\Response(
|
||||||
* response=200,
|
* response=200,
|
||||||
* description="Returns specific stop referenced via identificator.",
|
* description="Returns specific stop referenced via identificator.",
|
||||||
* @SWG\Schema(type="object", properties={
|
* @SWG\Schema(ref=@Model(type=TrackStop::class))
|
||||||
* @SWG\Property(property="track", type="object", ref=@Model(type=Track::class)),
|
|
||||||
* @SWG\Property(property="order", type="integer", minimum="0")
|
|
||||||
* })
|
|
||||||
* )
|
* )
|
||||||
*
|
|
||||||
* @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->stops(new RelatedFilter(Stop::reference($id))));
|
||||||
|
|
||||||
return $this->json($tracks->getByStop($stop)->map(function ($tuple) {
|
|
||||||
return array_combine(['track', 'order'], $tuple);
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function group(Collection $stops)
|
public static function group(Collection $stops)
|
||||||
@ -145,4 +125,19 @@ class StopsController extends Controller
|
|||||||
return $group;
|
return $group;
|
||||||
})->values();
|
})->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;
|
namespace App\Controller\Api\v1;
|
||||||
|
|
||||||
use App\Controller\Controller;
|
use App\Controller\Controller;
|
||||||
|
use App\Model\Line;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use App\Model\Track;
|
use App\Model\Track;
|
||||||
|
use App\Modifier\IdFilter;
|
||||||
|
use App\Modifier\RelatedFilter;
|
||||||
use App\Provider\TrackRepository;
|
use App\Provider\TrackRepository;
|
||||||
|
use App\Service\IterableUtils;
|
||||||
use Nelmio\ApiDocBundle\Annotation\Model;
|
use Nelmio\ApiDocBundle\Annotation\Model;
|
||||||
use Swagger\Annotations as SWG;
|
use Swagger\Annotations as SWG;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
use function App\Functions\encapsulate;
|
use function App\Functions\encapsulate;
|
||||||
|
use function Kadet\Functional\ref;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Route("/tracks")
|
* @Route("/tracks")
|
||||||
|
* @SWG\Tag(name="Tracks")
|
||||||
*/
|
*/
|
||||||
class TracksController extends Controller
|
class TracksController extends Controller
|
||||||
{
|
{
|
||||||
@ -23,48 +29,69 @@ class TracksController extends Controller
|
|||||||
* response=200,
|
* response=200,
|
||||||
* description="Returns all tracks for specific provider, e.g. ZTM Gdańsk.",
|
* description="Returns all tracks for specific provider, e.g. ZTM Gdańsk.",
|
||||||
* )
|
* )
|
||||||
* @SWG\Tag(name="Tracks")
|
|
||||||
* @Route("/", methods={"GET"})
|
* @Route("/", methods={"GET"})
|
||||||
*/
|
*/
|
||||||
public function index(Request $request, TrackRepository $repository)
|
public function index(Request $request, TrackRepository $repository)
|
||||||
{
|
{
|
||||||
switch (true) {
|
$modifiers = $this->getModifiersFromRequest($request);
|
||||||
case $request->query->has('stop'):
|
|
||||||
return $this->byStop($request, $repository);
|
return $this->json($repository->all(...$modifiers));
|
||||||
case $request->query->has('line'):
|
}
|
||||||
return $this->byLine($request, $repository);
|
|
||||||
case $request->query->has('id'):
|
/**
|
||||||
return $this->byId($request, $repository);
|
* @Route("/stops", methods={"GET"})
|
||||||
default:
|
* @Route("/{track}/stops", methods={"GET"})
|
||||||
throw new BadRequestHttpException(
|
*/
|
||||||
sprintf(
|
public function stops(Request $request, TrackRepository $repository)
|
||||||
'At least one parameter of %s must be set.',
|
{
|
||||||
implode(', ', ['stop', 'line', 'id'])
|
$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'));
|
||||||
|
|
||||||
|
yield new IdFilter($id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function byId(Request $request, TrackRepository $repository)
|
private function getStopsModifiersFromRequest(Request $request)
|
||||||
{
|
{
|
||||||
$id = encapsulate($request->query->get('id'));
|
if ($request->query->has('stop')) {
|
||||||
|
$stop = encapsulate($request->query->get('stop'));
|
||||||
|
$stop = collect($stop)->map(ref([Stop::class, 'reference']));
|
||||||
|
|
||||||
return $this->json($repository->getManyById($id));
|
yield new RelatedFilter($stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->query->has('track') || $request->attributes->has('track')) {
|
||||||
|
$track = $request->get('track');
|
||||||
|
$track = Track::reference($track);
|
||||||
|
|
||||||
|
yield new RelatedFilter($track);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->query->has('id')) {
|
||||||
|
$id = encapsulate($request->query->get('id'));
|
||||||
|
|
||||||
|
yield new IdFilter($id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private function byStop(Request $request, TrackRepository $repository)
|
|
||||||
{
|
|
||||||
$stop = $request->query->get('stop');
|
|
||||||
$stop = array_map([Stop::class, 'reference'], encapsulate($stop));
|
|
||||||
|
|
||||||
return $this->json($repository->getByStop($stop));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function byLine(Request $request, TrackRepository $repository)
|
|
||||||
{
|
|
||||||
$line = $request->query->get('line');
|
|
||||||
$line = array_map([Stop::class, 'reference'], encapsulate($line));
|
|
||||||
|
|
||||||
return $this->json($repository->getByLine($line));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,8 @@ namespace App\Controller\Api\v1;
|
|||||||
|
|
||||||
use App\Controller\Controller;
|
use App\Controller\Controller;
|
||||||
use App\Model\Trip;
|
use App\Model\Trip;
|
||||||
|
use App\Modifier\IdFilter;
|
||||||
|
use App\Modifier\With;
|
||||||
use App\Provider\TripRepository;
|
use App\Provider\TripRepository;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Routing\Annotation\Route;
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
@ -18,7 +20,7 @@ class TripController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function one($id, TripRepository $repository)
|
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));
|
return $this->json($trip, Response::HTTP_OK, [], $this->serializerContextFactory->create(Trip::class));
|
||||||
}
|
}
|
||||||
|
@ -45,16 +45,18 @@ class TrackEntity implements Entity, Fillable
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops in track
|
* 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"})
|
* @ORM\OrderBy({"order": "ASC"})
|
||||||
*/
|
*/
|
||||||
private $stopsInTrack;
|
private $stopsInTrack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Final stop in this track.
|
* 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;
|
private $final;
|
||||||
|
|
||||||
@ -114,7 +116,7 @@ class TrackEntity implements Entity, Fillable
|
|||||||
$this->final = $this->stopsInTrack->last();
|
$this->final = $this->stopsInTrack->last();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFinal(): StopInTrack
|
public function getFinal(): TrackStopEntity
|
||||||
{
|
{
|
||||||
return $this->final;
|
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"})
|
* @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;
|
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;
|
namespace App\Exception;
|
||||||
|
|
||||||
|
|
||||||
class NonExistentServiceException extends \Exception
|
class NonExistentServiceException extends \LogicException
|
||||||
{
|
{
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
namespace App\Exception;
|
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)));
|
||||||
|
}
|
||||||
|
}
|
@ -12,4 +12,10 @@ function encapsulate($value)
|
|||||||
default:
|
default:
|
||||||
return [ $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);
|
||||||
|
}
|
@ -138,4 +138,4 @@ class Line implements Fillable, Referable
|
|||||||
{
|
{
|
||||||
$this->operator = $operator;
|
$this->operator = $operator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,53 +4,25 @@ namespace App\Model;
|
|||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
||||||
class ScheduledStop implements Fillable
|
class ScheduledStop extends TrackStop
|
||||||
{
|
{
|
||||||
use FillTrait;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop (as a place) related to that scheduled bus stop
|
* Arrival time.
|
||||||
* @var Stop
|
|
||||||
*/
|
|
||||||
private $stop;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Order in trip
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $order;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arrival time
|
|
||||||
* @var Carbon
|
* @var Carbon
|
||||||
*/
|
*/
|
||||||
private $arrival;
|
private $arrival;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Departure time
|
* Departure time.
|
||||||
* @var Carbon
|
* @var Carbon
|
||||||
*/
|
*/
|
||||||
private $departure;
|
private $departure;
|
||||||
|
|
||||||
public function getStop()
|
/**
|
||||||
{
|
* Exact trip that this scheduled stop is part of.
|
||||||
return $this->stop;
|
* @var Trip|null
|
||||||
}
|
*/
|
||||||
|
private $trip;
|
||||||
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 getArrival(): Carbon
|
public function getArrival(): Carbon
|
||||||
{
|
{
|
||||||
@ -71,4 +43,14 @@ class ScheduledStop implements Fillable
|
|||||||
{
|
{
|
||||||
$this->departure = $departure;
|
$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;
|
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.
|
* Track constructor.
|
||||||
*/
|
*/
|
||||||
@ -89,4 +97,14 @@ class Track implements Referable, Fillable
|
|||||||
{
|
{
|
||||||
return $this->stops = collect($stops);
|
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;
|
private $schedule;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destination stop of this trip
|
||||||
|
* @var Stop|null
|
||||||
|
*/
|
||||||
|
private $destination;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Track constructor.
|
* Track constructor.
|
||||||
*/
|
*/
|
||||||
@ -87,4 +93,14 @@ class Trip implements Referable, Fillable
|
|||||||
{
|
{
|
||||||
return $this->schedule = collect($schedule);
|
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;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
use App\Entity\Entity;
|
|
||||||
use App\Entity\ProviderEntity;
|
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\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\Converter;
|
||||||
|
use App\Service\HandlerProvider;
|
||||||
use App\Service\IdUtils;
|
use App\Service\IdUtils;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
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 */
|
/** @var EntityManagerInterface */
|
||||||
protected $em;
|
protected $em;
|
||||||
|
|
||||||
@ -24,22 +43,38 @@ class DatabaseRepository
|
|||||||
/** @var Converter */
|
/** @var Converter */
|
||||||
protected $converter;
|
protected $converter;
|
||||||
|
|
||||||
|
/** @var HandlerProvider */
|
||||||
|
protected $handlers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DatabaseRepository constructor.
|
* DatabaseRepository constructor.
|
||||||
*
|
*
|
||||||
* @param EntityManagerInterface $em
|
* @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->em = $em;
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
$this->converter = $converter;
|
$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 */
|
/** @return static */
|
||||||
public function withProvider(ProviderEntity $provider)
|
public function withProvider(ProviderEntity $provider)
|
||||||
{
|
{
|
||||||
$result = clone $this;
|
$result = clone $this;
|
||||||
$result->provider = $provider;
|
$result->provider = $provider;
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@ -56,4 +91,69 @@ class DatabaseRepository
|
|||||||
|
|
||||||
return $this->em->getReference($class, $id);
|
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;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
use App\Entity\LineEntity;
|
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\Model\Line;
|
||||||
|
use App\Modifier\Limit;
|
||||||
|
use App\Modifier\IdFilter;
|
||||||
use App\Provider\LineRepository;
|
use App\Provider\LineRepository;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
use Kadet\Functional as f;
|
use Kadet\Functional as f;
|
||||||
|
|
||||||
class GenericLineRepository extends DatabaseRepository implements LineRepository
|
class GenericLineRepository extends DatabaseRepository implements LineRepository
|
||||||
{
|
{
|
||||||
public function getAll(): Collection
|
public function all(Modifier ...$modifiers): Collection
|
||||||
{
|
{
|
||||||
$repository = $this->em->getRepository(LineEntity::class);
|
$builder = $this->em
|
||||||
$lines = $repository->findAll();
|
->createQueryBuilder()
|
||||||
|
->from(LineEntity::class, 'line')
|
||||||
|
->select('line')
|
||||||
|
;
|
||||||
|
|
||||||
return collect($lines)->map(f\ref([$this, 'convert']));
|
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||||
|
'alias' => 'line',
|
||||||
|
'entity' => LineEntity::class,
|
||||||
|
'type' => Line::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -2,32 +2,26 @@
|
|||||||
|
|
||||||
namespace App\Provider\Database;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
|
use App\Entity\OperatorEntity;
|
||||||
use App\Model\Operator;
|
use App\Model\Operator;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use App\Provider\OperatorRepository;
|
use App\Provider\OperatorRepository;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
class GenericOperatorRepository extends DatabaseRepository implements OperatorRepository
|
class GenericOperatorRepository extends DatabaseRepository implements OperatorRepository
|
||||||
{
|
{
|
||||||
public function getAll(): Collection
|
public function all(Modifier ...$modifiers): Collection
|
||||||
{
|
{
|
||||||
$repository = $this->em->getRepository(Operator::class);
|
$builder = $this->em
|
||||||
$operators = $repository->findAll();
|
->createQueryBuilder()
|
||||||
|
->from(OperatorEntity::class, 'operator')
|
||||||
|
->select('operator')
|
||||||
|
;
|
||||||
|
|
||||||
return collect($operators);
|
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||||
|
'alias' => 'operator',
|
||||||
|
'entity' => OperatorEntity::class,
|
||||||
|
'type' => Operator::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
namespace App\Provider\Database;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
use App\Entity\StopEntity;
|
use App\Entity\StopEntity;
|
||||||
use App\Entity\StopInTrack;
|
use App\Entity\TrackStopEntity;
|
||||||
use App\Entity\TrackEntity;
|
use App\Entity\TrackEntity;
|
||||||
use App\Entity\TripEntity;
|
use App\Entity\TripEntity;
|
||||||
use App\Entity\TripStopEntity;
|
use App\Entity\TripStopEntity;
|
||||||
use App\Model\Departure;
|
use App\Model\Departure;
|
||||||
use App\Model\Line;
|
use App\Model\Line;
|
||||||
|
use App\Model\ScheduledStop;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use App\Model\Vehicle;
|
use App\Model\Vehicle;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use App\Provider\ScheduleRepository;
|
use App\Provider\ScheduleRepository;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Tightenco\Collect\Support\Collection;
|
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;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
use App\Entity\StopEntity;
|
use App\Entity\StopEntity;
|
||||||
use App\Entity\TrackEntity;
|
use App\Handler\Database\GenericWithDatabaseHandler;
|
||||||
use App\Model\Destination;
|
use App\Handler\Database\WithDestinationsDatabaseHandler;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
|
use App\Modifier\With;
|
||||||
use App\Provider\StopRepository;
|
use App\Provider\StopRepository;
|
||||||
use Kadet\Functional as f;
|
|
||||||
use Kadet\Functional\Transforms as t;
|
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
class GenericStopRepository extends DatabaseRepository implements StopRepository
|
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']));
|
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||||
|
'alias' => 'stop',
|
||||||
|
'entity' => StopEntity::class,
|
||||||
|
'type' => Stop::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getById($id): ?Stop
|
protected static function getHandlers()
|
||||||
{
|
{
|
||||||
$id = $this->id->generate($this->provider, $id);
|
return array_merge(parent::getHandlers(), [
|
||||||
$stop = $this->em->getRepository(StopEntity::class)->find($id);
|
With::class => function (With $modifier) {
|
||||||
|
return $modifier->getRelationship() === 'destinations'
|
||||||
return $this->convert($stop);
|
? WithDestinationsDatabaseHandler::class
|
||||||
}
|
: GenericWithDatabaseHandler::class;
|
||||||
|
},
|
||||||
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(),
|
|
||||||
]);
|
|
||||||
})->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())]);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,66 +2,41 @@
|
|||||||
|
|
||||||
namespace App\Provider\Database;
|
namespace App\Provider\Database;
|
||||||
|
|
||||||
use App\Entity\LineEntity;
|
use App\Entity\TrackStopEntity;
|
||||||
use App\Entity\StopEntity;
|
|
||||||
use App\Entity\StopInTrack;
|
|
||||||
use App\Entity\TrackEntity;
|
use App\Entity\TrackEntity;
|
||||||
use function App\Functions\encapsulate;
|
use App\Model\TrackStop;
|
||||||
use App\Model\Stop;
|
use App\Modifier\Modifier;
|
||||||
use App\Model\Track;
|
use App\Model\Track;
|
||||||
use App\Provider\TrackRepository;
|
use App\Provider\TrackRepository;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
use Kadet\Functional as f;
|
|
||||||
|
|
||||||
class GenericTrackRepository extends DatabaseRepository implements TrackRepository
|
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');
|
||||||
|
|
||||||
|
return $this->allFromQueryBuilder($builder, $modifiers, [
|
||||||
|
'alias' => 'track',
|
||||||
|
'entity' => TrackEntity::class,
|
||||||
|
'type' => Track::class,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
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']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,25 +4,23 @@ namespace App\Provider\Database;
|
|||||||
|
|
||||||
use App\Entity\TripEntity;
|
use App\Entity\TripEntity;
|
||||||
use App\Model\Trip;
|
use App\Model\Trip;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use App\Provider\TripRepository;
|
use App\Provider\TripRepository;
|
||||||
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
class GenericTripRepository extends DatabaseRepository implements TripRepository
|
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);
|
$builder = $this->em
|
||||||
|
|
||||||
$trip = $this->em
|
|
||||||
->createQueryBuilder()
|
->createQueryBuilder()
|
||||||
->from(TripEntity::class, 't')
|
->from(TripEntity::class, 'trip')
|
||||||
->join('t.stops', 'ts')
|
->select('trip');
|
||||||
->join('ts.stop', 's')
|
|
||||||
->select('t', 'ts')
|
|
||||||
->where('t.id = :id')
|
|
||||||
->getQuery()
|
|
||||||
->setParameter('id', $id)
|
|
||||||
->getOneOrNullResult();
|
|
||||||
|
|
||||||
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\Model\Stop;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
interface DepartureRepository extends Repository
|
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\Line;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use App\Model\Vehicle;
|
use App\Model\Vehicle;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use App\Provider\DepartureRepository;
|
use App\Provider\DepartureRepository;
|
||||||
use App\Service\Proxy\ReferenceFactory;
|
use App\Service\Proxy\ReferenceFactory;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
@ -25,21 +26,21 @@ class DummyDepartureRepository implements DepartureRepository
|
|||||||
$this->reference = $reference;
|
$this->reference = $reference;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getForStop(Stop $stop): Collection
|
public function current(iterable $stops, Modifier ...$modifiers)
|
||||||
{
|
{
|
||||||
return collect([
|
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) {
|
])->map(function ($departure) use ($stop) {
|
||||||
list($symbol, $type, $display, $vehicle) = $departure;
|
[$symbol, $type, $display, $vehicle] = $departure;
|
||||||
$scheduled = new Carbon();
|
$scheduled = new Carbon();
|
||||||
$estimated = (clone $scheduled)->addSeconds(40);
|
$estimated = (clone $scheduled)->addSeconds(40);
|
||||||
|
|
||||||
@ -53,4 +54,4 @@ class DummyDepartureRepository implements DepartureRepository
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Provider\Dummy;
|
namespace App\Provider\Dummy;
|
||||||
|
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use App\Provider\StopRepository;
|
use App\Provider\StopRepository;
|
||||||
use App\Service\Proxy\ReferenceFactory;
|
use App\Service\Proxy\ReferenceFactory;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
@ -41,4 +42,14 @@ class DummyStopRepository implements StopRepository
|
|||||||
{
|
{
|
||||||
return collect();
|
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;
|
namespace App\Provider;
|
||||||
|
|
||||||
|
interface LineRepository extends FluentRepository
|
||||||
use App\Model\Line;
|
|
||||||
use Tightenco\Collect\Support\Collection;
|
|
||||||
|
|
||||||
interface LineRepository extends Repository
|
|
||||||
{
|
{
|
||||||
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 App\Model\Operator;
|
||||||
use Tightenco\Collect\Support\Collection;
|
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
|
interface Repository
|
||||||
{
|
{
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -6,7 +6,7 @@ use App\Model\Stop;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
interface ScheduleRepository
|
interface ScheduleRepository extends FluentRepository
|
||||||
{
|
{
|
||||||
const DEFAULT_DEPARTURES_COUNT = 16;
|
const DEFAULT_DEPARTURES_COUNT = 16;
|
||||||
|
|
||||||
|
@ -7,10 +7,6 @@ namespace App\Provider;
|
|||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use Tightenco\Collect\Support\Collection;
|
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;
|
namespace App\Provider;
|
||||||
|
|
||||||
use App\Model\Track;
|
use App\Model\Track;
|
||||||
|
use App\Modifier\Modifier;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
|
|
||||||
interface TrackRepository
|
interface TrackRepository extends FluentRepository
|
||||||
{
|
{
|
||||||
public function getAll(): Collection;
|
public function stops(Modifier ...$modifiers): Collection;
|
||||||
|
}
|
||||||
public function getById($id): Track;
|
|
||||||
public function getManyById($ids): Collection;
|
|
||||||
|
|
||||||
public function getByStop($stop): Collection;
|
|
||||||
public function getByLine($line): Collection;
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@ namespace App\Provider;
|
|||||||
|
|
||||||
use App\Model\Trip;
|
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\OperatorEntity;
|
||||||
use App\Entity\ProviderEntity;
|
use App\Entity\ProviderEntity;
|
||||||
use App\Entity\StopEntity;
|
use App\Entity\StopEntity;
|
||||||
use App\Entity\StopInTrack;
|
use App\Entity\TrackStopEntity;
|
||||||
use App\Entity\TrackEntity;
|
use App\Entity\TrackEntity;
|
||||||
use App\Entity\TripEntity;
|
use App\Entity\TripEntity;
|
||||||
use App\Entity\TripStopEntity;
|
use App\Entity\TripStopEntity;
|
||||||
@ -216,7 +216,7 @@ class ZtmGdanskDataUpdateSubscriber implements EventSubscriberInterface
|
|||||||
return !in_array($stop['stopId'], $this->stopBlacklist);
|
return !in_array($stop['stopId'], $this->stopBlacklist);
|
||||||
})
|
})
|
||||||
->map(function ($stop) use ($entity, $provider) {
|
->map(function ($stop) use ($entity, $provider) {
|
||||||
return StopInTrack::createFromArray([
|
return TrackStopEntity::createFromArray([
|
||||||
'stop' => $this->em->getReference(
|
'stop' => $this->em->getReference(
|
||||||
StopEntity::class,
|
StopEntity::class,
|
||||||
$this->ids->generate($provider, $stop['stopId'])
|
$this->ids->generate($provider, $stop['stopId'])
|
||||||
|
@ -4,17 +4,28 @@ namespace App\Provider\ZtmGdansk;
|
|||||||
|
|
||||||
use App\Model\Departure;
|
use App\Model\Departure;
|
||||||
use App\Model\Line;
|
use App\Model\Line;
|
||||||
|
use App\Model\ScheduledStop;
|
||||||
use App\Model\Stop;
|
use App\Model\Stop;
|
||||||
use App\Model\Vehicle;
|
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\Database\GenericScheduleRepository;
|
||||||
use App\Provider\DepartureRepository;
|
use App\Provider\DepartureRepository;
|
||||||
use App\Provider\LineRepository;
|
use App\Provider\LineRepository;
|
||||||
use App\Provider\ScheduleRepository;
|
use App\Provider\ScheduleRepository;
|
||||||
|
use App\Service\IterableUtils;
|
||||||
|
use App\Service\ModifierUtils;
|
||||||
use App\Service\Proxy\ReferenceFactory;
|
use App\Service\Proxy\ReferenceFactory;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
|
use JMS\Serializer\Tests\Fixtures\Discriminator\Car;
|
||||||
use Tightenco\Collect\Support\Collection;
|
use Tightenco\Collect\Support\Collection;
|
||||||
use Kadet\Functional\Transforms as t;
|
use Kadet\Functional\Transforms as t;
|
||||||
|
use function App\Functions\setup;
|
||||||
|
use function Kadet\Functional\ref;
|
||||||
|
|
||||||
class ZtmGdanskDepartureRepository implements DepartureRepository
|
class ZtmGdanskDepartureRepository implements DepartureRepository
|
||||||
{
|
{
|
||||||
@ -39,16 +50,22 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
|||||||
$this->schedule = $schedule;
|
$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);
|
$now = Carbon::now()->second(0);
|
||||||
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
|
$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 $departure->getDeparture() > $now;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return $this->processResultWithModifiers($result, $modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRealDepartures(Stop $stop)
|
private function getRealDepartures(Stop $stop)
|
||||||
@ -65,7 +82,8 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
|||||||
$lines = $estimates->map(function ($delay) {
|
$lines = $estimates->map(function ($delay) {
|
||||||
return $delay['routeId'];
|
return $delay['routeId'];
|
||||||
})->unique();
|
})->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) {
|
return collect($estimates)->map(function ($delay) use ($stop, $lines) {
|
||||||
$scheduled = (new Carbon($delay['theoreticalTime'], 'Europe/Warsaw'))->tz('UTC');
|
$scheduled = (new Carbon($delay['theoreticalTime'], 'Europe/Warsaw'))->tz('UTC');
|
||||||
@ -86,15 +104,35 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
|||||||
})->values();
|
})->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)
|
private function pair(Collection $schedule, Collection $real)
|
||||||
{
|
{
|
||||||
$key = function (Departure $departure) {
|
$key = function ($departure) {
|
||||||
return sprintf("%s::%s", $departure->getLine()->getSymbol(), $departure->getScheduled()->format("H:i"));
|
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();
|
$schedule = $schedule->keyBy($key)->all();
|
||||||
@ -108,32 +146,81 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
|||||||
unset($schedule[$key]);
|
unset($schedule[$key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ $real, $scheduled ];
|
return [
|
||||||
})->merge(collect($schedule)->map(function (Departure $scheduled) {
|
'estimated' => $real,
|
||||||
return [ null, $scheduled ];
|
'scheduled' => $scheduled,
|
||||||
|
];
|
||||||
|
})->merge(collect($schedule)->map(function (ScheduledStop $scheduled) {
|
||||||
|
return [
|
||||||
|
'estimated' => null,
|
||||||
|
'scheduled' => $scheduled,
|
||||||
|
];
|
||||||
}))->map(function ($pair) {
|
}))->map(function ($pair) {
|
||||||
return $this->merge(...$pair);
|
return $this->merge($pair['estimated'], $pair['scheduled']);
|
||||||
})->sortBy(function (Departure $departure) {
|
})->sortBy(function (Departure $departure) {
|
||||||
$time = $departure->getEstimated() ?? $departure->getScheduled();
|
$time = $departure->getEstimated() ?? $departure->getScheduled();
|
||||||
return $time->getTimestamp();
|
return $time->getTimestamp();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private function merge(?Departure $real, ?Departure $scheduled)
|
private function merge(?Departure $real, ?ScheduledStop $scheduled)
|
||||||
{
|
{
|
||||||
if (!$real) {
|
if (!$real) {
|
||||||
return $scheduled;
|
return $this->convertScheduledStopToDeparture($scheduled);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$scheduled) {
|
if (!$scheduled) {
|
||||||
return $real;
|
return $real;
|
||||||
}
|
}
|
||||||
|
|
||||||
$departure = clone $real;
|
return setup(clone $real, function (Departure $departure) use ($scheduled, $real) {
|
||||||
$departure->setDisplay($scheduled->getDisplay());
|
$departure->setDisplay($this->extractDisplayFromScheduledStop($scheduled));
|
||||||
$departure->setTrack($scheduled->getTrack());
|
$departure->setTrack($scheduled->getTrack());
|
||||||
$departure->setTrip($scheduled->getTrip());
|
$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;
|
namespace App\Service;
|
||||||
|
|
||||||
use Hoa\Iterator\Recursive\Recursive;
|
use Tightenco\Collect\Support\Collection;
|
||||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
|
||||||
use function Kadet\Functional\Predicates\equals;
|
|
||||||
use function Kadet\Functional\Predicates\method;
|
|
||||||
|
|
||||||
class AggregateConverter implements Converter
|
class AggregateConverter implements Converter
|
||||||
{
|
{
|
||||||
@ -40,4 +37,9 @@ class AggregateConverter implements Converter
|
|||||||
return $converter->supports($entity);
|
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 Kadet\Functional\Transforms as t;
|
||||||
use const Kadet\Functional\_;
|
use const Kadet\Functional\_;
|
||||||
|
|
||||||
final class EntityConverter implements Converter, RecursiveConverter
|
final class EntityConverter implements Converter, RecursiveConverter, CacheableConverter
|
||||||
{
|
{
|
||||||
use RecursiveConverterTrait;
|
use RecursiveConverterTrait;
|
||||||
|
|
||||||
private $id;
|
private $id;
|
||||||
private $reference;
|
private $reference;
|
||||||
|
private $cache;
|
||||||
|
|
||||||
public function __construct(IdUtils $id, ReferenceFactory $reference)
|
public function __construct(IdUtils $id, ReferenceFactory $reference)
|
||||||
{
|
{
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
$this->reference = $reference;
|
$this->reference = $reference;
|
||||||
|
$this->cache = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -30,21 +32,22 @@ final class EntityConverter implements Converter, RecursiveConverter
|
|||||||
*
|
*
|
||||||
* @return Line|Track|Stop|Operator|Trip|ScheduledStop
|
* @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)) {
|
if (array_key_exists($key = get_class($entity) . ':' . $this->getId($entity), $this->cache)) {
|
||||||
return $cache[$key];
|
return $this->cache[$key];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity instanceof Proxy && !$entity->__isInitialized()) {
|
if ($entity instanceof Proxy && !$entity->__isInitialized()) {
|
||||||
return $this->reference($entity);
|
return $this->reference($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->create($entity);
|
$result = $this->create($entity);
|
||||||
$cache = $cache + [$key => $result];
|
$this->cache[$key] = $result;
|
||||||
$convert = function ($entity) use ($cache) {
|
|
||||||
|
$convert = function ($entity) {
|
||||||
return $this->supports($entity)
|
return $this->supports($entity)
|
||||||
? $this->convert($entity, $cache)
|
? $this->convert($entity)
|
||||||
: $this->parent->convert($entity);
|
: $this->parent->convert($entity);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +81,7 @@ final class EntityConverter implements Converter, RecursiveConverter
|
|||||||
->map(t\property('stop'))
|
->map(t\property('stop'))
|
||||||
->map($convert),
|
->map($convert),
|
||||||
'line' => $convert($entity->getLine()),
|
'line' => $convert($entity->getLine()),
|
||||||
|
'destination' => $convert($entity->getFinal()->getStop()),
|
||||||
]);
|
]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -154,7 +158,7 @@ final class EntityConverter implements Converter, RecursiveConverter
|
|||||||
|
|
||||||
private function create(Entity $entity)
|
private function create(Entity $entity)
|
||||||
{
|
{
|
||||||
$id = $this->id->of($entity);
|
$id = $this->id->of($entity);
|
||||||
$class = $this->getModelClassForEntity($entity);
|
$class = $this->getModelClassForEntity($entity);
|
||||||
|
|
||||||
return $class::createFromArray(['id' => $id]);
|
return $class::createFromArray(['id' => $id]);
|
||||||
@ -162,7 +166,7 @@ final class EntityConverter implements Converter, RecursiveConverter
|
|||||||
|
|
||||||
private function reference(Entity $entity)
|
private function reference(Entity $entity)
|
||||||
{
|
{
|
||||||
$id = $this->id->strip($this->getId($entity));
|
$id = $this->id->strip($this->getId($entity));
|
||||||
$class = $this->getModelClassForEntity($entity);
|
$class = $this->getModelClassForEntity($entity);
|
||||||
|
|
||||||
return $this->reference->get($class, ['id' => $id]);
|
return $this->reference->get($class, ['id' => $id]);
|
||||||
@ -172,4 +176,9 @@ final class EntityConverter implements Converter, RecursiveConverter
|
|||||||
{
|
{
|
||||||
return $entity instanceof Entity;
|
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)
|
public function generate(ProviderEntity $provider, $id)
|
||||||
{
|
{
|
||||||
|
// todo: use array cache if not fast enough
|
||||||
return sprintf('%s%s%s', $provider->getId(), self::DELIMITER, $id);
|
return sprintf('%s%s%s', $provider->getId(), self::DELIMITER, $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,4 +24,4 @@ class IdUtils
|
|||||||
{
|
{
|
||||||
return $this->strip($entity->getId());
|
return $this->strip($entity->getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Entity\TrackStopEntity;
|
||||||
use App\Entity\TripStopEntity;
|
use App\Entity\TripStopEntity;
|
||||||
use App\Model\ScheduledStop;
|
use App\Model\ScheduledStop;
|
||||||
|
use App\Model\TrackStop;
|
||||||
|
|
||||||
class ScheduledStopConverter implements Converter, RecursiveConverter
|
class ScheduledStopConverter implements Converter, RecursiveConverter
|
||||||
{
|
{
|
||||||
@ -11,18 +13,29 @@ class ScheduledStopConverter implements Converter, RecursiveConverter
|
|||||||
|
|
||||||
public function convert($entity)
|
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(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return ScheduledStop::createFromArray([
|
if ($entity instanceof TripStopEntity) {
|
||||||
'arrival' => $entity->getArrival(),
|
return ScheduledStop::createFromArray([
|
||||||
'departure' => $entity->getDeparture(),
|
'arrival' => $entity->getArrival(),
|
||||||
'stop' => $this->parent->convert($entity->getStop()),
|
'departure' => $entity->getDeparture(),
|
||||||
'order' => $entity->getOrder(),
|
'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)
|
public function supports($entity)
|
||||||
{
|
{
|
||||||
return $entity instanceof TripStopEntity;
|
return $entity instanceof TripStopEntity
|
||||||
|
|| $entity instanceof TrackStopEntity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,11 @@ const config = {
|
|||||||
new GenerateSW({
|
new GenerateSW({
|
||||||
navigationPreload: true,
|
navigationPreload: true,
|
||||||
runtimeCaching: [{
|
runtimeCaching: [{
|
||||||
urlPattern: ({event}) => event.request.mode === 'navigate',
|
urlPattern: ({ event }) => event.request.mode === 'navigate',
|
||||||
handler: 'NetworkFirst',
|
handler: 'NetworkFirst',
|
||||||
|
}, {
|
||||||
|
urlPattern: /^https?:\/\/api\.maptiler\.com\//,
|
||||||
|
handler: 'CacheFirst',
|
||||||
}],
|
}],
|
||||||
swDest: '../service-worker.js'
|
swDest: '../service-worker.js'
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user