diff --git a/composer.json b/composer.json
index ba43bff..1a02cba 100644
--- a/composer.json
+++ b/composer.json
@@ -22,6 +22,7 @@
"symfony/asset": "6.4.*",
"symfony/console": "6.4.*",
"symfony/doctrine-messenger": "6.4.*",
+ "symfony/dom-crawler": "6.4.*",
"symfony/dotenv": "6.4.*",
"symfony/expression-language": "6.4.*",
"symfony/flex": "^2",
diff --git a/composer.lock b/composer.lock
index c567a7f..e0c4d32 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "40436ef2d80ce14b8acf4decf58c66e8",
+ "content-hash": "b7889b1556dde930fda7eb3acf8e7532",
"packages": [
{
"name": "carbonphp/carbon-doctrine-types",
@@ -1506,6 +1506,73 @@
},
"time": "2024-06-03T08:48:36+00:00"
},
+ {
+ "name": "masterminds/html5",
+ "version": "2.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
+ },
+ "time": "2024-03-31T07:05:07+00:00"
+ },
{
"name": "monolog/monolog",
"version": "3.8.0",
@@ -3207,6 +3274,73 @@
],
"time": "2024-10-18T09:45:38+00:00"
},
+ {
+ "name": "symfony/dom-crawler",
+ "version": "v6.4.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/dom-crawler.git",
+ "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4304e6ad5c894a9c72831ad459f627bfd35d766d",
+ "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d",
+ "shasum": ""
+ },
+ "require": {
+ "masterminds/html5": "^2.6",
+ "php": ">=8.1",
+ "symfony/polyfill-ctype": "~1.8",
+ "symfony/polyfill-mbstring": "~1.0"
+ },
+ "require-dev": {
+ "symfony/css-selector": "^5.4|^6.0|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\DomCrawler\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Eases DOM navigation for HTML and XML documents",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/dom-crawler/tree/v6.4.16"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2024-11-13T15:06:22+00:00"
+ },
{
"name": "symfony/dotenv",
"version": "v6.4.16",
@@ -8377,73 +8511,6 @@
],
"time": "2024-11-27T21:35:20+00:00"
},
- {
- "name": "masterminds/html5",
- "version": "2.9.0",
- "source": {
- "type": "git",
- "url": "https://github.com/Masterminds/html5-php.git",
- "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
- "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
- "shasum": ""
- },
- "require": {
- "ext-dom": "*",
- "php": ">=5.3.0"
- },
- "require-dev": {
- "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
- },
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "2.7-dev"
- }
- },
- "autoload": {
- "psr-4": {
- "Masterminds\\": "src"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Matt Butcher",
- "email": "technosophos@gmail.com"
- },
- {
- "name": "Matt Farina",
- "email": "matt@mattfarina.com"
- },
- {
- "name": "Asmir Mustafic",
- "email": "goetas@gmail.com"
- }
- ],
- "description": "An HTML5 parser and serializer.",
- "homepage": "http://masterminds.github.io/html5-php",
- "keywords": [
- "HTML5",
- "dom",
- "html",
- "parser",
- "querypath",
- "serializer",
- "xml"
- ],
- "support": {
- "issues": "https://github.com/Masterminds/html5-php/issues",
- "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
- },
- "time": "2024-03-31T07:05:07+00:00"
- },
{
"name": "myclabs/deep-copy",
"version": "1.12.1",
@@ -10272,73 +10339,6 @@
],
"time": "2024-09-25T14:18:03+00:00"
},
- {
- "name": "symfony/dom-crawler",
- "version": "v6.4.16",
- "source": {
- "type": "git",
- "url": "https://github.com/symfony/dom-crawler.git",
- "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4304e6ad5c894a9c72831ad459f627bfd35d766d",
- "reference": "4304e6ad5c894a9c72831ad459f627bfd35d766d",
- "shasum": ""
- },
- "require": {
- "masterminds/html5": "^2.6",
- "php": ">=8.1",
- "symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.0"
- },
- "require-dev": {
- "symfony/css-selector": "^5.4|^6.0|^7.0"
- },
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\DomCrawler\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Eases DOM navigation for HTML and XML documents",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/symfony/dom-crawler/tree/v6.4.16"
- },
- "funding": [
- {
- "url": "https://symfony.com/sponsor",
- "type": "custom"
- },
- {
- "url": "https://github.com/fabpot",
- "type": "github"
- },
- {
- "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
- "type": "tidelift"
- }
- ],
- "time": "2024-11-13T15:06:22+00:00"
- },
{
"name": "symfony/maker-bundle",
"version": "v1.61.0",
@@ -10655,7 +10655,8 @@
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
- "ext-simplexml": "*"
+ "ext-simplexml": "*",
+ "ext-sockets": "*"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php
index 0676ee0..abda90e 100644
--- a/src/BusProNet/ApiClient.php
+++ b/src/BusProNet/ApiClient.php
@@ -2,7 +2,7 @@
namespace App\BusProNet;
-use App\BusProNet\ApiResponseParser\ResponseParser;
+use App\BusProNet\XmlParser\ApiResponseParser;
use App\BusProNet\DataProcessor\BookingDataProcessor;
use App\BusProNet\Exception\ApiClientException;
use App\BusProNet\Exception\ResponseParserException;
@@ -34,9 +34,9 @@ class ApiClient
public function __construct(
private readonly SerializerInterface $serializer,
- private readonly ResponseParser $responseParser,
- private readonly LoggerInterface $logger,
- array $options
+ private readonly ApiResponseParser $responseParser,
+ private readonly LoggerInterface $logger,
+ array $options
) {
$this->config = $this->resolveOptions($options);
}
@@ -180,16 +180,15 @@ class ApiClient
* @throws ApiClientException
* @throws ResponseParserException
*/
- public function updateBooking(BookingData $formData, bool $dryRun = true): Notification|BookingUpdate
+ public function updateBooking(BookingData $formData): Notification|BookingUpdate
{
- $mode = $dryRun ? 'Anfrage' : 'Buchung';
$payload = (new BookingDataProcessor())->createUpdateRequestPayload($formData);
$data = [
'user' => $this->config['bpn_username'],
'key' => $this->createKey($this->config['bpn_username'], $this->config['bpn_password'], static::TYPE_BOOKING_UPDATE),
'satz' => ['@typ' => static::TYPE_BOOKING_UPDATE],
- 'buchungsart' => $mode,
+ 'buchungsart' => 'Buchung',
...$payload,
];
diff --git a/src/BusProNet/ApiResponseParser/AvailabilitiesResponseParser.php b/src/BusProNet/ApiResponseParser/AvailabilitiesResponseParser.php
deleted file mode 100644
index b6d2a9e..0000000
--- a/src/BusProNet/ApiResponseParser/AvailabilitiesResponseParser.php
+++ /dev/null
@@ -1,32 +0,0 @@
-leistungen->leistung as $item) {
- $attributes = $item->attributes();
- $id = (int) $attributes['id'];
-
- $availability = new Availability();
- $availability->serviceId = $id;
- $availability->status = (string) $attributes['status'];
- $availability->available = (int) $attributes['frei'];
- $availability->price = $this->stringToFloat((string) $attributes['preis']);
-
- $availabilities[$id] = $availability;
- }
-
- return new BaseData($availabilities);
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/BookingResponseParser.php b/src/BusProNet/ApiResponseParser/BookingResponseParser.php
deleted file mode 100644
index 8cb8e1d..0000000
--- a/src/BusProNet/ApiResponseParser/BookingResponseParser.php
+++ /dev/null
@@ -1,255 +0,0 @@
-reise;
-
- $booking = new Booking();
-
- $booking->id = (int) $xml->idbuchung;
- $booking->agencyId = (int) $xml->idagentur;
- $booking->bookingNumber = (int) $xml->vorgang;
- $booking->invoiceNumber = (int) $xml->zahlungsdaten->rechnung;
- $booking->totalPrice = $this->stringToFloat((string) $xml->zahlungsdaten->gesamtbetrag);
- $booking->status = (string) $xml->status;
-
- $booking->travel = (string) $travelData->attributes()['bezeichnung'];
- $booking->travelId = (int) $xml->idreise;
- $booking->travelCode = (string) $travelData->attributes()['code'];
- $booking->travelDate = $this->stringToDate((string) $travelData->attributes()['termin']);
-
- $booking->hotelId = (int) $xml->idpartner;
- $booking->hotelName = (string) $xml->partner;
-
- $booking->applicant = $this->parsePersonalData($xml->anmelder);
-
- $participantsStatus = $this->stringToArray((string) $xml->status_teilnehmer, '/');
- $booking->participantsStatus = array_combine(range(1, count($participantsStatus)), $participantsStatus);
-
- $booking->participants = $this->parseParticipants($xml);
-
- $paymentData = $xml->zahlung;
- $booking->paymentId = (int) $paymentData->attributes()['idzahlungsart'];
- $booking->paymentLabel = (string) $paymentData->attributes()['bezeichnung'];
- $booking->paymentType = (string) $paymentData->attributes()['art'];
-
- if ($xml->beförderungen) {
- $booking->transportationServices = $this
- ->parseServices($xml->beförderungen->beförderung, Service::CATEGORY_TRANSPORTATION);
- }
- if ($xml->zusatzleistungen) {
- $booking->additionalServices = $this
- ->parseServices($xml->zusatzleistungen->zusatzleistung, Service::CATEGORY_ADDITIONAL);
- }
- if ($xml->ferienzielunterbringungen) {
- $booking->rooms = $this->parseRooms($xml->ferienzielunterbringungen);
- }
- if ($xml->zustiege) {
- $booking->pickups = $this->parsePickups($xml->zustiege);
- }
- if ($xml->zuschlaege) {
- $booking->surcharges = $this->parseSurcharges($xml->zuschlaege);
- }
-
- return $booking;
- }
-
- private function parseParticipants(\SimpleXMLElement $xml): array
- {
- $participants = [];
-
- foreach ($xml->teilnehmerliste->teilnehmer as $participant) {
- $id = (int) $participant->attributes()['id'];
- $participants[$id] = $this->parsePersonalData($participant);
- }
-
- return $participants;
- }
-
- private function parsePersonalData(\SimpleXMLElement $xml): PersonalData
- {
- $personalData = new PersonalData();
- $personalData->addressId = $xml->idadresse ? (int) $xml->idadresse : null;
- $personalData->personId = (int) $xml->idadresseperson;
- $personalData->name = (string) $xml->name;
- $personalData->firstName = (string) $xml->vorname;
- $personalData->salutation = (string) $xml->anrede;
- $personalData->title = (string) $xml->titel;
- $personalData->gender = $xml->geschlecht ? strtoupper((string) $xml->geschlecht) : null;
- $personalData->dateOfBirth = $xml->geburtsdatum ? $this->stringToDate((string) $xml->geburtsdatum) : null;
- $personalData->nationality = (string) $xml->nationalitaet;
- $personalData->height = $xml->sonstiges1 ? (int) $xml->sonstiges1 : null;
- $personalData->weight = $xml->sonstiges2 ? (int) $xml->sonstiges2 : null;
- $personalData->shoeSize = $xml->sonstiges3 ? (int) $xml->sonstiges3 : null;
-
- if ($xml->anschrift) {
- $address = new Address();
- $address->street = (string) $xml->anschrift->strasse;
- $address->postCode = (string) $xml->anschrift->plz;
- $address->city = (string) $xml->anschrift->ort;
- $address->district = (string) $xml->anschrift->ortsteil;
- $address->country = (string) $xml->anschrift->land;
-
- $personalData->address = $address;
- }
-
- if ($xml->kommunikation) {
- $communication = new Communication();
- $communication->email = (string) $xml->kommunikation->email;
- $communication->phone = (string) $xml->kommunikation->telefonprivat;
- $communication->mobile = (string) $xml->kommunikation->telefonmobil;
-
- $personalData->communication = $communication;
- }
-
- if ($xml->bemerkung) {
- $antiXss = new AntiXSS();
- $remarks = $antiXss->xss_clean((string) $xml->bemerkung);
- $personalData->remarks = $remarks;
- }
-
- return $personalData;
- }
-
- private function parseServices(\SimpleXMLElement $xml, string $category): array
- {
- $services = [];
-
- foreach ($xml as $service) {
- $attributes = $service->attributes();
- $id = (int) $attributes['idleistung'];
-
- $service = new Service();
- $service->id = $id;
- $service->category = $category;
- $service->source = Service::SOURCE_BOOKING;
- $service->label = (string) $attributes['leistung'];
- $service->dateFrom = $attributes['termin'] ? $this->stringToDate((string) $attributes['termin']) : null;
- $service->dateTo = $attributes['terminbis'] ? $this->stringToDate((string) $attributes['terminbis']) : null;
- $service->subType = (string) $attributes['unterart'];
- $service->totalCount = (int) $attributes['anzahl'];
- $mapping = $this->stringToArray((string) $attributes['zuordnung']);
- $service->mapping = array_map('intval', $mapping);
- $service->totalPrice = $this->stringToFloat((string) $attributes['gesamtpreis']);
- $individualPrices = array_map(
- function ($price) {
- return $this->stringToFloat((string) $price);
- }, $this->stringToArray((string) $attributes['einzelpreis'], '/')
- );
- $service->individualPrice = array_combine($mapping, $individualPrices);
- if (Service::CATEGORY_TRANSPORTATION === $category) {
- $service->direction = (string) $attributes['richtung'];
- }
-
- $services[$id] = $service;
- }
-
- return $services;
- }
-
- private function parseRooms(\SimpleXMLElement $xml): array
- {
- $rooms = [];
-
- foreach ($xml->ferienzielunterbringung as $item) {
- $attributes = $item->attributes();
- $id = (int) $attributes['idzimmer'];
-
- $room = new Room();
- $room->id = $id;
- $room->label = (string) $attributes['zimmer'];
- $room->category = (string) $attributes['kategorie'];
- $room->boardId = (int) $attributes['idverpflegung'];
- $room->dateFrom = $attributes['anreise'] ? $this->stringToDate((string) $attributes['anreise']) : null;
- $room->dateTo = $attributes['abreise'] ? $this->stringToDate((string) $attributes['abreise']) : null;
- $room->totalCount = (int) $attributes['anzahl'];
- $room->minPax = (int) $attributes['minpax'];
- $room->maxPax = (int) $attributes['maxpax'];
- $room->board = (string) $attributes['verpflegung'];
- $mapping = $this->stringToArray((string) $attributes['zuordnung']);
- $room->mapping = array_map('intval', $mapping);
- $room->totalPrice = $this->stringToFloat((string) $attributes['gesamtpreis']);
- $individualPrices = array_map(
- function ($price) {
- return $this->stringToFloat((string) $price);
- }, $this->stringToArray((string) $attributes['einzelpreis'], '/')
- );
- $room->individualPrice = array_combine($mapping, $individualPrices);
-
- $rooms[$id] = $room;
- }
-
- return $rooms;
- }
-
- private function parsePickups(\SimpleXMLElement $xml): array
- {
- $pickups = [];
-
- foreach ($xml->zustieg as $item) {
- $attributes = $item->attributes();
- $pickupId = (int) $attributes['idzustieg'];
- $pickupDate = (string) $attributes['datum'];
- $pickupTime = (string) $attributes['zeit'];
-
- $pickup = new Pickup();
- $pickup->id = $pickupId;
- $pickup->code = (string) $attributes['code'];
- $pickup->city = (string) $xml->ort;
- $pickup->postalCode = (string) $xml->plz;
- $pickup->street = (string) $xml->strasse;
- $pickup->time = $this->stringToDateTime($pickupDate.' '.$pickupTime);
- $pickup->price = $attributes['preis'] ? $this->stringToFloat((string) $attributes['preis']) : null;
- $mapping = $this->stringToArray((string) $attributes['zuordnung']);
- $pickup->mapping = array_map('intval', $mapping);
-
- $pickups[$pickupId] = $pickup;
- }
-
- return $pickups;
- }
-
- private function parseSurcharges(\SimpleXMLElement $xml): array
- {
- $surcharges = [];
-
- foreach ($xml->zuschlag as $item) {
- $attributes = $item->attributes();
-
- $label = empty($attributes['bezeichnung']) ? 'unbekannt' : (string) $attributes['bezeichnung'];
-
- $surcharge = new Surcharge();
- $surcharge->label = $label;
- $surcharge->totalPrice = $this->stringToFloat((string) $attributes['gesamtpreis']);
- $mapping = $this->stringToArray((string) $attributes['zuordnung']);
- $surcharge->mapping = array_map('intval', $mapping);
- $individualPrices = array_map(
- function ($price) {
- return $this->stringToFloat((string) $price);
- }, $this->stringToArray((string) $attributes['einzelpreis'], '/')
- );
- $surcharge->individualPrice = array_combine($mapping, $individualPrices);
-
- $surcharges[] = $surcharge;
- }
-
- return $surcharges;
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php b/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php
deleted file mode 100644
index b860a95..0000000
--- a/src/BusProNet/ApiResponseParser/BookingUpdateResponseParser.php
+++ /dev/null
@@ -1,20 +0,0 @@
-valid = 'möglich' === (string) $xml->aenderung;
- $bookingUpdate->totalPrice = $this->stringToFloat((string) $xml->gesamtpreis);
-
- return $bookingUpdate;
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/BookingsResponseParser.php b/src/BusProNet/ApiResponseParser/BookingsResponseParser.php
deleted file mode 100644
index 2592cd0..0000000
--- a/src/BusProNet/ApiResponseParser/BookingsResponseParser.php
+++ /dev/null
@@ -1,37 +0,0 @@
-vorgaenge->vorgang as $item) {
- $booking = new Booking();
-
- $booking->id = (int) $item->id;
- $booking->bookingNumber = (int) $item->vorgangsnummer;
- $booking->status = (string) $item->status;
- $booking->participantCount = (int) $item->personen;
- $booking->price = $this->stringToFloat((string) $item->preis);
- $booking->bookingDate = $this->stringToDateTime((string) $item->buchungsdatum);
- $booking->travel = (string) $item->reise;
- $booking->travelId = (int) $item->idreise;
- $booking->travelDate = $this->stringToDate((string) $item->reisedatum);
- $booking->document = $this->stringToBool((string) $item->reisedokument);
- $booking->payment = $this->stringToFloat((string) $item->zahlung);
-
- $bookings[] = $booking;
- }
-
- return new BaseData($bookings);
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/CountriesResponseParser.php b/src/BusProNet/ApiResponseParser/CountriesResponseParser.php
deleted file mode 100644
index f392979..0000000
--- a/src/BusProNet/ApiResponseParser/CountriesResponseParser.php
+++ /dev/null
@@ -1,28 +0,0 @@
-laender->land as $item) {
- $itemAttributes = $item->attributes();
- $token = (string) $itemAttributes['kuerzel'];
- $country = new Country();
- $country->id = (int) $itemAttributes['id'];
- $country->name = (string) $itemAttributes['bezeichnung'];
- $country->token = (string) $itemAttributes['kuerzel'];
- $country->nationality = (string) $itemAttributes['nationalitaet'];
-
- $countries[$token] = $country;
- }
-
- return new BaseData($countries);
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/CrmAttributesResponseParser.php b/src/BusProNet/ApiResponseParser/CrmAttributesResponseParser.php
deleted file mode 100644
index 3e69954..0000000
--- a/src/BusProNet/ApiResponseParser/CrmAttributesResponseParser.php
+++ /dev/null
@@ -1,84 +0,0 @@
-selektionsmerkmale->selektionsgruppe as $item) {
- $group = new CrmSelectionGroup();
- $group->id = (int) $item->attributes()['id'];
- $group->label = (string) $item->attributes()['bezeichnung'];
- $attributes = [];
-
- foreach ($item->selektion as $subItem) {
- $subItemAttributes = $subItem->attributes();
- $attributeLabel = (string) $subItemAttributes['bezeichnung'];
- $attribute = new CrmSelection();
- $attribute->id = (int) $subItemAttributes['id'];
- $attribute->label = $attributeLabel;
- $attribute->mutable = $this->stringToBool((string) $subItemAttributes['aenderbar']);
- $attribute->selected = $this->stringToBool((string) $subItemAttributes['auswahl']);
-
- if (1 === preg_match('/^Hausleitung ([A-Z0-9]+)$/', $attributeLabel, $matches) && true === $attribute->selected) {
- $isHouseManager = true;
- $hotelCode = $matches[1];
- }
- if (static::BPN_CRM_ID_ADMIN === $attribute->id && true === $attribute->selected) {
- $isAdmin = true;
- }
- if (static::BPN_CRM_ID_MANAGER === $attribute->id && true === $attribute->selected) {
- $isManager = true;
- }
- if (static::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) {
- $isTeamer = true;
- }
-
- $attributes[] = $attribute;
- }
- $group->selections = $attributes;
- $groups[] = $group;
- }
-
- foreach ($xml->crmaktionen->crmaktion as $item) {
- $attributes = $item->attributes();
- $crmAction = new CrmAction();
- $crmAction->id = (int) $attributes['id'];
- $crmAction->code = (string) $attributes['code'];
- $crmAction->label = (string) $attributes['bezeichnung'];
- $crmAction->mutable = $this->stringToBool((string) $attributes['aenderbar']);
- $crmAction->selected = $this->stringToBool((string) $attributes['auswahl']);
-
- $actions[] = $crmAction;
- }
-
- $response = new CrmAttributes();
- $response->selectionGroups = $groups;
- $response->crmActions = $actions;
- $response->admin = $isAdmin;
- $response->manager = $isManager;
- $response->houseManager = $isHouseManager;
- $response->teamer = $isTeamer;
- $response->hotelCode = $hotelCode;
-
- return $response;
- }
-}
diff --git a/src/BusProNet/ApiResponseParser/DocumentsResponseParser.php b/src/BusProNet/ApiResponseParser/DocumentsResponseParser.php
deleted file mode 100644
index b024fde..0000000
--- a/src/BusProNet/ApiResponseParser/DocumentsResponseParser.php
+++ /dev/null
@@ -1,43 +0,0 @@
-bestaetigungpdf;
- [, $pdfData] = explode(',', $item);
-
- return new File('bestaetigung.pdf', base64_decode($pdfData), 'application/pdf');
- }
-
- public function parseDocuments(\SimpleXMLElement $xml): ?File
- {
- if (1 === count($xml->reisedokumente->reisedokument)) {
- $item = $xml->reisedokumente->reisedokument[0];
- [, $pdfData] = explode(',', $item->pdf);
-
- return new File((string) $item->datei.'.pdf', base64_decode($pdfData), 'application/pdf');
- }
-
- // In case of multiple documents create ZIP file
- try {
- $zip = new ZipFile();
-
- foreach ($xml->reisedokumente->reisedokument as $item) {
- [, $pdfData] = explode(',', $item->pdf);
- $zip->addFromString($item->datei.'.pdf', base64_decode($pdfData));
- }
-
- return new File('reisedokumente.zip', $zip->outputAsString(), 'application/zip');
- }
- catch (ZipException $e) {
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/MutableDataResponseParser.php b/src/BusProNet/ApiResponseParser/MutableDataResponseParser.php
deleted file mode 100644
index aee351e..0000000
--- a/src/BusProNet/ApiResponseParser/MutableDataResponseParser.php
+++ /dev/null
@@ -1,45 +0,0 @@
-änderungen->änderung as $item) {
- $attributes = $item->attributes();
-
- $category = match ((string) $attributes['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) {
- continue;
- }
-
- $mutableField = new MutableData($category, $this->stringToBool((string) $attributes['möglich']));
-
- if ($attributes['möglichbiszum']) {
- $mutableField->mutableBefore = $this->stringToDate((string) $attributes['möglichbiszum']);
- }
-
- $mutableFields[$category] = $mutableField;
- }
-
- return new BaseData($mutableFields);
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/NotificationResponseParser.php b/src/BusProNet/ApiResponseParser/NotificationResponseParser.php
deleted file mode 100644
index 29971b8..0000000
--- a/src/BusProNet/ApiResponseParser/NotificationResponseParser.php
+++ /dev/null
@@ -1,15 +0,0 @@
-satz;
-
- return new Notification((int) $recordXml->nr, (string) $recordXml->text);
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/PersonalDataResponseParser.php b/src/BusProNet/ApiResponseParser/PersonalDataResponseParser.php
deleted file mode 100644
index 2253172..0000000
--- a/src/BusProNet/ApiResponseParser/PersonalDataResponseParser.php
+++ /dev/null
@@ -1,58 +0,0 @@
-adressdaten;
-
- $dateString = (string) $addressXml->geburtsdatum;
- $dateOfBirth = empty($dateString) ? null : \DateTimeImmutable::createFromFormat('d.m.Y', $dateString);
-
- $personalData = new PersonalData();
-
- $personalData->addressId = (int) $xml->idadresse;
- $personalData->personId = (int) $xml->idperson;
- $personalData->firstName = (string) $addressXml->vorname;
- $personalData->name = (string) $addressXml->name;
- $personalData->salutation = (string) $addressXml->anrede;
- $personalData->title = (string) $addressXml->titel;
- $personalData->gender = strtoupper((string) $addressXml->geschlecht);
- $personalData->dateOfBirth = $dateOfBirth;
-
- $personalData->height = $xml->sonstiges1 ? (int) $xml->sonstiges1 : null;
- $personalData->weight = $xml->sonstiges2 ? (int) $xml->sonstiges2 : null;
- $personalData->shoeSize = $xml->sonstiges3 ? (int) $xml->sonstiges3 : null;
-
- if ($postalXml = $addressXml->anschrift) {
- $address = new Address();
- $address->street = (string) $postalXml->strasse;
- $address->postCode = (string) $postalXml->plz;
- $address->city = (string) $postalXml->ort;
- $address->country = (string) $postalXml->land;
-
- $personalData->address = $address;
- }
-
- if ($contactXml = $addressXml->kommunikation) {
- $communication = new Communication();
- $communication->phone = (string) $contactXml->telefonprivat;
- $communication->mobile = (string) $contactXml->telefonmobil;
- $communication->email = (string) $contactXml->email;
- $communication->newsletter = $this->stringToBool((string) $contactXml->newsletter);
-
- $personalData->communication = $communication;
- }
-
- return $personalData;
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/ApiResponseParser/ResponseParser.php b/src/BusProNet/ApiResponseParser/ResponseParser.php
deleted file mode 100644
index 50df4e7..0000000
--- a/src/BusProNet/ApiResponseParser/ResponseParser.php
+++ /dev/null
@@ -1,63 +0,0 @@
-xpath('satz/@typ');
- if (0 < count($responseTypeXml)) {
- $responseType = (string) $xml->xpath('satz/@typ')[0];
- }
-
- switch ($responseType) {
- case ApiClient::TYPE_NOTIFICATION:
- return (new NotificationResponseParser())->parse($xml);
- case ApiClient::TYPE_CUSTOMER_DATA:
- $subType = (string) $xml->art;
- switch ($subType) {
- case 'Adressdaten':
- case 'Adressdaten_Ändern':
- case 'Newsletter':
- return (new PersonalDataResponseParser())->parse($xml);
- case 'SelektionCRM':
- case 'SelektionCRM_Ändern':
- return (new CrmAttributesResponseParser())->parse($xml);
- case 'Vorgänge':
- return (new BookingsResponseParser())->parse($xml);
- case 'Vorgang_Details':
- return (new BookingResponseParser())->parse($xml);
- case 'Dokumentdruck':
- return (new DocumentsResponseParser())->parseDocuments($xml);
- case 'Vorgangdruck':
- return (new DocumentsResponseParser())->parseConfirmation($xml);
- }
- break;
- case ApiClient::TYPE_BASE_DATA_COUNTRIES:
- return (new CountriesResponseParser())->parse($xml);
- case ApiClient::TYPE_MUTABLE_DATA:
- return (new MutableDataResponseParser())->parse($xml);
- case ApiClient::TYPE_AVAILABILITY:
- return (new AvailabilitiesResponseParser())->parse($xml);
- case ApiClient::TYPE_BOOKING_UPDATE:
- return (new BookingUpdateResponseParser())->parse($xml);
- }
-
- throw new ResponseParserException('Unable to parse XML response');
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/DataProvider/CountryDataProvider.php b/src/BusProNet/DataProvider/CountryDataProvider.php
index ee999b5..210f02b 100644
--- a/src/BusProNet/DataProvider/CountryDataProvider.php
+++ b/src/BusProNet/DataProvider/CountryDataProvider.php
@@ -18,7 +18,7 @@ class CountryDataProvider
{
try {
return $this->cache->get('bpn_countries', function (ItemInterface $item) {
- $item->expiresAfter(3600);
+ $item->expiresAfter(6 * 60 * 60);
return $this
->apiClient
diff --git a/src/BusProNet/Model/CrmAttributes.php b/src/BusProNet/Model/CrmAttributes.php
index da4ff0f..69bfefe 100644
--- a/src/BusProNet/Model/CrmAttributes.php
+++ b/src/BusProNet/Model/CrmAttributes.php
@@ -11,4 +11,5 @@ class CrmAttributes
public bool $houseManager = false;
public bool $teamer = false;
public ?string $hotelCode = null;
+ public array $roles = [];
}
\ No newline at end of file
diff --git a/src/BusProNet/TypeConversionTrait.php b/src/BusProNet/TypeConversionTrait.php
index 098ace7..8c59fd0 100644
--- a/src/BusProNet/TypeConversionTrait.php
+++ b/src/BusProNet/TypeConversionTrait.php
@@ -7,13 +7,21 @@ use Carbon\Exceptions\InvalidFormatException;
trait TypeConversionTrait
{
- protected function stringToArray(string $string, string $separator = ','): array
+ protected function stringToArray(?string $string, string $separator = ','): array
{
+ if (null === $string) {
+ return [];
+ }
+
return explode($separator, $string);
}
- protected function stringToFloat(string $string): float
+ protected function stringToFloat(?string $string): ?float
{
+ if (null === $string) {
+ return null;
+ }
+
return (float) str_replace(['.', ','], ['', '.'], $string);
}
@@ -22,8 +30,12 @@ trait TypeConversionTrait
return number_format($float, 2, ',', '');
}
- protected function stringToBool(string $string): bool
+ protected function stringToBool(?string $string): bool
{
+ if (null === $string) {
+ return false;
+ }
+
return 'True' === $string;
}
@@ -32,8 +44,12 @@ trait TypeConversionTrait
return $bool ? 'True' : 'False';
}
- protected function stringToDate(string $string): ?\DateTimeImmutable
+ protected function stringToDate(?string $string): ?\DateTimeImmutable
{
+ if (null === $string) {
+ return null;
+ }
+
try {
$date = Carbon::createFromFormat('d.m.Y', $string)->startOfDay();
return $date->toDateTimeImmutable();
@@ -42,8 +58,12 @@ trait TypeConversionTrait
}
}
- protected function stringToDateTime(string $string): ?\DateTimeImmutable
+ protected function stringToDateTime(?string $string): ?\DateTimeImmutable
{
+ if (null === $string) {
+ return null;
+ }
+
try {
$dateTime = Carbon::createFromFormat('d.m.Y H:i', substr($string, 0, 16))->startOfDay();
return $dateTime->toDateTimeImmutable();
diff --git a/src/BusProNet/XmlLoader/AbstractXmlLoader.php b/src/BusProNet/XmlLoader/AbstractLoader.php
similarity index 76%
rename from src/BusProNet/XmlLoader/AbstractXmlLoader.php
rename to src/BusProNet/XmlLoader/AbstractLoader.php
index 5997658..4e849f4 100644
--- a/src/BusProNet/XmlLoader/AbstractXmlLoader.php
+++ b/src/BusProNet/XmlLoader/AbstractLoader.php
@@ -3,11 +3,13 @@
namespace App\BusProNet\XmlLoader;
use App\BusProNet\TypeConversionTrait;
+use App\BusProNet\XmlParserTrait;
use Symfony\Contracts\Cache\CacheInterface;
-abstract class AbstractXmlLoader
+abstract class AbstractLoader
{
use TypeConversionTrait;
+ use XmlParserTrait;
public function __construct(protected readonly CacheInterface $cache, protected readonly ?string $xmlPath = null)
{
diff --git a/src/BusProNet/XmlLoader/HotelXmlLoader.php b/src/BusProNet/XmlLoader/HotelLoader.php
similarity index 53%
rename from src/BusProNet/XmlLoader/HotelXmlLoader.php
rename to src/BusProNet/XmlLoader/HotelLoader.php
index c94a20e..0ea14bb 100644
--- a/src/BusProNet/XmlLoader/HotelXmlLoader.php
+++ b/src/BusProNet/XmlLoader/HotelLoader.php
@@ -5,24 +5,27 @@ namespace App\BusProNet\XmlLoader;
use App\BusProNet\Model\Hotel;
use App\BusProNet\Model\Travel;
use Psr\Cache\InvalidArgumentException;
+use Symfony\Component\DomCrawler\Crawler;
use Symfony\Contracts\Cache\ItemInterface;
-class HotelXmlLoader extends AbstractXmlLoader
+class HotelLoader extends AbstractLoader
{
public function loadAll(?string $filename = 'hotel.xml'): array
{
try {
return $this->cache->get('bpn_hotels', function (ItemInterface $item) use ($filename) {
- $item->expiresAfter(3600);
-
- $xml = $this->loadXml($filename);
+ $item->expiresAfter(3 * 60 * 60);
$hotels = [];
- foreach ($xml->hotel as $hotel) {
- $id = (int)$hotel->attributes()['idbuspro'];
- $hotels[$id] = $this->parseXml($hotel);
- }
+ $this
+ ->loadXml($filename)
+ ->filterXPath('//hotels/hotel')
+ ->each(function (Crawler $node) use (&$hotels) {
+ $id = (int) $node->attr('idbuspro');
+ $hotels[$id] = $this->parseXml($node);
+ })
+ ;
return $hotels;
});
@@ -51,25 +54,24 @@ class HotelXmlLoader extends AbstractXmlLoader
return $hotels[$id] ?? null;
}
- private function loadXml(?string $filename = 'hotel.xml'): \SimpleXMLElement
+ private function loadXml(?string $filename = 'hotel.xml'): Crawler
{
$filepath = $this->xmlPath.'/'.$filename;
+ $xml = file_get_contents($filepath);
- return simplexml_load_file($filepath);
+ return new Crawler($xml);
}
- public function parseXml(\SimpleXMLElement $xml): Hotel
+ public function parseXml(Crawler $node): Hotel
{
- $attributes = $xml->attributes();
-
$hotel = new Hotel();
- $hotel->id = (int) $attributes['idbuspro'];
- $hotel->code = (string) $attributes['code'];
- $hotel->name = (string) $xml->name;
- $hotel->city = $xml->ort ? (string) $xml->ort : null;
- $hotel->country = (string) $xml->land;
- $hotel->street = (string) $xml->strasse;
- $hotel->phone = $xml->telefon ? (string) $xml->telefon : null;
+ $hotel->id = (int) $node->attr('idbuspro');
+ $hotel->code = $node->attr('code');
+ $hotel->name = $this->getStringOrNullValue($node->filterXPath('//name'));
+ $hotel->city = $this->getStringOrNullValue($node->filterXPath('//ort'));
+ $hotel->country = $this->getStringOrNullValue($node->filterXPath('//land'));
+ $hotel->street = $this->getStringOrNullValue($node->filterXPath('//strasse'));
+ $hotel->phone = $this->getStringOrNullValue($node->filterXPath('//telefon'));
return $hotel;
}
diff --git a/src/BusProNet/XmlLoader/PickupXmlLoader.php b/src/BusProNet/XmlLoader/PickupLoader.php
similarity index 56%
rename from src/BusProNet/XmlLoader/PickupXmlLoader.php
rename to src/BusProNet/XmlLoader/PickupLoader.php
index 8424960..4224fbe 100644
--- a/src/BusProNet/XmlLoader/PickupXmlLoader.php
+++ b/src/BusProNet/XmlLoader/PickupLoader.php
@@ -5,24 +5,26 @@ namespace App\BusProNet\XmlLoader;
use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Travel;
use Psr\Cache\InvalidArgumentException;
+use Symfony\Component\DomCrawler\Crawler;
use Symfony\Contracts\Cache\ItemInterface;
-class PickupXmlLoader extends AbstractXmlLoader
+class PickupLoader extends AbstractLoader
{
public function loadAll(?string $filename = 'zustiege.xml'): array
{
try {
return $this->cache->get('bpn_pickups', function (ItemInterface $item) use ($filename) {
- $item->expiresAfter(3600);
+ $item->expiresAfter(3 * 60 * 60);
- $xml = simplexml_load_file($this->xmlPath . '/' . $filename);
+ $crawler = new Crawler(file_get_contents($this->xmlPath . '/' . $filename));
+ $pickupNodes = $crawler->filterXPath('//zustiege/zustieg');
$pickups = [];
- foreach ($xml->zustieg as $pickup) {
- $id = (int)$pickup->attributes()['idbuspro'];
- $pickups[$id] = $this->parseXml($pickup);
- }
+ $pickupNodes->each(function (Crawler $node) use (&$pickups) {
+ $id = $node->attr('idbuspro');
+ $pickups[$id] = $this->parseXml($node);
+ });
return $pickups;
});
@@ -38,16 +40,14 @@ class PickupXmlLoader extends AbstractXmlLoader
return $pickups[$id] ?? null;
}
- public function parseXml(\SimpleXMLElement $xml): Pickup
+ public function parseXml(Crawler $node): Pickup
{
- $attributes = $xml->attributes();
-
$pickup = new Pickup();
- $pickup->id = (int) $attributes['idbuspro'];
- $pickup->code = (string) $attributes['code'];
- $pickup->city = (string) $xml->ort;
- $pickup->postalCode = (string) $xml->plz;
- $pickup->street = (string) $xml->strasse;
+ $pickup->id = (int) $node->attr('idbuspro');
+ $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'));
return $pickup;
}
diff --git a/src/BusProNet/XmlLoader/TravelLoader.php b/src/BusProNet/XmlLoader/TravelLoader.php
new file mode 100644
index 0000000..32f2517
--- /dev/null
+++ b/src/BusProNet/XmlLoader/TravelLoader.php
@@ -0,0 +1,350 @@
+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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlLoader/TravelXmlLoader.php b/src/BusProNet/XmlLoader/TravelXmlLoader.php
deleted file mode 100644
index 7484a9a..0000000
--- a/src/BusProNet/XmlLoader/TravelXmlLoader.php
+++ /dev/null
@@ -1,338 +0,0 @@
-cache->get('bpn_travels_mapping', function (ItemInterface $item) {
- $item->expiresAfter(3600);
-
- $finder = new Finder();
- $finder->files()->in($this->xmlPath)->name('Ziel_*.xml');
-
- $mapping = [];
- $hotels = $this->hotelDataLoader->loadAll();
-
- foreach ($finder as $file) {
- $xml = simplexml_load_file($file->getRealPath());
-
- foreach ($xml->reise->termin as $travelXml) {
- $attributes = $travelXml->attributes();
-
- $travelId = (int) $attributes['idbuspro'];
-
- $mapping[$travelId] = [
- 'id' => (int) $attributes['idbuspro'],
- 'code' => (string) $attributes['code'],
- 'label' => (string) $travelXml->text,
- 'dateFrom' => $this->stringToDate((string) $attributes['termin']),
- 'dateTo' => $this->stringToDate((string) $attributes['bis']),
- 'hotels' => [],
- 'file' => $file->getRealPath(),
- ];
-
- foreach ($travelXml->hotel as $hotelXml) {
- $hotelId = (int) $hotelXml->attributes()['idbuspro'];
-
- $mapping[$travelId]['hotels'][$hotelId] = [
- 'id' => $hotelId,
- 'code' => (string) $hotelXml->attributes()['code'],
- 'label' => $hotels[$hotelId]->name,
- ];
- }
- }
- }
-
- return $mapping;
- });
- }
-
- 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 = simplexml_load_file($filename);
-
- $travelXml = $xml->xpath(sprintf('//reise/termin[@idbuspro="%d"]', $id));
-
- if (0 === count($travelXml)) {
- return null;
- }
-
- return $this->parseXml($travelXml[0], $hotelId);
- }
-
- public function parseXml(\SimpleXMLElement $xml, ?int $hotelId): Travel
- {
- $attributes = $xml->attributes();
- $hotelXml = $this->getHotelXml($xml, $hotelId);
-
- $travel = new Travel();
- $travel->id = (int) $attributes['idbuspro'];
- $travel->hotelId = $hotelId;
- $travel->label = (string) $xml->text;
- $travel->dateFrom = $this->stringToDate((string) $attributes['termin']);
- $travel->dateTo = $this->stringToDate((string) $attributes['bis']);
- $travel->code = (string) $attributes['code'];
- $travel->type = (string) $attributes['reiseart'];
- $travel->priceFrom = $this->stringToFloat((string) $xml->abpreis);
- $travel->selectionGroups = $this->getSelectionGroups($xml);
- $travel->additionalServices = $this->getAdditionalServices($xml);
- $travel->transportationServices = $this->getTransportationServices($xml);
- $travel->rooms = $this->getRooms($hotelXml);
- $travel->pickups = $this->getPickups($xml);
-
- return $travel;
- }
-
- public function getSelectionGroups(\SimpleXMLElement $xml): array
- {
- $selectionGroups = [];
-
- foreach ($xml->selektiongruppe as $item) {
- $groupId = (int) $item->attributes()['idbuspro'];
- $selectionGroup = new CrmSelectionGroup();
- $selectionGroup->id = $groupId;
- $selectionGroup->label = (string) $item->attributes()['bezeichnung'];
-
- foreach ($item->selektion as $subItem) {
- $selectionId = (int) $subItem->attributes()['idbuspro'];
- $selection = new CrmSelection();
- $selection->id = $selectionId;
- $selection->label = (string) $subItem->attributes()['bezeichnung'];
- $selectionGroups[$groupId]['selections'][$selectionId] = (string) $subItem->attributes()['bezeichnung'];
- $selectionGroup->selections[] = $selection;
- }
-
- $selectionGroups[$groupId] = $selectionGroup;
- }
-
- return $selectionGroups;
- }
-
- public function getAdditionalServices(\SimpleXMLElement $xml): array
- {
- $additionalServices = [];
-
- foreach ($xml->lei_sonstiges->leistung as $item) {
- $attributes = $item->attributes();
- $serviceId = (int) $attributes['idbuspro'];
-
- $service = new Service();
- $service->source = Service::SOURCE_TRAVEL;
- $service->category = Service::CATEGORY_ADDITIONAL;
- $service->id = $serviceId;
- $service->subType = (string) $attributes['unterart'];
- $service->mandatory = $this->stringToBool((string) $attributes['pflicht']);
- $service->dateFrom = $this->stringToDate((string) $attributes['termin']);
- $service->dateTo = $this->stringToDate((string) $attributes['bis']);
- $service->label = (string) $item->text;
- $service->price = $this->stringToFloat((string) $item->preis);
- $service->status = (string) $item->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(\SimpleXMLElement $xml): array
- {
- $transportationServices = [];
-
- foreach ($xml->lei_befoerderung->leistung as $item) {
- $attributes = $item->attributes();
- $serviceId = (int) $attributes['idbuspro'];
-
- $service = new Service();
- $service->source = Service::SOURCE_TRAVEL;
- $service->category = Service::CATEGORY_TRANSPORTATION;
- $service->id = $serviceId;
- $service->subType = (string) $attributes['unterart'];
- $service->dateFrom = $this->stringToDate((string) $attributes['termin']);
- $service->dateTo = $this->stringToDate((string) $attributes['bis']);
- $service->label = (string) $item->text;
- $service->price = $this->stringToFloat((string) $item->preis);
- $service->direction = (string) $item->richtung;
-
- $transportationServices[$serviceId] = $service;
- }
-
- return $transportationServices;
- }
-
- public function getPickups(\SimpleXMLElement $xml): array
- {
- $pickups = [];
-
- foreach ($xml->zustiege->zustieg as $item) {
- $attributes = $item->attributes();
- $pickupId = (int) $attributes['idbuspro'];
-
- $pickup = new Pickup();
- $pickup->id = $pickupId;
- $pickup->time = $this->stringToDateTime((string) $attributes['zeit']);
- $pickup->price = $attributes['preis'] ? $this->stringToFloat((string) $attributes['preis']) : null;
-
- $pickups[$pickupId] = $pickup;
- }
-
- return $pickups;
- }
-
- public function getHotelXml(\SimpleXMLElement $xml, ?int $hotelId): ?\SimpleXMLElement
- {
- // In case not hotel id is provided, take the first hotel node (which is most probably the only one)
- if (null === $hotelId) {
- return $xml->hotel;
- }
-
- $hotelXml = $xml->xpath(sprintf('//hotel[@idbuspro="%d"]', $hotelId));
-
- return $hotelXml[0] ?? null;
- }
-
- public function getRooms(\SimpleXMLElement $xml): array
- {
- if (false === isset($xml->zimmer)) {
- return [];
- }
-
- $rooms = [];
-
- foreach ($xml->zimmer->preis as $item) {
- $attributes = $item->attributes();
- $roomId = (int) $attributes['idbuspro_zimmer'];
-
- $room = new Room();
- $room->id = $roomId;
- $room->code = (string) $attributes['zimmercode'];
- $room->category = (string) $attributes['kat'];
- $room->boardId = (int) $attributes['idbuspro_vp'];
- $room->label = (string) $attributes['zimmertext'];
- $room->minPax = (int) $attributes['MinPax'];
- $room->maxPax = (int) $attributes['MaxPax'];
- $room->nights = (int) $attributes['naechte'];
- $room->price = $attributes['preis'] ? $this->stringToFloat((string) $attributes['preis']) : null;
- $room->status = (string) $item->status;
- $room->available = (int) $attributes['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);
- }
- }
-}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/AbstractParser.php b/src/BusProNet/XmlParser/AbstractParser.php
new file mode 100644
index 0000000..9280151
--- /dev/null
+++ b/src/BusProNet/XmlParser/AbstractParser.php
@@ -0,0 +1,21 @@
+count() ? $node->text() : null;
+ }
+
+ protected function getIntOrNullValue(Crawler $node): ?int
+ {
+ return 0 < $node->count() ? (int) $node->text() : null;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/ApiResponseParser.php b/src/BusProNet/XmlParser/ApiResponseParser.php
new file mode 100644
index 0000000..4c1cd1e
--- /dev/null
+++ b/src/BusProNet/XmlParser/ApiResponseParser.php
@@ -0,0 +1,64 @@
+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');
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/AvailabilitiesParser.php b/src/BusProNet/XmlParser/AvailabilitiesParser.php
new file mode 100644
index 0000000..f25d898
--- /dev/null
+++ b/src/BusProNet/XmlParser/AvailabilitiesParser.php
@@ -0,0 +1,30 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/BookingParser.php b/src/BusProNet/XmlParser/BookingParser.php
new file mode 100644
index 0000000..234f8bd
--- /dev/null
+++ b/src/BusProNet/XmlParser/BookingParser.php
@@ -0,0 +1,155 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/BookingUpdateParser.php b/src/BusProNet/XmlParser/BookingUpdateParser.php
new file mode 100644
index 0000000..20b4350
--- /dev/null
+++ b/src/BusProNet/XmlParser/BookingUpdateParser.php
@@ -0,0 +1,18 @@
+valid = 'möglich' === $node->filterXPath('//aenderung')->text();
+ $bookingUpdate->totalPrice = $this->stringToFloat($node->filterXPath('//gesamtpreis')->text());
+
+ return $bookingUpdate;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/BookingsParser.php b/src/BusProNet/XmlParser/BookingsParser.php
new file mode 100644
index 0000000..7775fa8
--- /dev/null
+++ b/src/BusProNet/XmlParser/BookingsParser.php
@@ -0,0 +1,44 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/CountriesParser.php b/src/BusProNet/XmlParser/CountriesParser.php
new file mode 100644
index 0000000..123cc91
--- /dev/null
+++ b/src/BusProNet/XmlParser/CountriesParser.php
@@ -0,0 +1,30 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/CrmAttributesResponseParser.php b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php
new file mode 100644
index 0000000..2d9f887
--- /dev/null
+++ b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php
@@ -0,0 +1,95 @@
+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;
+ }
+}
diff --git a/src/BusProNet/XmlParser/DocumentsParser.php b/src/BusProNet/XmlParser/DocumentsParser.php
new file mode 100644
index 0000000..455faac
--- /dev/null
+++ b/src/BusProNet/XmlParser/DocumentsParser.php
@@ -0,0 +1,60 @@
+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];
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/MutableDataParser.php b/src/BusProNet/XmlParser/MutableDataParser.php
new file mode 100644
index 0000000..6d39fe8
--- /dev/null
+++ b/src/BusProNet/XmlParser/MutableDataParser.php
@@ -0,0 +1,42 @@
+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);
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/NotificationParser.php b/src/BusProNet/XmlParser/NotificationParser.php
new file mode 100644
index 0000000..c25ff4f
--- /dev/null
+++ b/src/BusProNet/XmlParser/NotificationParser.php
@@ -0,0 +1,17 @@
+getIntOrNullValue($result->filterXPath('//satz/nr'));
+ $text = $this->getStringOrNullValue($result->filterXPath('//satz/text'));
+
+ return new Notification($number, $text);
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/PersonalDataParser.php b/src/BusProNet/XmlParser/PersonalDataParser.php
new file mode 100644
index 0000000..98ed9f0
--- /dev/null
+++ b/src/BusProNet/XmlParser/PersonalDataParser.php
@@ -0,0 +1,70 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/PickupsParser.php b/src/BusProNet/XmlParser/PickupsParser.php
new file mode 100644
index 0000000..a985b80
--- /dev/null
+++ b/src/BusProNet/XmlParser/PickupsParser.php
@@ -0,0 +1,36 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/RoomsParser.php b/src/BusProNet/XmlParser/RoomsParser.php
new file mode 100644
index 0000000..bc89ce8
--- /dev/null
+++ b/src/BusProNet/XmlParser/RoomsParser.php
@@ -0,0 +1,43 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/ServicesParser.php b/src/BusProNet/XmlParser/ServicesParser.php
new file mode 100644
index 0000000..b077312
--- /dev/null
+++ b/src/BusProNet/XmlParser/ServicesParser.php
@@ -0,0 +1,44 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParser/SurchargesParser.php b/src/BusProNet/XmlParser/SurchargesParser.php
new file mode 100644
index 0000000..b903232
--- /dev/null
+++ b/src/BusProNet/XmlParser/SurchargesParser.php
@@ -0,0 +1,33 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/src/BusProNet/XmlParserTrait.php b/src/BusProNet/XmlParserTrait.php
new file mode 100644
index 0000000..a357826
--- /dev/null
+++ b/src/BusProNet/XmlParserTrait.php
@@ -0,0 +1,18 @@
+count() ? $node->text() : null;
+ }
+
+ protected function getIntOrNullValue(Crawler $node): ?int
+ {
+ return 0 < $node->count() ? (int) $node->text() : null;
+ }
+}
\ No newline at end of file
diff --git a/src/Controller/Api/HotelController.php b/src/Controller/Api/HotelController.php
index 28c6b8b..2fa3d83 100644
--- a/src/Controller/Api/HotelController.php
+++ b/src/Controller/Api/HotelController.php
@@ -2,7 +2,7 @@
namespace App\Controller\Api;
-use App\BusProNet\XmlLoader\HotelXmlLoader;
+use App\BusProNet\XmlLoader\HotelLoader;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@@ -11,7 +11,7 @@ use Symfony\Component\Routing\Attribute\Route;
#[Route('/api')]
class HotelController extends AbstractController
{
- public function __construct(private readonly HotelXmlLoader $xmlLoader)
+ public function __construct(private readonly HotelLoader $xmlLoader)
{
}
diff --git a/src/Controller/Api/PickupController.php b/src/Controller/Api/PickupController.php
index b22e64c..3d5374d 100644
--- a/src/Controller/Api/PickupController.php
+++ b/src/Controller/Api/PickupController.php
@@ -2,7 +2,7 @@
namespace App\Controller\Api;
-use App\BusProNet\XmlLoader\PickupXmlLoader;
+use App\BusProNet\XmlLoader\PickupLoader;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
@@ -11,7 +11,7 @@ use Symfony\Component\Routing\Attribute\Route;
#[Route('/api')]
class PickupController extends AbstractController
{
- public function __construct(private readonly PickupXmlLoader $xmlLoader)
+ public function __construct(private readonly PickupLoader $xmlLoader)
{
}
diff --git a/src/Controller/Api/TravelController.php b/src/Controller/Api/TravelController.php
index 7373f66..1cd5822 100644
--- a/src/Controller/Api/TravelController.php
+++ b/src/Controller/Api/TravelController.php
@@ -3,9 +3,9 @@
namespace App\Controller\Api;
use App\BusProNet\Model\Travel;
-use App\BusProNet\XmlLoader\HotelXmlLoader;
-use App\BusProNet\XmlLoader\PickupXmlLoader;
-use App\BusProNet\XmlLoader\TravelXmlLoader;
+use App\BusProNet\XmlLoader\HotelLoader;
+use App\BusProNet\XmlLoader\PickupLoader;
+use App\BusProNet\XmlLoader\TravelLoader;
use Psr\Cache\InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
@@ -18,9 +18,9 @@ use Symfony\Contracts\Cache\ItemInterface;
class TravelController extends AbstractController
{
public function __construct(
- private readonly TravelXmlLoader $travelXmlLoader,
- private readonly HotelXmlLoader $hotelXmlLoader,
- private readonly PickupXmlLoader $pickupXmlLoader,
+ private readonly TravelLoader $travelXmlLoader,
+ private readonly HotelLoader $hotelXmlLoader,
+ private readonly PickupLoader $pickupXmlLoader,
private readonly CacheInterface $cache,
) {
}
diff --git a/src/Controller/Booking/DownloadController.php b/src/Controller/Booking/DownloadController.php
index 3097c0b..18fc35b 100644
--- a/src/Controller/Booking/DownloadController.php
+++ b/src/Controller/Booking/DownloadController.php
@@ -9,6 +9,7 @@ use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
+use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use function Symfony\Component\String\u;
@@ -60,7 +61,9 @@ class DownloadController extends AbstractController
$filename = u($file->filename)->ascii();
- $response = new Response($file->content);
+ $response = new StreamedResponse(function () use ($file) {
+ echo $file->content;
+ });
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
diff --git a/src/Controller/Booking/EditController.php b/src/Controller/Booking/EditController.php
index 97ed810..61b3a4f 100644
--- a/src/Controller/Booking/EditController.php
+++ b/src/Controller/Booking/EditController.php
@@ -3,8 +3,8 @@
namespace App\Controller\Booking;
use App\BusProNet\ApiClient;
-use App\BusProNet\XmlLoader\PickupXmlLoader;
-use App\BusProNet\XmlLoader\TravelXmlLoader;
+use App\BusProNet\XmlLoader\PickupLoader;
+use App\BusProNet\XmlLoader\TravelLoader;
use App\Form\BookingType;
use App\Form\Model\BookingData;
use Psr\Cache\InvalidArgumentException;
@@ -20,11 +20,11 @@ use Symfony\Contracts\Cache\ItemInterface;
class EditController extends AbstractController
{
public function __construct(
- private readonly ApiClient $apiClient,
- private readonly TravelXmlLoader $travelDataLoader,
- private readonly PickupXmlLoader $pickupDataLoader,
+ private readonly ApiClient $apiClient,
+ private readonly TravelLoader $travelDataLoader,
+ private readonly PickupLoader $pickupDataLoader,
private readonly CacheInterface $cache,
- private readonly Security $security,
+ private readonly Security $security,
) {
}
@@ -89,7 +89,7 @@ class EditController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
- $this->apiClient->updateBooking($formData, false);
+ $this->apiClient->updateBooking($formData);
try {
$this->cache->delete($cacheKeySingle);
diff --git a/src/Controller/Booking/IndexController.php b/src/Controller/Booking/IndexController.php
index 35fe46d..d9ced13 100644
--- a/src/Controller/Booking/IndexController.php
+++ b/src/Controller/Booking/IndexController.php
@@ -3,7 +3,7 @@
namespace App\Controller\Booking;
use App\BusProNet\ApiClient;
-use App\BusProNet\XmlLoader\TravelXmlLoader;
+use App\BusProNet\XmlLoader\TravelLoader;
use Psr\Cache\InvalidArgumentException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Bundle\SecurityBundle\Security;
@@ -17,10 +17,10 @@ use Symfony\Contracts\Cache\ItemInterface;
class IndexController extends AbstractController
{
public function __construct(
- private readonly ApiClient $apiClient,
- private readonly TravelXmlLoader $travelDataLoader,
+ private readonly ApiClient $apiClient,
+ private readonly TravelLoader $travelDataLoader,
private readonly CacheInterface $cache,
- private readonly Security $security,
+ private readonly Security $security,
) {
}
diff --git a/templates/booking/index.html.twig b/templates/booking/index.html.twig
index 56057e4..3ac67ca 100644
--- a/templates/booking/index.html.twig
+++ b/templates/booking/index.html.twig
@@ -69,13 +69,11 @@
{% endif %}
+ class="button bg-button button--small">
Bestätigung
+ class="button bg-button button--small">
Reisedokumente
diff --git a/tests/BusProNet/DataLoader/HotelDataLoaderTest.php b/tests/BusProNet/DataLoader/HotelDataLoaderTest.php
index 3e52189..19bc078 100644
--- a/tests/BusProNet/DataLoader/HotelDataLoaderTest.php
+++ b/tests/BusProNet/DataLoader/HotelDataLoaderTest.php
@@ -2,7 +2,7 @@
namespace App\Tests\BusProNet\DataLoader;
-use App\BusProNet\XmlLoader\HotelXmlLoader;
+use App\BusProNet\XmlLoader\HotelLoader;
use App\BusProNet\Model\Hotel;
use PHPUnit\Framework\TestCase;
@@ -10,7 +10,7 @@ class HotelDataLoaderTest extends TestCase
{
public function testParse(): void
{
- $loader = new HotelXmlLoader(__DIR__.'/../../Resources');
+ $loader = new HotelLoader(__DIR__.'/../../Resources');
$hotelId = 163113;
$xml = $loader->loadById($hotelId, 'hotels_data.xml');
diff --git a/tests/BusProNet/DataLoader/PickupDataLoaderTest.php b/tests/BusProNet/DataLoader/PickupDataLoaderTest.php
index 608be5e..bdd933e 100644
--- a/tests/BusProNet/DataLoader/PickupDataLoaderTest.php
+++ b/tests/BusProNet/DataLoader/PickupDataLoaderTest.php
@@ -2,7 +2,7 @@
namespace App\Tests\BusProNet\DataLoader;
-use App\BusProNet\XmlLoader\PickupXmlLoader;
+use App\BusProNet\XmlLoader\PickupLoader;
use App\BusProNet\Model\Pickup;
use PHPUnit\Framework\TestCase;
@@ -10,7 +10,7 @@ class PickupDataLoaderTest extends TestCase
{
public function testParse(): void
{
- $loader = new PickupXmlLoader();
+ $loader = new PickupLoader();
$pickupId = 1;
$xml = $loader->loadById($pickupId, 'pickups_data.xml');
diff --git a/tests/BusProNet/DataLoader/TravelDataLoaderTest.php b/tests/BusProNet/DataLoader/TravelDataLoaderTest.php
index 97931dd..8ffc2f6 100644
--- a/tests/BusProNet/DataLoader/TravelDataLoaderTest.php
+++ b/tests/BusProNet/DataLoader/TravelDataLoaderTest.php
@@ -2,7 +2,7 @@
namespace App\Tests\BusProNet\DataLoader;
-use App\BusProNet\XmlLoader\TravelXmlLoader;
+use App\BusProNet\XmlLoader\TravelLoader;
use App\BusProNet\Model\Travel;
use PHPUnit\Framework\TestCase;
@@ -10,7 +10,7 @@ class TravelDataLoaderTest extends TestCase
{
public function testParse(): void
{
- $loader = new TravelXmlLoader('');
+ $loader = new TravelLoader('');
$filename = __DIR__.'/../../Resources/travel_data.xml';
$travelId = 11478;