validate([ 'query' => 'required|string|max:120', ]); $key = config('services.google.maps_key'); if (empty($key)) { return response()->json(['error' => 'no_key'], 503); } $query = $request->string('query')->toString(); try { $response = Http::timeout(8)->get('https://maps.googleapis.com/maps/api/geocode/json', [ 'address' => $query, 'language' => 'de', 'key' => $key, ]); if (! $response->successful()) { return response()->json(['error' => 'api_error', 'status' => $response->status()], 502); } $data = $response->json(); if (($data['status'] ?? null) !== 'OK' || empty($data['results'])) { return response()->json(['results' => []], 200); } // Nur Städte/Ortschaften behalten — keine POI/Adressen $cityTypes = ['locality', 'administrative_area_level_1', 'administrative_area_level_2', 'political']; $candidates = []; foreach ($data['results'] as $r) { $types = $r['types'] ?? []; if (! array_intersect($cityTypes, $types)) { continue; } $components = $r['address_components'] ?? []; $country = ''; $region = ''; foreach ($components as $c) { if (in_array('country', $c['types'] ?? [], true)) { $country = $c['long_name'] ?? ''; } if (in_array('administrative_area_level_1', $c['types'] ?? [], true)) { $region = $c['long_name'] ?? ''; } } $candidates[] = [ 'name' => $r['address_components'][0]['long_name'] ?? $query, 'formatted' => $r['formatted_address'] ?? '', 'country' => $country, 'region' => $region, 'lat' => $r['geometry']['location']['lat'] ?? null, 'lng' => $r['geometry']['location']['lng'] ?? null, ]; if (count($candidates) >= 5) { break; } } return response()->json(['results' => $candidates]); } catch (\Throwable $e) { Log::warning('Cities lookup exception', ['e' => $e->getMessage()]); return response()->json(['error' => 'exception'], 500); } } }