feat: refactoring of xml parsers

This commit is contained in:
Björn Fromme
2025-01-24 17:10:49 +01:00
parent 11f1508640
commit 6730c243f6
49 changed files with 1414 additions and 1235 deletions
@@ -0,0 +1,21 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\TypeConversionTrait;
use Symfony\Component\DomCrawler\Crawler;
abstract class AbstractParser
{
use TypeConversionTrait;
protected function getStringOrNullValue(Crawler $node): ?string
{
return 0 < $node->count() ? $node->text() : null;
}
protected function getIntOrNullValue(Crawler $node): ?int
{
return 0 < $node->count() ? (int) $node->text() : null;
}
}
@@ -0,0 +1,64 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\ApiClient;
use App\BusProNet\Exception\ResponseParserException;
use Symfony\Component\DomCrawler\Crawler;
class ApiResponseParser extends AbstractParser
{
/**
* @throws ResponseParserException
*/
public function parseXmlString(string $type, string $xml): mixed
{
$crawler = new Crawler($xml);
if (0 === $crawler->count()) {
throw new ResponseParserException('Empty response received from server');
}
// Override type when present in XML to catch error responses
$responseType = $this
->getStringOrNullValue($crawler->filterXPath('//ergebnis/satz/@typ')) ?? $type;
$resultNode = $crawler->filterXPath('//ergebnis');
switch ($responseType) {
case ApiClient::TYPE_NOTIFICATION:
return (new NotificationParser())->parse($crawler);
case ApiClient::TYPE_CUSTOMER_DATA:
$subType = $resultNode->filterXPath('//art')->text();
switch ($subType) {
case 'Adressdaten':
case 'Adressdaten_Ändern':
case 'Newsletter':
return (new PersonalDataParser())->parse($resultNode);
case 'SelektionCRM':
case 'SelektionCRM_Ändern':
return (new CrmAttributesResponseParser())->parse($resultNode);
case 'Vorgänge':
return (new BookingsParser())->parse($resultNode);
case 'Vorgang_Details':
return (new BookingParser())->parse($resultNode);
case 'Dokumentdruck':
return (new DocumentsParser())->parseDocuments($resultNode);
case 'Vorgangdruck':
return (new DocumentsParser())->parseConfirmation($resultNode);
}
break;
case ApiClient::TYPE_BASE_DATA_COUNTRIES:
return (new CountriesParser())->parse($resultNode);
case ApiClient::TYPE_MUTABLE_DATA:
return (new MutableDataParser())->parse($resultNode);
case ApiClient::TYPE_AVAILABILITY:
return (new AvailabilitiesParser())->parse($resultNode);
case ApiClient::TYPE_BOOKING_UPDATE:
return (new BookingUpdateParser())->parse($resultNode);
}
throw new ResponseParserException('Unable to parse XML response');
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Availability;
use App\BusProNet\Model\BaseData;
use Symfony\Component\DomCrawler\Crawler;
class AvailabilitiesParser extends AbstractParser
{
public function parse(Crawler $result): BaseData
{
$availabilities = [];
$result
->filterXPath('//leistungen/leistung')
->each(function (Crawler $node) use (&$availabilities) {
$availability = new Availability();
$availability->serviceId = (int) $node->attr('id');
$availability->status = $node->attr('status');
$availability->available = (int) $node->attr('frei');
$availability->price = $this->stringToFloat($node->attr('preis'));
$availabilities[$availability->serviceId] = $availability;
})
;
return new BaseData($availabilities);
}
}
+155
View File
@@ -0,0 +1,155 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Address;
use App\BusProNet\Model\Booking;
use App\BusProNet\Model\Communication;
use App\BusProNet\Model\PersonalData;
use App\BusProNet\Model\Service;
use Symfony\Component\DomCrawler\Crawler;
class BookingParser extends AbstractParser
{
private readonly ServicesParser $servicesParser;
private readonly RoomsParser $roomsParser;
private readonly PickupsParser $pickupsParser;
private readonly SurchargesParser $surchargesParser;
public function __construct()
{
$this->servicesParser = new ServicesParser();
$this->roomsParser = new RoomsParser();
$this->pickupsParser = new PickupsParser();
$this->surchargesParser = new SurchargesParser();
}
public function parse(Crawler $node): Booking
{
$booking = new Booking();
$booking->id = $this->getIntOrNullValue($node->filterXPath('//idbuchung'));
$booking->agencyId = $this->getIntOrNullValue($node->filterXPath('//idagentur'));
$booking->bookingNumber = $this->getIntOrNullValue($node->filterXPath('//vorgang'));
$booking->invoiceNumber = $this->getIntOrNullValue($node->filterXPath('//zahlungsdaten/rechnung'));
$booking->totalPrice = $this
->stringToFloat($this->getStringOrNullValue($node->filterXPath('//zahlungsdaten/gesamtbetrag')));
$booking->status = $this->getStringOrNullValue($node->filterXPath('//status'));
$travelData = $node->filterXPath('//reise');
$booking->travel = $travelData->attr('bezeichnung');
$booking->travelId = $this->getIntOrNullValue($node->filterXPath('//idreise'));
$booking->travelCode = $travelData->attr('code');
$booking->travelDate = $this->stringToDate($travelData->attr('termin'));
$booking->hotelId = $this->getIntOrNullValue($node->filterXPath('//idpartner'));
$booking->hotelName = $this->getStringOrNullValue($node->filterXPath('//partner'));
$booking->applicant = $this->parsePersonalData($node->filterXPath('//anmelder'));
$participantsStatus = $this
->stringToArray($this->getStringOrNullValue($node->filterXPath('//status_teilnehmer')), '/');
$booking->participantsStatus = array_combine(range(1, count($participantsStatus)), $participantsStatus);
$booking->participants = $this->parseParticipants($node->filterXPath('//teilnehmerliste/teilnehmer'));
$paymentData = $node->filterXPath('//zahlung');
$booking->paymentId = (int) $paymentData->attr('idzahlungsart');
$booking->paymentLabel = $paymentData->attr('bezeichnung');
$booking->paymentType = $paymentData->attr('art');
$transportationData = $node->filterXPath('//beförderungen/beförderung');
if (0 < $transportationData->count()) {
$booking->transportationServices = $this
->servicesParser
->parse($transportationData, Service::CATEGORY_TRANSPORTATION, Service::SOURCE_BOOKING);
}
$additionalServicesData = $node->filterXPath('//zusatzleistungen/zusatzleistung');
if (0 < $additionalServicesData->count()) {
$booking->additionalServices = $this
->servicesParser
->parse($additionalServicesData, Service::CATEGORY_ADDITIONAL, Service::SOURCE_BOOKING);
}
$roomsData = $node->filterXPath('//ferienzielunterbringungen/ferienzielunterbringung');
if (0 < $roomsData->count()) {
$booking->rooms = $this->roomsParser->parse($roomsData);
}
$pickupsData = $node->filterXPath('//zustiege/zustieg');
if (0 < $pickupsData->count()) {
$booking->pickups = $this->pickupsParser->parse($pickupsData);
}
$surchargesData = $node->filterXPath('//zuschlaege/zuschlag');
if (0 < $surchargesData->count()) {
$booking->surcharges = $this->surchargesParser->parse($surchargesData);
}
return $booking;
}
private function parseParticipants(Crawler $node): array
{
$participants = [];
$node->each(function (Crawler $node) use (&$participants) {
$id = (int) $node->attr('id');
$participants[$id] = $this->parsePersonalData($node);
});
return $participants;
}
private function parsePersonalData(Crawler $node): PersonalData
{
$personalData = new PersonalData();
$personalData->addressId = $this->getIntOrNullValue($node->filterXPath('//idadresse'));
$personalData->personId = $this->getIntOrNullValue($node->filterXPath('//idadresseperson'));
$dateString = $this->getStringOrNullValue($node->filterXPath('//geburtsdatum'));
$personalData->dateOfBirth = null !== $dateString
? \DateTimeImmutable::createFromFormat('d.m.Y', $dateString) : null;
$personalData->firstName = $this->getStringOrNullValue($node->filterXPath('//vorname'));
$personalData->name = $this->getStringOrNullValue($node->filterXPath('//name'));
$personalData->salutation = $this->getStringOrNullValue($node->filterXPath('//anrede'));
$personalData->title = $this->getStringOrNullValue($node->filterXPath('//titel'));
$genderValue = $this->getStringOrNullValue($node->filterXPath('//geschlecht'));
$personalData->gender = null !== $genderValue ? strtoupper($genderValue) : null;
$personalData->height = $this->getStringOrNullValue($node->filterXPath('//sonstiges1'));
$personalData->weight = $this->getStringOrNullValue($node->filterXPath('//sonstiges2'));
$personalData->shoeSize = $this->getStringOrNullValue($node->filterXPath('//sonstiges3'));
$addressNode = $node->filterXPath('//anschrift');
if (0 < $addressNode->count()) {
$address = new Address();
$address->street = $this->getStringOrNullValue($addressNode->filterXPath('//strasse'));
$address->postCode = $this->getStringOrNullValue($addressNode->filterXPath('//plz'));
$address->city = $this->getStringOrNullValue($addressNode->filterXPath('//ort'));
$address->country = $this->getStringOrNullValue($addressNode->filterXPath('//land'));
$personalData->address = $address;
}
$contactNode = $node->filterXPath('//kommunikation');
if (0 < $contactNode->count()) {
$communication = new Communication();
$communication->phone = $this->getStringOrNullValue($contactNode->filterXPath('//telefonprivat'));
$communication->mobile = $this->getStringOrNullValue($contactNode->filterXPath('//telefonmobil'));
$communication->email = $this->getStringOrNullValue($contactNode->filterXPath('//email'));
$communication->newsletter = $this
->stringToBool($contactNode->filterXPath('//newsletter')->text('False'));
$personalData->communication = $communication;
}
return $personalData;
}
}
@@ -0,0 +1,18 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\BookingUpdate;
use Symfony\Component\DomCrawler\Crawler;
class BookingUpdateParser extends AbstractParser
{
public function parse(Crawler $node): BookingUpdate
{
$bookingUpdate = new BookingUpdate();
$bookingUpdate->valid = 'möglich' === $node->filterXPath('//aenderung')->text();
$bookingUpdate->totalPrice = $this->stringToFloat($node->filterXPath('//gesamtpreis')->text());
return $bookingUpdate;
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\BaseData;
use App\BusProNet\Model\Booking;
use Symfony\Component\DomCrawler\Crawler;
class BookingsParser extends AbstractParser
{
public function parse(Crawler $result): BaseData
{
$bookings = [];
$items = $result->filterXPath('//vorgaenge/vorgang');
if (0 === $items->count()) {
return new BaseData($bookings);
}
$items->each(function (Crawler $node) use (&$bookings) {
$booking = new Booking();
$booking->id = $this->getIntOrNullValue($node->filterXPath('//id'));
$booking->bookingNumber = $this->getIntOrNullValue($node->filterXPath('//vorgangsnummer'));
$booking->status = $this->getStringOrNullValue($node->filterXPath('//status'));
$booking->participantCount = $this->getIntOrNullValue($node->filterXPath('//personen'));
$booking->price = $this->stringToFloat($this->getStringOrNullValue($node->filterXPath('//preis')));
$booking->bookingDate = $this
->stringToDateTime($this->getStringOrNullValue($node->filterXPath('//buchungsdatum')));
$booking->travel = $this->getStringOrNullValue($node->filterXPath('//reise'));
$booking->travelId = $this->getIntOrNullValue($node->filterXPath('//idreise'));
$booking->travelDate = $this
->stringToDate($this->getStringOrNullValue($node->filterXPath('//reisedatum')));
$booking->document = $this
->stringToBool($this->getStringOrNullValue($node->filterXPath('//reisedokument')));
$booking->payment = $this
->stringToFloat($this->getStringOrNullValue($node->filterXPath('//zahlung')));
$bookings[] = $booking;
});
return new BaseData($bookings);
}
}
@@ -0,0 +1,30 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\BaseData;
use App\BusProNet\Model\Country;
use Symfony\Component\DomCrawler\Crawler;
class CountriesParser
{
public function parse(Crawler $result): BaseData
{
$countries = [];
$result
->filterXPath('//laender/land')
->each(function (Crawler $node) use (&$countries) {
$country = new Country();
$country->id = (int) $node->attr('id');
$country->name = $node->attr('bezeichnung');
$country->token = $node->attr('kuerzel');
$country->nationality = $node->attr('nationalitaet');
$countries[$country->token] = $country;
})
;
return new BaseData($countries);
}
}
@@ -0,0 +1,95 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\CrmAction;
use App\BusProNet\Model\CrmAttributes;
use App\BusProNet\Model\CrmSelection;
use App\BusProNet\Model\CrmSelectionGroup;
use App\BusProNet\TypeConversionTrait;
use Symfony\Component\DomCrawler\Crawler;
class CrmAttributesResponseParser
{
use TypeConversionTrait;
private const BPN_CRM_ID_ADMIN = 1292;
private const BPN_CRM_ID_MANAGER = 1293;
private const BPN_CRM_ID_TEAMER = 1070;
public function parse(Crawler $result): CrmAttributes
{
$groups = [];
$actions = [];
$roles = [];
$hotelCode = null;
$result
->filterXPath('//selektionsmerkmale/selektionsgruppe')
->each(function (Crawler $node) use (&$groups, &$roles, &$hotelCode) {
$group = new CrmSelectionGroup();
$group->id = (int) $node->attr('id');
$group->label = $node->attr('bezeichnung');
$attributes = [];
$node
->filterXPath('//selektion')
->each(function (Crawler $node) use (&$attributes, &$roles, &$hotelCode) {
$attribute = new CrmSelection();
$attribute->id = (int) $node->attr('id');
$attribute->label = $node->attr('bezeichnung');
$attribute->mutable = $this->stringToBool($node->attr('aenderbar'));
$attribute->selected = $this->stringToBool($node->attr('auswahl'));
if (1 === preg_match('/^Hausleitung ([A-Z0-9]+)$/', $attribute->label, $matches) && true === $attribute->selected) {
$roles[] = 'ROLE_HOUSE_MANAGER';
$hotelCode = $matches[1];
}
if (static::BPN_CRM_ID_ADMIN === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_ADMIN';
}
if (static::BPN_CRM_ID_MANAGER === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_MANAGER';
}
if (static::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) {
$roles[] = 'ROLE_TEAMER';
}
$attributes[] = $attribute;
})
;
$group->selections = $attributes;
$groups[] = $group;
})
;
$result
->filterXPath('//crmaktionen/crmaktion')
->each(function (Crawler $node) use (&$actions) {
$crmAction = new CrmAction();
$crmAction->id = (int) $node->attr('id');
$crmAction->code = $node->attr('code');
$crmAction->label = $node->attr('bezeichnung');
$crmAction->mutable = $this->stringToBool($node->attr('aenderbar'));
$crmAction->selected = $this->stringToBool($node->attr('auswahl'));
$actions[] = $crmAction;
})
;
$response = new CrmAttributes();
$response->selectionGroups = $groups;
$response->crmActions = $actions;
$response->roles = $roles;
$response->admin = in_array('ROLE_ADMIN', $roles);
$response->manager = in_array('ROLE_MANAGER', $roles);
$response->houseManager = in_array('ROLE_HOUSE_MANAGER', $roles);
$response->teamer = in_array('ROLE_TEAMER', $roles);
$response->hotelCode = $hotelCode;
return $response;
}
}
@@ -0,0 +1,60 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\File;
use PhpZip\Exception\ZipException;
use PhpZip\ZipFile;
use Symfony\Component\DomCrawler\Crawler;
class DocumentsParser
{
public function parseConfirmation(Crawler $result): ?File
{
$pdfData = $this->decode($result->filterXPath('//bestaetigungpdf'));
return new File('bestaetigung.pdf', $pdfData, 'application/pdf');
}
public function parseDocuments(Crawler $result): ?File
{
$documents = $result->filterXPath('//reisedokumente/reisedokument');
if (1 === $documents->count()) {
$node = $documents->first();
[$filename, $pdfData] = $this->getFileInfo($node);
return new File($filename.'.pdf', $pdfData, 'application/pdf');
}
// In case of multiple documents create ZIP file
try {
$zip = new ZipFile();
$documents->each(function (Crawler $node) use (&$zip) {
[$filename, $pdfData] = $this->getFileInfo($node);
$zip->addFromString($filename.'.pdf', $pdfData);
});
return new File('reisedokumente.zip', $zip->outputAsString(), 'application/zip');
}
catch (ZipException $e) {
return null;
}
}
private function decode(Crawler $node): string
{
[, $pdfData] = explode(',', $node->text());
return base64_decode($pdfData);
}
private function getFileInfo(Crawler $node): array
{
$pdfData = $this->decode($node->filterXPath('//pdf'));
$filename = $node->filterXPath('//datei')->text();
return [$filename, $pdfData];
}
}
@@ -0,0 +1,42 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\BaseData;
use App\BusProNet\Model\MutableData;
use Symfony\Component\DomCrawler\Crawler;
class MutableDataParser extends AbstractParser
{
public function parse(Crawler $result): BaseData
{
$mutableFields = [];
$result
->filterXPath('//änderungen/änderung')
->each(function (Crawler $node) use (&$mutableFields) {
$category = match ($node->attr('art')) {
'anzahl_teilnehmer' => MutableData::CATEGORY_PARTICIPANT_COUNT,
'beförderung' => MutableData::CATEGORY_TRANSPORTATION,
'zustieg' => MutableData::CATEGORY_PICKUP,
'unterbringung' => MutableData::CATEGORY_ACCOMMODATION,
'zusatzleistung' => MutableData::CATEGORY_ADDITIONAL_SERVICES,
'teilnehmerdaten' => MutableData::CATEGORY_PARTICIPANT_DATA,
default => null,
};
if (null !== $category) {
$mutableField = new MutableData($category, $this->stringToBool($node->attr('möglich')));
if (null !== $until = $node->attr('möglichbiszum')) {
$mutableField->mutableBefore = $this->stringToDate($until);
}
$mutableFields[$category] = $mutableField;
}
})
;
return new BaseData($mutableFields);
}
}
@@ -0,0 +1,17 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Notification;
use Symfony\Component\DomCrawler\Crawler;
class NotificationParser extends AbstractParser
{
public function parse(Crawler $result): Notification
{
$number = $this->getIntOrNullValue($result->filterXPath('//satz/nr'));
$text = $this->getStringOrNullValue($result->filterXPath('//satz/text'));
return new Notification($number, $text);
}
}
@@ -0,0 +1,70 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Address;
use App\BusProNet\Model\Communication;
use App\BusProNet\Model\PersonalData;
use Symfony\Component\DomCrawler\Crawler;
use voku\helper\AntiXSS;
class PersonalDataParser extends AbstractParser
{
public function parse(Crawler $node): PersonalData
{
$personalData = new PersonalData();
$personalData->addressId = $this->getIntOrNullValue($node->filterXPath('//idadresse'));
$personalData->personId = $this->getIntOrNullValue($node->filterXPath('//idperson'));
$addressDataNode = $node->filterXPath('//adressdaten');
$dateString = $this->getStringOrNullValue($addressDataNode->filterXPath('//geburtsdatum'));
$personalData->dateOfBirth = null !== $dateString ?
\DateTimeImmutable::createFromFormat('d.m.Y', $dateString) : null;
$personalData->firstName = $this->getStringOrNullValue($addressDataNode->filterXPath('//vorname'));
$personalData->name = $this->getStringOrNullValue($addressDataNode->filterXPath('//name'));
$personalData->salutation = $this->getStringOrNullValue($addressDataNode->filterXPath('//anrede'));
$personalData->title = $this->getStringOrNullValue($addressDataNode->filterXPath('//titel'));
$personalData->gender = strtoupper($this->getStringOrNullValue($addressDataNode->filterXPath('//geschlecht')));
$personalData->height = $this->getStringOrNullValue($addressDataNode->filterXPath('//sonstiges1'));
$personalData->weight = $this->getStringOrNullValue($addressDataNode->filterXPath('//sonstiges2'));
$personalData->shoeSize = $this->getStringOrNullValue($addressDataNode->filterXPath('//sonstiges3'));
$addressNode = $addressDataNode->filterXPath('//anschrift');
if (0 < $addressNode->count()) {
$address = new Address();
$address->street = $this->getStringOrNullValue($addressNode->filterXPath('//strasse'));
$address->postCode = $this->getStringOrNullValue($addressNode->filterXPath('//plz'));
$address->city = $this->getStringOrNullValue($addressNode->filterXPath('//ort')) ;
$address->country = $this->getStringOrNullValue($addressNode->filterXPath('//land')) ;
$personalData->address = $address;
}
$contactDataNode = $addressDataNode->filterXPath('//kommunikation');
if (0 < $contactDataNode->count()) {
$communication = new Communication();
$communication->phone = $this->getStringOrNullValue($contactDataNode->filterXPath('//telefonprivat'));
$communication->mobile = $this->getStringOrNullValue($contactDataNode->filterXPath('//telefonmobil'));
$communication->email = $this->getStringOrNullValue($contactDataNode->filterXPath('//email'));
$communication->newsletter = $this
->stringToBool($this->getStringOrNullValue($contactDataNode->filterXPath('//newsletter')));
$personalData->communication = $communication;
}
$remarksNode = $addressDataNode->filterXPath('//bemerkung');
if (0 < $remarksNode->count()) {
$antiXss = new AntiXSS();
$personalData->remarks = $antiXss->xss_clean($remarksNode->text());
}
return $personalData;
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Pickup;
use Symfony\Component\DomCrawler\Crawler;
class PickupsParser extends AbstractParser
{
public function parse(Crawler $result): array
{
$pickups = [];
$result->each(function (Crawler $node) use (&$pickups) {
$pickupId = (int) $node->attr('idzustieg');
$pickupDate = $node->attr('datum');
$pickupTime = $node->attr('zeit');
$price = $node->attr('preis') ? $this->stringToFloat($node->attr('preis')) : null;
$pickup = new Pickup();
$pickup->id = $pickupId;
$pickup->code = $node->attr('code');
$pickup->city = $this->getStringOrNullValue($node->filterXPath('//ort'));
$pickup->postalCode = $this->getStringOrNullValue($node->filterXPath('//plz'));
$pickup->street = $this->getStringOrNullValue($node->filterXPath('//strasse'));
$pickup->time = $this->stringToDateTime($pickupDate.' '.$pickupTime);
$pickup->price = $price;
$mapping = $this->stringToArray($node->attr('zuordnung'));
$pickup->mapping = array_map('intval', $mapping);
$pickups[$pickupId] = $pickup;
});
return $pickups;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Room;
use Symfony\Component\DomCrawler\Crawler;
class RoomsParser extends AbstractParser
{
public function parse(Crawler $result): array
{
$rooms = [];
$result->each(function (Crawler $node) use (&$rooms) {
$room = new Room();
$room->id = (int) $node->attr('idzimmer');;
$room->label = $node->attr('zimmer');
$room->category = $node->attr('kategorie');
$room->boardId = (int) $node->attr('idverpflegung');
$room->dateFrom = $node->attr('anreise') ?
$this->stringToDate($node->attr('anreise')) : null;
$room->dateTo = $node->attr('abreise') ?
$this->stringToDate($node->attr('abreise')) : null;
$room->totalCount = (int) $node->attr('anzahl');
$room->minPax = (int) $node->attr('minpax');
$room->maxPax = (int) $node->attr('maxpax');
$room->board = $node->attr('verpflegung');
$mapping = $this->stringToArray($node->attr('zuordnung'));
$room->mapping = array_map('intval', $mapping);
$room->totalPrice = $this->stringToFloat($node->attr('gesamtpreis'));
$individualPrices = array_map(
function ($price) {
return $this->stringToFloat($price);
}, $this->stringToArray($node->attr('einzelpreis', ''), '/')
);
$room->individualPrice = array_combine($mapping, $individualPrices);
$rooms[$room->id] = $room;
});
return $rooms;
}
}
@@ -0,0 +1,44 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Service;
use Symfony\Component\DomCrawler\Crawler;
class ServicesParser extends AbstractParser
{
public function parse(Crawler $result, string $category, string $source): array
{
$services = [];
$result->each(function (Crawler $node) use (&$services, $category, $source) {
$service = new Service();
$service->id = (int) $node->attr('idleistung');
$service->category = $category;
$service->source = $source;
$service->label = $node->attr('leistung');
$service->dateFrom = $node->attr('termin') ?
$this->stringToDate($node->attr('termin')) : null;
$service->dateTo = $node->attr('terminbis') ?
$this->stringToDate($node->attr('terminbis')) : null;
$service->subType = $node->attr('unterart');
$service->totalCount = $node->attr('anzahl') ? (int) $node->attr('anzahl') : null;
$mapping = $this->stringToArray($node->attr('zuordnung'));
$service->mapping = array_map('intval', $mapping);
$service->totalPrice = $this->stringToFloat($node->attr('gesamtpreis'));
$individualPrices = array_map(
function ($price) {
return $this->stringToFloat($price);
}, $this->stringToArray($node->attr('einzelpreis', ''), '/')
);
$service->individualPrice = array_combine($mapping, $individualPrices);
if (Service::CATEGORY_TRANSPORTATION === $category) {
$service->direction = $node->attr('richtung');
}
$services[$service->id] = $service;
});
return $services;
}
}
@@ -0,0 +1,33 @@
<?php
namespace App\BusProNet\XmlParser;
use App\BusProNet\Model\Surcharge;
use Symfony\Component\DomCrawler\Crawler;
class SurchargesParser extends AbstractParser
{
public function parse(Crawler $result): array
{
$surcharges = [];
$result->each(function (Crawler $node) use (&$surcharges) {
$surcharge = new Surcharge();
$surchargeLabel = $node->attr('bezeichnung');
$surcharge->label = empty($surchargeLabel) ? 'unbekannt' : $surchargeLabel;
$surcharge->totalPrice = $this->stringToFloat($node->attr('gesamtpreis'));
$mapping = $this->stringToArray($node->attr('zuordnung'));
$surcharge->mapping = array_map('intval', $mapping);
$individualPrices = array_map(
function ($price) {
return $this->stringToFloat($price);
}, $this->stringToArray($node->attr('einzelpreis', ''), '/')
);
$surcharge->individualPrice = array_combine($mapping, $individualPrices);
$surcharges[] = $surcharge;
});
return $surcharges;
}
}