*/ private array $ignoredRoomCodes = [ 'PDGS', ]; /** * @return array{roomTypes: array>, rows: array>} */ public function parse(string $xml): array { $crawler = new Crawler($xml); $roomTypes = $this->parseRoomTypes($crawler); $rows = $this->parseRows($crawler, $roomTypes); return [ 'roomTypes' => $roomTypes, 'rows' => $rows, ]; } /** * @return array> */ private function parseRoomTypes(Crawler $crawler): array { $roomTypes = []; $crawler ->filterXPath('//unterbringungen/unterbringung') ->each(function (Crawler $node) use (&$roomTypes) { $roomId = $this->getIntOrNullAttribute($node->attr('idbuspro')); $code = $node->attr('code'); if (null === $roomId || null === $code) { return; } if (true === in_array($code, $this->ignoredRoomCodes, true)) { return; } $label = $node->attr('zimmerbezeichnung') ?? $this->getStringOrNullValue($node->filterXPath('.//text')) ?? $code; $pax = $this->getIntOrNullAttribute($node->attr('pax_max')) ?? 0; $link = $this->getIntOrNullAttribute($node->attr('idbuspro_kontingent_aus')); $roomTypes[$roomId] = [ 'idBusPro' => $roomId, 'code' => $code, 'pax' => $pax, 'label' => $label, 'isControlRoom' => self::CONTROL_ROOM_CODE === $code, 'link' => $link, ]; }); return $roomTypes; } /** * @param array> $roomTypes * * @return array> */ private function parseRows(Crawler $crawler, array $roomTypes): array { $rows = []; $crawler ->filterXPath('//kapazitaeten/kapazitaet') ->each(function (Crawler $dateNode) use (&$rows, $roomTypes) { $date = $this->stringToDate($dateNode->attr('termin')); if (null === $date) { return; } foreach ($roomTypes as $roomType) { $roomId = $roomType['link'] ?? $roomType['idBusPro']; $roomNode = $this->findRoomNode($dateNode, $roomId); if (null === $roomNode) { continue; } $capacity = $this->getIntOrNullAttribute($roomNode->attr('kontingent')) ?? 0; $free = $this->getIntOrNullAttribute($roomNode->attr('frei')) ?? 0; $statusRaw = $roomNode->attr('status') ?? ''; $rows[] = [ 'date' => $date, 'roomCode' => $roomType['code'], 'roomLabel' => $roomType['label'], 'pax' => $roomType['isControlRoom'] ? 0 : $capacity * (int) ($roomType['pax'] ?? 0), 'available' => $roomType['isControlRoom'] ? 0 : $free * (int) ($roomType['pax'] ?? 0), 'status' => $this->mapStatus($statusRaw), 'minPrice' => $this->stringToFloat($roomNode->attr('abpreis')), 'minNights' => $this->getIntOrNullAttribute($roomNode->attr('abpreis_naechte')), 'additionalNightMinPrice' => $this->stringToFloat($roomNode->attr('abpreis_verlaengerung')), 'additionalNightMinNights' => $this->getIntOrNullAttribute($roomNode->attr('abpreis_verlaengerung_naechte')), 'isControlRoom' => $roomType['isControlRoom'], ]; } }); return $rows; } private function findRoomNode(Crawler $dateNode, int $roomId): ?Crawler { $roomNode = $dateNode->filterXPath(sprintf('.//zimmerliste/zimmer[@idbuspro="%d"]', $roomId)); if (0 < $roomNode->count()) { return $roomNode->first(); } $roomNode = $dateNode->filterXPath(sprintf('.//zimmerliste/zimmer[@id="%d"]', $roomId)); if (0 < $roomNode->count()) { return $roomNode->first(); } $roomNode = $dateNode->filterXPath(sprintf('.//zimmerliste/zimmer[@id_zimmer="%d"]', $roomId)); if (0 < $roomNode->count()) { return $roomNode->first(); } return null; } private function mapStatus(?string $status): string { return match (strtoupper((string) $status)) { 'A' => 'ON_REQUEST', 'S' => 'BLOCKED', default => 'OK', }; } }