0, 'double' => 0, 'multi' => 0, ]; $statement = $pdo->query( "SELECT setting_key, price FROM pricing_settings WHERE setting_key IN ('room_count_single', 'room_count_double', 'room_count_multi')" ); foreach ($statement->fetchAll(PDO::FETCH_KEY_PAIR) as $key => $value) { $type = str_replace('room_count_', '', (string) $key); if (array_key_exists($type, $defaults)) { $defaults[$type] = max(0, (int) $value); } } return $defaults; } function normalizedRoommateName(string $name): string { return mb_strtolower(preg_replace('/\\s+/u', ' ', trim($name)) ?? '', 'UTF-8'); } function buildRoomPlan(array $participants, array $roomCounts): array { $rooms = []; $warnings = []; $unassigned = []; $available = [ 'single' => max(0, (int) ($roomCounts['single'] ?? 0)), 'double' => max(0, (int) ($roomCounts['double'] ?? 0)), 'multi' => max(0, (int) ($roomCounts['multi'] ?? 0)), ]; $roomNumber = ['single' => 0, 'double' => 0, 'multi' => 0]; $assigned = []; $blocked = []; $byName = []; foreach ($participants as &$participant) { $participant['roommate_key'] = normalizedRoommateName((string) ($participant['roommate_requests'] ?? '')); $participant['name_key'] = normalizedRoommateName((string) $participant['name']); $byName[$participant['name_key']][] = $participant; } unset($participant); $addRoom = static function (string $type, array $members) use (&$rooms, &$roomNumber, &$available, &$assigned): bool { if (($available[$type] ?? 0) < 1) { return false; } $roomNumber[$type]++; $capacity = $type === 'single' ? 1 : ($type === 'double' ? 2 : 4); $rooms[] = [ 'type' => $type, 'number' => $roomNumber[$type], 'capacity' => $capacity, 'members' => $members, ]; $available[$type]--; foreach ($members as $member) { $assigned[(string) $member['id']] = true; } return true; }; // Mutual wishes are handled first, but only when both participants requested the same room type. foreach ($participants as $participant) { $id = (string) $participant['id']; if (isset($assigned[$id]) || $participant['roommate_key'] === '') { continue; } foreach ($byName[$participant['roommate_key']] ?? [] as $requested) { $requestedId = (string) $requested['id']; if ($requestedId === $id || isset($assigned[$requestedId])) { continue; } $mutual = $requested['roommate_key'] === $participant['name_key']; $sameType = $requested['room_type'] === $participant['room_type']; if (!$mutual || !$sameType) { continue; } if (!$addRoom((string) $participant['room_type'], [$participant, $requested])) { $warnings[] = sprintf( 'Der gegenseitige Zimmerwunsch von %s und %s konnte nicht erfüllt werden: kein %s-Zimmer verfügbar.', $participant['name'], $requested['name'], $participant['room_type'] === 'double' ? 'Doppel' : 'Mehrbett' ); $unassigned[$id] = $participant; $unassigned[$requestedId] = $requested; $blocked[$id] = true; $blocked[$requestedId] = true; } break; } } foreach (['single', 'double', 'multi'] as $type) { $waiting = []; foreach ($participants as $participant) { $id = (string) $participant['id']; if (!isset($assigned[$id]) && !isset($blocked[$id]) && $participant['room_type'] === $type) { $waiting[] = $participant; } } $capacity = $type === 'single' ? 1 : ($type === 'double' ? 2 : 4); while ($waiting !== []) { $group = array_splice($waiting, 0, $capacity); if (!$addRoom($type, $group)) { foreach ($group as $participant) { $unassigned[(string) $participant['id']] = $participant; } $warnings[] = sprintf( '%d Teilnehmer mit Wunsch %s konnten keinem verfügbaren Zimmer zugeordnet werden.', count($group), $type === 'single' ? 'Einzelzimmer' : ($type === 'double' ? 'Doppelzimmer' : 'Mehrbettzimmer') ); } } } foreach ($unassigned as $participant) { $warnings[] = sprintf('Nicht eingeplant: %s (%s).', $participant['name'], $participant['email']); } return [ 'rooms' => $rooms, 'warnings' => array_values(array_unique($warnings)), 'unassigned' => array_values($unassigned), 'available' => $available, ]; }