WIP - Add scheduled stop repository

This commit is contained in:
Kacper Donat 2020-03-15 12:50:38 +01:00
parent 7ffd3c02cd
commit 50a79470e7
13 changed files with 247 additions and 22 deletions

View File

@ -5,6 +5,7 @@ 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;
@ -15,6 +16,10 @@ class FieldFilterDatabaseHandler implements ModifierHandler
Stop::class => [
'name' => 'name',
],
ScheduledStop::class => [
'departure' => 'departure',
'arrival' => 'arrival',
]
];
public function process(HandleModifierEvent $event)

View File

@ -0,0 +1,99 @@
<?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\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',
],
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)];
}
}
}

View File

@ -6,9 +6,11 @@ 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;
@ -27,6 +29,10 @@ class RelatedFilterDatabaseGenericHandler implements ModifierHandler, ServiceSub
Stop::class => 'stop',
Track::class => 'track',
],
ScheduledStop::class => [
Stop::class => 'stop',
Trip::class => 'trip',
],
];
private $em;

View File

@ -7,17 +7,23 @@ use Carbon\Carbon;
class ScheduledStop extends TrackStop
{
/**
* Arrival time
* Arrival time.
* @var Carbon
*/
private $arrival;
/**
* Departure time
* Departure time.
* @var Carbon
*/
private $departure;
/**
* Exact trip that this scheduled stop is part of.
* @var Trip|null
*/
private $trip;
public function getArrival(): Carbon
{
return $this->arrival;
@ -37,4 +43,14 @@ class ScheduledStop extends TrackStop
{
$this->departure = $departure;
}
public function getTrip(): ?Trip
{
return $this->trip;
}
public function setTrip(?Trip $trip): void
{
$this->trip = $trip;
}
}

View File

@ -42,6 +42,14 @@ class Track implements Referable, Fillable
*/
private $stops;
/**
* Destination stop of this track
* @var Stop|null
* @Serializer\Type(Stop::class)
* @SWG\Property(ref=@Model(type=Stop::class))
*/
private $destination;
/**
* Track constructor.
*/
@ -89,4 +97,14 @@ class Track implements Referable, Fillable
{
return $this->stops = collect($stops);
}
}
public function getDestination(): ?Stop
{
return $this->destination;
}
public function setDestination(?Stop $destination): void
{
$this->destination = $destination;
}
}

View File

@ -40,6 +40,12 @@ class Trip implements Referable, Fillable
*/
private $schedule;
/**
* Destination stop of this trip
* @var Stop|null
*/
private $destination;
/**
* Track constructor.
*/
@ -87,4 +93,14 @@ class Trip implements Referable, Fillable
{
return $this->schedule = collect($schedule);
}
public function getDestination(): ?Stop
{
return $this->destination;
}
public function setDestination(?Stop $destination): void
{
$this->destination = $destination;
}
}

View File

@ -9,6 +9,7 @@ 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;
@ -17,6 +18,7 @@ use App\Modifier\IdFilter;
use App\Modifier\Limit;
use App\Modifier\Modifier;
use App\Modifier\RelatedFilter;
use App\Modifier\With;
use App\Provider\Repository;
use App\Service\Converter;
use App\Service\HandlerProvider;
@ -64,6 +66,7 @@ abstract class DatabaseRepository implements Repository
Limit::class => LimitDatabaseHandler::class,
FieldFilter::class => FieldFilterDatabaseHandler::class,
RelatedFilter::class => RelatedFilterDatabaseGenericHandler::class,
With::class => GenericWithDatabaseHandler::class,
], static::getHandlers()));
}

View File

@ -9,8 +9,10 @@ use App\Entity\TripEntity;
use App\Entity\TripStopEntity;
use App\Model\Departure;
use App\Model\Line;
use App\Model\ScheduledStop;
use App\Model\Stop;
use App\Model\Vehicle;
use App\Modifier\Modifier;
use App\Provider\ScheduleRepository;
use Carbon\Carbon;
use Tightenco\Collect\Support\Collection;
@ -71,8 +73,19 @@ class GenericScheduleRepository extends DatabaseRepository implements ScheduleRe
});
}
protected static function getHandlers()
public function all(Modifier ...$modifiers): Collection
{
return [];
$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,
]);
}
}

View File

@ -3,6 +3,7 @@
namespace App\Provider\Database;
use App\Entity\StopEntity;
use App\Handler\Database\GenericWithDatabaseHandler;
use App\Handler\Database\WithDestinationsDatabaseHandler;
use App\Model\Stop;
use App\Modifier\Modifier;
@ -33,7 +34,7 @@ class GenericStopRepository extends DatabaseRepository implements StopRepository
With::class => function (With $modifier) {
return $modifier->getRelationship() === 'destinations'
? WithDestinationsDatabaseHandler::class
: GenericWithHandler::class;
: GenericWithDatabaseHandler::class;
},
]);
}

View File

