feat: refactoring of xml parsers

This commit is contained in:
Björn Fromme committed 2025-01-24 17:10:49 +01:00
1 parent 11f1508640
commit 6730c243f6
49 files changed
+1414 -1235

No files matched your search

+350
View File
@@ -0,0 +1,350 @@
<?php
namespace App\BusProNet\XmlLoader;
use App\BusProNet\Model\BaseData;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\Model\Guide;
use App\BusProNet\Model\MutableData;
use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Room;
use App\BusProNet\Model\Service;
use App\BusProNet\Model\Travel;
use Psr\Cache\InvalidArgumentException;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\Finder\Finder;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
class TravelLoader extends AbstractLoader
{
public function __construct(
private readonly HotelLoader $hotelDataLoader,
CacheInterface $cache,
?string $xmlPath = null
) {
parent::__construct($cache, $xmlPath);
}
public function generateFilesMap(): array
{
try {
return $this->cache->get('bpn_travels_mapping', function (ItemInterface $item) {
$item->expiresAfter(3 * 60 * 60);
$finder = new Finder();
$finder->files()->in($this->xmlPath)->name('Ziel_*.xml');
$mapping = [];
$hotels = $this->hotelDataLoader->loadAll();
foreach ($finder as $file) {
$xml = $file->getContents();
$crawler = new Crawler($xml);
$travelDataNodes = $crawler->filterXPath('//reisen/reise/termin');
$travelDataNodes->each(function (Crawler $node) use (&$mapping, $hotels, $file) {
$travelId = $node->attr('idbuspro');
$mapping[$travelId] = [
'id' => $travelId,
'code' => $node->attr('code'),
'label' => $this->getStringOrNullValue($node->filterXPath('//text')),
'dateFrom' => $this->stringToDate($node->attr('termin')),
'dateTo' => $this->stringToDate($node->attr('bis')),
'hotels' => [],
'file' => $file->getRealPath(),
];
$node->filterXPath('//hotel')
->each(function (Crawler $hotelNode) use (&$mapping, $hotels, $travelId) {
$hotelId = (int) $hotelNode->attr('idbuspro');
$mapping[$travelId]['hotels'][$hotelId] = [
'id' => $hotelId,
'code' => $hotelNode->attr('code'),
'label' => $hotels[$hotelId]->name,
];
});
});
}
return $mapping;
});
} catch (InvalidArgumentException $e) {
return [];
}
}
public function mapCodeToId(string $travelCode): ?int
{
$mapping = $this->generateFilesMap();
$travelCodes = array_column($mapping, 'code', 'id');
if (false === $travelId = array_search($travelCode, $travelCodes)) {
return null;
}
return $travelId;
}
public function loadById(int $travelId, ?int $hotelId = null, ?string $filename = null): ?Travel
{
if (null !== $filename) {
return $this->loadXml($travelId, $hotelId, $filename);
}
$mapping = $this->generateFilesMap();
if (false === isset($mapping[$travelId])) {
return null;
}
$filename = $mapping[$travelId]['file'];
return $this->loadXml($travelId, $hotelId, $filename);
}
private function loadXml(int $id, ?int $hotelId, string $filename): ?Travel
{
$xml = file_get_contents($filename);
$crawler = new Crawler($xml);
$travelNode = $crawler->filterXPath(sprintf('//reise/termin[@idbuspro="%d"]', $id));
if (0 === $travelNode->count()) {
return null;
}
return $this->parseXml($travelNode->first(), $hotelId);
}
public function parseXml(Crawler $node, ?int $hotelId): Travel
{
$hotelNode = $this->getHotelNode($node, $hotelId);
$travel = new Travel();
$travel->id = (int) $node->attr('idbuspro');
$travel->hotelId = $hotelId;
$travel->label = $this->getStringOrNullValue($node->filterXPath('//text'));
$travel->dateFrom = $this->stringToDate($node->attr('termin'));
$travel->dateTo = $this->stringToDate($node->attr('bis'));
$travel->code = $node->attr('code');
$travel->type = $node->attr('reiseart');
$travel->priceFrom = $this->stringToFloat($this->getStringOrNullValue($node->filterXPath('//abpreis')));
$travel->selectionGroups = $this->getSelectionGroups($node->filterXPath('//selektionsgruppe'));
$travel->additionalServices = $this
->getAdditionalServices($node->filterXPath('//lei_sonstiges/leistung'));
$travel->transportationServices = $this
->getTransportationServices($node->filterXPath('//lei_befoerderung/leistung'));
$travel->rooms = $this->getRooms($hotelNode);
$travel->pickups = $this->getPickups($node->filterXPath('//zustiege/zustieg'));
return $travel;
}
public function getSelectionGroups(Crawler $node): array
{
$selectionGroups = [];
$node->each(function (Crawler $groupNode) use (&$selectionGroups) {
$groupId = (int) $groupNode->attr('idbuspro');
$selectionGroup = new CrmSelectionGroup();
$selectionGroup->id = $groupId;
$selectionGroup->label = $groupNode->attr('bezeichnung');
$groupNode
->filterXPath('//selektion')
->each(function (Crawler $selectionNode) use (&$selectionGroups, &$selectionGroup, $groupId) {
$selectionId = (int) $selectionNode->attr('idbuspro');
$selection = new CrmSelection();
$selection->id = $selectionId;
$selection->label = $selectionNode->attr('bezeichnung');
$selectionGroups[$groupId]['selections'][$selectionId] = $selectionNode->attr('bezeichnung');
$selectionGroup->selections[] = $selection;
})
;
$selectionGroups[$groupId] = $selectionGroup;
});
return $selectionGroups;
}
public function getAdditionalServices(Crawler $node): array
{
$additionalServices = [];
$node->each(function (Crawler $serviceNode) use (&$additionalServices) {
$serviceId = (int) $serviceNode->attr('idbuspro');
$service = new Service();
$service->source = Service::SOURCE_TRAVEL;
$service->category = Service::CATEGORY_ADDITIONAL;
$service->id = $serviceId;
$service->subType = $serviceNode->attr('unterart');
$service->mandatory = $this->stringToBool($serviceNode->attr('pflicht'));
$service->dateFrom = $this->stringToDate($serviceNode->attr('termin'));
$service->dateTo = $this->stringToDate($serviceNode->attr('bis'));
$service->label = $this->getStringOrNullValue($serviceNode->filterXPath('//text'));
$service->price = $this
->stringToFloat($this->getStringOrNullValue($serviceNode->filterXPath('//preis')));
$service->status = $this->getStringOrNullValue($serviceNode->filterXPath('//status'));
$additionalServices[$serviceId] = $service;
});
return $additionalServices;
}
public function getGuideForTransportationService(\SimpleXMLElement $xmlTransportationService): ?Guide
{
if (null === $xmlTransportationService->reiseleiter) {
return null;
}
$guide = new Guide();
$guide->name = $xmlTransportationService->reiseleiter->name;
$guide->phone = $xmlTransportationService->reiseleiter->telefon;
return $guide;
}
public function getTransportationServices(Crawler $node): array
{
$transportationServices = [];
$node->each(function (Crawler $serviceNode) use (&$transportationServices) {
$serviceId = (int) $serviceNode->attr('idbuspro');
$service = new Service();
$service->source = Service::SOURCE_TRAVEL;
$service->category = Service::CATEGORY_TRANSPORTATION;
$service->id = $serviceId;
$service->subType = $serviceNode->attr('unterart');
$service->dateFrom = $this->stringToDate($serviceNode->attr('termin'));
$service->dateTo = $this->stringToDate($serviceNode->attr('bis'));
$service->label = $this->getStringOrNullValue($serviceNode->filterXPath('//text'));
$service->direction = $this->getStringOrNullValue($serviceNode->filterXPath('//richtung'));
$transportationServices[$serviceId] = $service;
});
return $transportationServices;
}
public function getPickups(Crawler $node): array
{
$pickups = [];
$node->each(function (Crawler $pickupNode) use (&$pickups) {
$pickupId = (int) $pickupNode->attr('idbuspro');
$pickup = new Pickup();
$pickup->id = $pickupId;
$pickup->time = $this->stringToDateTime($pickupNode->attr('zeit'));
$pickup->price = $pickupNode->attr('preis') ?
$this->stringToFloat($pickupNode->attr('preis')) : null;
$pickups[$pickupId] = $pickup;
});
return $pickups;
}
public function getHotelNode(Crawler $node, ?int $hotelId): Crawler
{
// In case not hotel id is provided, take the first hotel node (which is most probably the only one)
if (null === $hotelId) {
return $node->filterXPath('//hotel')->first();
}
$hotelNode = $node->filterXPath(sprintf('//hotel[@idbuspro="%d"]', $hotelId));
return $hotelNode->first();
}
public function getRooms(Crawler $node): array
{
$roomNodes = $node->filterXPath('//zimmer/preis');
if (0 === $roomNodes->count()) {
return [];
}
$rooms = [];
$roomNodes->each(function (Crawler $roomNode) use (&$rooms) {
$roomId = (int) $roomNode->attr('idbuspro_zimmer');
$room = new Room();
$room->id = $roomId;
$room->code = $roomNode->attr('zimmercode');
$room->category = $roomNode->attr('kat');
$room->boardId = (int) $roomNode->attr('idbuspro_vp');
$room->label = $roomNode->attr('zimmertext');
$room->minPax = (int) $roomNode->attr('MinPax');
$room->maxPax = (int) $roomNode->attr('MaxPax');
$room->nights = (int) $roomNode->attr('naechte');
$room->price = $roomNode->attr('preis') ?
$this->stringToFloat($roomNode->attr('preis')) : null;
$room->status = $this->getStringOrNullValue($roomNode->filterXPath('//status'));
$room->available = (int) $roomNode->attr('verfuegbar');
$rooms[$roomId] = $room;
});
return $rooms;
}
public function patchMutability(Travel $travel, BaseData $mutableData): void
{
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_ADDITIONAL_SERVICES)) {
$travel->additionalServicesMutable = $item->mutable;
}
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_TRANSPORTATION)) {
$travel->transportationServicesMutable = $item->mutable;
}
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_PICKUP)) {
$travel->pickupsMutable = $item->mutable;
}
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_ACCOMMODATION)) {
$travel->roomsMutable = $item->mutable;
}
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_PARTICIPANT_COUNT)) {
$travel->participantCountMutable = $item->mutable;
}
if (null !== $item = $mutableData->getItemByKey(MutableData::CATEGORY_PARTICIPANT_DATA)) {
$travel->participantDataMutable = $item->mutable;
}
}
public function patchAvailabilities(Travel $travel, BaseData $availabilities): void
{
$serviceAvailabilities = $availabilities->getItems();
foreach ([...$travel->additionalServices, ...$travel->transportationServices] as $service) {
if (array_key_exists($service->id, $serviceAvailabilities)) {
$service->available = $serviceAvailabilities[$service->id]->available;
}
}
}
public function patchBookings(BaseData $bookings): void
{
foreach ($bookings->getItems() as $booking) {
if (null === $travelId = $booking->travelId) {
continue;
}
$booking->travelData = $this->loadById($travelId);
}
}
}