110 lines
4.5 KiB
PHP
110 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
/**
|
|
* "Wie schaut der erste Bezirk aus?" / "Erzähl mir was über das Belvedere" →
|
|
* Google Places (New) Text Search + Photo-Fetch. Liefert dem HUD ein Detail-Panel
|
|
* mit Bild + Adresse + Bewertung + Öffnungszeiten + Website.
|
|
*/
|
|
class FoxPlaceInfoController
|
|
{
|
|
public function __invoke(Request $request): JsonResponse
|
|
{
|
|
$request->validate([
|
|
'query' => 'required|string|max:200',
|
|
'bias_lat' => 'nullable|numeric',
|
|
'bias_lng' => 'nullable|numeric',
|
|
]);
|
|
|
|
$key = config('services.google.maps_key');
|
|
if (empty($key)) {
|
|
return response()->json(['error' => 'no_key'], 503);
|
|
}
|
|
|
|
$query = $request->string('query')->toString();
|
|
$lat = $request->filled('bias_lat') ? (float) $request->input('bias_lat') : null;
|
|
$lng = $request->filled('bias_lng') ? (float) $request->input('bias_lng') : null;
|
|
|
|
try {
|
|
$body = [
|
|
'textQuery' => $query,
|
|
'languageCode' => 'de',
|
|
'regionCode' => 'AT',
|
|
'maxResultCount' => 1,
|
|
];
|
|
if ($lat !== null && $lng !== null) {
|
|
$body['locationBias'] = [
|
|
'circle' => [
|
|
'center' => ['latitude' => $lat, 'longitude' => $lng],
|
|
'radius' => 30_000.0,
|
|
],
|
|
];
|
|
}
|
|
|
|
$response = Http::timeout(10)
|
|
->withHeaders([
|
|
'Content-Type' => 'application/json',
|
|
'X-Goog-Api-Key' => $key,
|
|
'X-Goog-FieldMask' => 'places.id,places.displayName,places.formattedAddress,'
|
|
.'places.location,places.rating,places.userRatingCount,places.websiteUri,'
|
|
.'places.regularOpeningHours,places.types,places.editorialSummary,'
|
|
.'places.photos,places.priceLevel,'
|
|
.'places.nationalPhoneNumber,places.internationalPhoneNumber',
|
|
])
|
|
->post('https://places.googleapis.com/v1/places:searchText', $body);
|
|
|
|
if (! $response->successful()) {
|
|
Log::warning('Place-Info HTTP-Fehler', ['status' => $response->status()]);
|
|
|
|
return response()->json(['error' => 'api_error'], 502);
|
|
}
|
|
|
|
$place = $response->json('places.0');
|
|
if (! $place) {
|
|
return response()->json(['error' => 'not_found'], 404);
|
|
}
|
|
|
|
$photoUrl = null;
|
|
if (! empty($place['photos'][0]['name'])) {
|
|
// Photo URL: /v1/{photo_resource}/media?maxWidthPx=...&key=...
|
|
$photoUrl = 'https://places.googleapis.com/v1/'.$place['photos'][0]['name'].'/media?maxWidthPx=800&key='.urlencode($key);
|
|
}
|
|
|
|
$photoUrls = [];
|
|
foreach (array_slice($place['photos'] ?? [], 0, 5) as $photo) {
|
|
if (!empty($photo['name'])) {
|
|
$photoUrls[] = 'https://places.googleapis.com/v1/'.$photo['name'].'/media?maxWidthPx=800&key='.urlencode($key);
|
|
}
|
|
}
|
|
|
|
return response()->json([
|
|
'name' => $place['displayName']['text'] ?? null,
|
|
'address' => $place['formattedAddress'] ?? '',
|
|
'lat' => $place['location']['latitude'] ?? null,
|
|
'lng' => $place['location']['longitude'] ?? null,
|
|
'rating' => $place['rating'] ?? null,
|
|
'rating_count' => $place['userRatingCount'] ?? null,
|
|
'website' => $place['websiteUri'] ?? null,
|
|
'summary' => $place['editorialSummary']['text'] ?? null,
|
|
'opening_hours' => $place['regularOpeningHours']['weekdayDescriptions'] ?? null,
|
|
'types' => $place['types'] ?? [],
|
|
'photo_url' => $photoUrl,
|
|
'price_level' => $place['priceLevel'] ?? null,
|
|
'place_id' => $place['id'] ?? null,
|
|
'phone' => $place['nationalPhoneNumber'] ?? $place['internationalPhoneNumber'] ?? null,
|
|
'photo_urls' => $photoUrls,
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
Log::warning('Place-Info exception', ['e' => $e->getMessage()]);
|
|
|
|
return response()->json(['error' => 'exception'], 500);
|
|
}
|
|
}
|
|
}
|