@ -6,7 +6,7 @@ use App\Model\Stop;
use Carbon\Carbon;
use Tightenco\Collect\Support\Collection;
interface ScheduleRepository
interface ScheduleRepository extends FluentRepository
{
const DEFAULT_DEPARTURES_COUNT = 16;

View File

@ -4,9 +4,14 @@ namespace App\Provider\ZtmGdansk;
use App\Model\Departure;
use App\Model\Line;
use App\Model\ScheduledStop;
use App\Model\Stop;
use App\Model\Vehicle;
use App\Modifier\FieldFilter;
use App\Modifier\IdFilter;
use App\Modifier\Limit;
use App\Modifier\RelatedFilter;
use App\Modifier\With;
use App\Provider\Database\GenericScheduleRepository;
use App\Provider\DepartureRepository;
use App\Provider\LineRepository;
@ -90,13 +95,33 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
private function getScheduledDepartures(Stop $stop, Carbon $time)
{
return $this->schedule->getDeparturesForStop($stop, $time);
return $this->schedule->all(
new RelatedFilter($stop),
new FieldFilter('departure', $time, '>='),
new With('track'),
new With('destination'),
Limit::count(16)
);
}
private function pair(Collection $schedule, Collection $real)
{
$key = function (Departure $departure) {
return sprintf("%s::%s", $departure->getLine()->getSymbol(), $departure->getScheduled()->format("H:i"));
$key = function ($departure) {
if ($departure instanceof Departure) {
return sprintf(
"%s::%s",
$departure->getLine()->getId(),
$departure->getScheduled()->format("H:i")
);
} elseif ($departure instanceof ScheduledStop) {
return sprintf(
"%s::%s",
$departure->getTrack()->getLine()->getId(),
$departure->getDeparture()->format("H:i")
);
} else {
throw new \Exception();
}
};
$schedule = $schedule->keyBy($key)->all();
@ -110,21 +135,27 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
unset($schedule[$key]);
}
return [ $real, $scheduled ];
})->merge(collect($schedule)->map(function (Departure $scheduled) {
return [ null, $scheduled ];
return [
'estimated' => $real,
'scheduled' => $scheduled,
];
})->merge(collect($schedule)->map(function (ScheduledStop $scheduled) {
return [
'estimated' => null,
'scheduled' => $scheduled,
];
}))->map(function ($pair) {
return $this->merge(...$pair);
return $this->merge($pair['estimated'], $pair['scheduled']);
})->sortBy(function (Departure $departure) {
$time = $departure->getEstimated() ?? $departure->getScheduled();
return $time->getTimestamp();
});
}
private function merge(?Departure $real, ?Departure $scheduled)
private function merge(?Departure $real, ?ScheduledStop $scheduled)
{
if (!$real) {
return $scheduled;
return $this->convertScheduledStopToDeparture($scheduled);
}
if (!$scheduled) {
@ -132,10 +163,24 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
}
$departure = clone $real;
$departure->setDisplay($scheduled->getDisplay());
$departure->setDisplay($real->getDisplay());
$departure->setTrack($scheduled->getTrack());
$departure->setTrip($scheduled->getTrip());
return $departure;
}
private function convertScheduledStopToDeparture(ScheduledStop $stop): Departure
{
$converted = new Departure();
$converted->setDisplay($stop->getTrack()->getDestination()->getName());
$converted->setLine($stop->getTrack()->getLine());
$converted->setTrack($stop->getTrack());
$converted->setTrip($stop->getTrip());
$converted->setScheduled($stop->getDeparture());
$converted->setStop($stop->getStop());
return $converted;
}
}

View File

@ -32,7 +32,7 @@ final class EntityConverter implements Converter, RecursiveConverter
*/
public function convert($entity, array $cache = [])
{
if (array_key_exists($key = get_class($entity).':'.$this->getId($entity), $cache)) {
if (array_key_exists($key = get_class($entity) . ':' . $this->getId($entity), $cache)) {
return $cache[$key];
}
@ -78,6 +78,7 @@ final class EntityConverter implements Converter, RecursiveConverter
->map(t\property('stop'))
->map($convert),
'line' => $convert($entity->getLine()),
'destination' => $convert($entity->getFinal()->getStop()),
]);
break;
@ -154,7 +155,7 @@ final class EntityConverter implements Converter, RecursiveConverter
private function create(Entity $entity)
{
$id = $this->id->of($entity);
$id = $this->id->of($entity);
$class = $this->getModelClassForEntity($entity);
return $class::createFromArray(['id' => $id]);
@ -162,7 +163,7 @@ final class EntityConverter implements Converter, RecursiveConverter
private function reference(Entity $entity)
{
$id = $this->id->strip($this->getId($entity));
$id = $this->id->strip($this->getId($entity));
$class = $this->getModelClassForEntity($entity);
return $this->reference->get($class, ['id' => $id]);

View File

@ -5,6 +5,7 @@ namespace App\Service;
use App\Entity\TrackStopEntity;
use App\Entity\TripStopEntity;
use App\Model\ScheduledStop;
use App\Model\TrackStop;
class ScheduledStopConverter implements Converter, RecursiveConverter
{
@ -12,9 +13,8 @@ class ScheduledStopConverter implements Converter, RecursiveConverter
public function convert($entity)
{
if ($entity instanceof TrackStopEntity) {
return ScheduledStop::createFromArray([
return TrackStop::createFromArray([
'stop' => $this->parent->convert($entity->getStop()),
'track' => $this->parent->convert($entity->getTrack()),
'order' => $entity->getOrder(),
@ -27,6 +27,8 @@ class ScheduledStopConverter implements Converter, RecursiveConverter
'departure' => $entity->getDeparture(),
'stop' => $this->parent->convert($entity->getStop()),
'order' => $entity->getOrder(),
'track' => $this->parent->convert($entity->getTrip()->getTrack()),
'trip' => $this->parent->convert($entity->getTrip()),
]);
}
}