1787 lines
88 KiB
PHP
1787 lines
88 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers;
|
||
|
||
use App\Core\Service\PropertyService;
|
||
use App\Core\Service\PropertyContactService;
|
||
use App\Core\Service\PropertyFactService;
|
||
use App\Core\Service\PropertyFactMappingService;
|
||
use App\Core\Service\PropertyRoomService;
|
||
use App\Core\Service\UserService;
|
||
use App\Core\Service\UserPropertyMappingService;
|
||
use App\Core\Service\JwtService;
|
||
use App\Core\Service\ApiAccessTokenService;
|
||
use App\Core\Service\ProductService;
|
||
use App\Exceptions\ApiErrorException;
|
||
use App\Models\PropertyFact;
|
||
use App\Models\PropertyPhoto;
|
||
use Exception;
|
||
use GuzzleHttp\Client;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Support\Facades\Config;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\File;
|
||
use Illuminate\Support\Facades\Log;
|
||
use Intervention\Image\Facades\Image;
|
||
|
||
/**
|
||
* propertyInsertController
|
||
*
|
||
* Booking.com'dan gelen otel verisiyle property oluşturma akışını
|
||
* tek endpoint üzerinden uçtan uca çalıştırır.
|
||
*
|
||
* AKIŞ:
|
||
* ADIM 1 — Kullanıcı kaydı + property oluşturma (user/register-user-with-property)
|
||
* ADIM 2 — Property bilgilerini güncelle (property/info/update)
|
||
* ADIM 3 — İletişim bilgilerini kaydet (property/contact/update)
|
||
* ADIM 4 — Facility eşleştir → fact-mapping ekle (property/fact-mapping/add)
|
||
* ADIM 5 — Oda ekle (property/room/add-room-bed)
|
||
*
|
||
* KULLANIM:
|
||
* POST /bulut/property-insert
|
||
* {
|
||
* "hotel_id" : "89675", // Booking.com hotel_id (zorunlu)
|
||
* "email" : "owner@hotel.com", // kullanıcı e-posta (zorunlu)
|
||
* "password" : "Abc123!", // kullanıcı şifre (zorunlu)
|
||
* "name" : "Ahmet", // kullanıcı adı (zorunlu)
|
||
* "surname" : "Yılmaz", // kullanıcı soyadı (zorunlu)
|
||
* "property_name" : "Grand Yavuz", // property adı (zorunlu)
|
||
* "phone" : "+90...", // iletişim telefonu (opsiyonel)
|
||
* "dry_run" : false // true → DB'ye yazma, sadece rapor üret
|
||
* }
|
||
*/
|
||
class propertyInsertController extends Controller
|
||
{
|
||
const API_HOST = 'booking-com15.p.rapidapi.com';
|
||
const API_BASE = 'https://booking-com15.p.rapidapi.com';
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Alias map: Booking.com facility adı → property_fact.name (küçük harf)
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private $aliasMap = array(
|
||
'internet services' => 'wi-fi',
|
||
'free wifi' => 'wi-fi',
|
||
'wifi' => 'wi-fi',
|
||
'wi-fi' => 'wi-fi',
|
||
'wifi in all areas' => 'wi-fi',
|
||
'free wi-fi' => 'wi-fi',
|
||
'internet' => 'wi-fi',
|
||
'internet access' => 'wi-fi',
|
||
'airport shuttle' => 'airport transfer',
|
||
'airport pick up' => 'airport to hotel transfer',
|
||
'airport drop off' => 'hotel to airport transfer',
|
||
'car hire' => 'rent a car',
|
||
'car rental' => 'rent a car',
|
||
'shuttle service' => 'airport transfer',
|
||
'24-hour front desk' => 'reception',
|
||
'concierge service' => 'concierge',
|
||
'luggage storage' => 'luggage storage',
|
||
'laundry' => 'cleaning service',
|
||
'daily housekeeping' => 'housekeeping',
|
||
'tour desk' => 'tour desk',
|
||
'private check-in/check-out' => 'check-in service',
|
||
'express check-in/check-out' => 'check-in service',
|
||
'wake-up service' => 'wake up service',
|
||
'dry cleaning' => 'dry cleaning',
|
||
'currency exchange' => 'currency exchange',
|
||
'facilities for disabled guests' => 'disabled',
|
||
'wheelchair accessible' => 'wheelchair',
|
||
'upper floors accessible by elevator' => 'elevator',
|
||
'lower bathroom sink' => 'lower bathroom sink',
|
||
'toilet with grab rails' => 'toilet with grab rails',
|
||
'breakfast' => 'bed and breakfast',
|
||
'snack bar' => 'snack bar',
|
||
'meeting/banquet facilities' => 'meeting room',
|
||
'business centre' => 'working area',
|
||
'business center' => 'working area',
|
||
'fax/photocopying' => 'photocopy',
|
||
'air conditioning' => 'air conditioner',
|
||
'flat-screen tv' => 'tv',
|
||
'flat screen tv' => 'tv',
|
||
'bath or shower' => 'shower',
|
||
'free toiletries' => 'shower',
|
||
'single-room air conditioning for guest accommodation' => 'air conditioner',
|
||
'single-room ac for guest accommodation' => 'air conditioner',
|
||
'minibar' => 'mini bar',
|
||
'hairdryer' => 'hair dryer',
|
||
'hair dryer' => 'hair dryer',
|
||
'wardrobe or closet' => 'clothes cabinet',
|
||
'socket near the bed' => 'planning electrical sockets',
|
||
'family rooms' => 'family room',
|
||
'clothes rack' => 'clotheshorse',
|
||
'cctv in common areas' => 'security camera',
|
||
'cctv outside property' => 'security camera',
|
||
'smoke alarms' => 'smoke alarm',
|
||
'safety deposit box' => 'safety deposit box',
|
||
'24-hour security' => 'security service',
|
||
'key card access' => 'security service',
|
||
'hand sanitizer in guest accommodation and key areas' => 'hand sanitiser',
|
||
'hand sanitiser' => 'hand sanitiser',
|
||
'hand sanitizer' => 'hand sanitiser',
|
||
'face masks for guests available' => 'protective masks for guests',
|
||
'physical distancing rules followed' => 'social distancing regulations',
|
||
'screens or physical barriers placed between staff and guests in appropriate areas' => 'protective hygiene screens',
|
||
'guest accommodation is disinfected between stays' => 'disinfection in all rooms',
|
||
'cashless payment available' => 'contactless payment',
|
||
'food can be delivered to guest accommodation' => 'food delivery',
|
||
'tennis court' => 'tennis',
|
||
'golf course' => 'golf',
|
||
'table tennis' => 'ping pong',
|
||
'jogging track' => 'jogging',
|
||
'lift' => 'elevator',
|
||
'elevator' => 'elevator',
|
||
);
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Oda olanaklarına özel alias: Booking.com oda facility adı → property_fact.name
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private $roomAliasMap = array(
|
||
'safe' => 'safe box',
|
||
'towels' => 'towel',
|
||
'linens' => 'towel',
|
||
'tea/coffee maker' => 'coffee maker',
|
||
'coffee/tea maker' => 'coffee maker',
|
||
'electric kettle' => 'kettle',
|
||
'private bathroom' => 'bathroom',
|
||
'telephone' => 'telephone',
|
||
'toilet paper' => 'toilet',
|
||
'free toiletries' => 'hair dryer',
|
||
'slippers' => 'bathrobe',
|
||
'bath or shower' => 'shower',
|
||
'bathtub or shower' => 'shower',
|
||
'shower only' => 'shower',
|
||
);
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// GuzzleHttp → Booking.com API
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function bookingClient()
|
||
{
|
||
return new Client(array(
|
||
'timeout' => 30,
|
||
'headers' => array(
|
||
'x-rapidapi-host' => env('BULUT_RAPIDAPI_HOST', self::API_HOST),
|
||
'x-rapidapi-key' => env('BULUT_RAPIDAPI_KEY'),
|
||
'Content-Type' => 'application/json',
|
||
),
|
||
));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 0: Booking.com'dan otel verilerini çek
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function fetchBookingData($hotelId, $checkin, $checkout)
|
||
{
|
||
$client = $this->bookingClient();
|
||
|
||
// Otel detayı (property bilgileri: isim, konum, dil, timezone vb.)
|
||
$detailResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelDetails', array(
|
||
'query' => array(
|
||
'hotel_id' => $hotelId,
|
||
'arrival_date' => $checkin,
|
||
'departure_date' => $checkout,
|
||
'adults' => 1,
|
||
'room_qty' => 1,
|
||
'languagecode' => 'en-us',
|
||
'currency_code' => 'USD',
|
||
'units' => 'metric',
|
||
'temperature_unit' => 'c',
|
||
),
|
||
));
|
||
$detail = json_decode($detailResp->getBody()->getContents(), true);
|
||
$detail = isset($detail['data']) ? $detail['data'] : array();
|
||
|
||
// Oda listesi — tüm oda tiplerini çek (getRoomList daha kapsamlı)
|
||
// getHotelDetails sadece o güne müsait odaları döndürür, getRoomList tüm tipleri verir
|
||
$checkinRooms = date('Y-m-d', strtotime('+30 days'));
|
||
$checkoutRooms = date('Y-m-d', strtotime('+37 days'));
|
||
$roomListResp = $client->get(self::API_BASE . '/api/v1/hotels/getRoomList', array(
|
||
'query' => array(
|
||
'hotel_id' => $hotelId,
|
||
'arrival_date' => $checkinRooms,
|
||
'departure_date' => $checkoutRooms,
|
||
'adults' => 2,
|
||
'room_qty' => 1,
|
||
'currency_code' => 'EUR',
|
||
'languagecode' => 'en-us',
|
||
),
|
||
));
|
||
$roomListData = json_decode($roomListResp->getBody()->getContents(), true);
|
||
$roomListData = isset($roomListData['data']) ? $roomListData['data'] : array();
|
||
|
||
// getRoomList'ten gelen block[] ve rooms[] ile detail'i zenginleştir
|
||
if (!empty($roomListData['block'])) {
|
||
$detail['block'] = $roomListData['block'];
|
||
}
|
||
if (!empty($roomListData['rooms'])) {
|
||
$detail['rooms'] = $roomListData['rooms'];
|
||
}
|
||
|
||
// Facility listesi
|
||
$facilResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelFacilities', array(
|
||
'query' => array('hotel_id' => $hotelId),
|
||
));
|
||
$facilData = json_decode($facilResp->getBody()->getContents(), true);
|
||
$facilData = isset($facilData['data']) ? $facilData['data'] : array();
|
||
|
||
// Otel fotograflari
|
||
$photoResp = $client->get(self::API_BASE . '/api/v1/hotels/getHotelPhotos', array(
|
||
'query' => array('hotel_id' => $hotelId),
|
||
));
|
||
$photoData = json_decode($photoResp->getBody()->getContents(), true);
|
||
|
||
return array('detail' => $detail, 'facilities' => $facilData, 'photos' => $photoData);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 1: Kullanıcı + Property oluştur
|
||
// → user/register-user-with-property mantığı
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function createUserAndProperty(array $params, $propertyName, $userId = null)
|
||
{
|
||
// Kullanıcı zaten varsa sadece property ekle
|
||
$userService = app(\App\Core\Service\UserService::class);
|
||
$propertyService = app(\App\Core\Service\PropertyService::class);
|
||
$userPropertyMappingService = app(\App\Core\Service\UserPropertyMappingService::class);
|
||
$productService = app(\App\Core\Service\ProductService::class);
|
||
$jwtService = app(\App\Core\Service\JwtService::class);
|
||
$apiAccessTokenService = app(\App\Core\Service\ApiAccessTokenService::class);
|
||
|
||
// Kullanıcı oluştur
|
||
$userParams = array(
|
||
'name' => $params['name'],
|
||
'surname' => $params['surname'],
|
||
'email' => $params['email'],
|
||
'password' => $params['password'],
|
||
'status' => 1,
|
||
'user_type' => 1,
|
||
'property_name' => $propertyName,
|
||
);
|
||
$userCreate = $userService->create($userParams);
|
||
if ($userCreate['status'] != 'success') {
|
||
throw new ApiErrorException('Kullanıcı oluşturulamadı: ' . $userCreate['message']);
|
||
}
|
||
$user = $userCreate['data'];
|
||
|
||
// UserService::create rastgele şifre üretiyor (Str::random(6))
|
||
// Kullanıcının istediği şifreyi DB'ye doğrudan güncelle
|
||
DB::table('user')
|
||
->where('id', $user['id'])
|
||
->update(array('password' => \Illuminate\Support\Facades\Hash::make($params['password'])));
|
||
|
||
// Property oluştur
|
||
$propertyCreate = $propertyService->create(array(
|
||
'name' => $propertyName,
|
||
'status' => 1,
|
||
'created_by' => $user['id'],
|
||
'updated_by' => $user['id'],
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
if ($propertyCreate['status'] != 'success') {
|
||
throw new ApiErrorException('Property oluşturulamadı: ' . $propertyCreate['message']);
|
||
}
|
||
$property = $propertyCreate['data'];
|
||
|
||
// User-Property mapping
|
||
$mappingCreate = $userPropertyMappingService->create(array(
|
||
'user_id' => $user['id'],
|
||
'property_id' => $property['id'],
|
||
'status' => 1,
|
||
'created_by' => $user['id'],
|
||
'updated_by' => $user['id'],
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
if ($mappingCreate['status'] != 'success') {
|
||
throw new ApiErrorException('Kullanıcı-Property eşleştirme hatası: ' . $mappingCreate['message']);
|
||
}
|
||
|
||
// Default ürünleri ata
|
||
$productService->setDefaultPropertyProducts(array(
|
||
'user_id' => $user['id'],
|
||
'property_id' => $property['id'],
|
||
));
|
||
|
||
// JWT token al
|
||
$jwtResult = $jwtService->jwtCreate(array('user_id' => $user['id']));
|
||
if ($jwtResult['status'] != 'success') {
|
||
throw new ApiErrorException('Token oluşturulamadı');
|
||
}
|
||
$jwt = $jwtResult['data'];
|
||
|
||
$apiAccessTokenService->create(array(
|
||
'token' => md5(fillOnUndefined($jwt, 'token')),
|
||
'expire_date' => fillOnUndefined($jwt, 'exp'),
|
||
'user_id' => $user['id'],
|
||
'invalidate' => fillOnUndefined($jwt, 'invalidate', 0),
|
||
));
|
||
|
||
return array(
|
||
'user' => $user,
|
||
'property_id' => $property['id'],
|
||
'token' => $jwt['token'],
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 2: Property bilgilerini güncelle
|
||
// → property/info/update
|
||
// Booking.com detail → property_info, additional_info, locale
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function updatePropertyInfo($propertyId, $userId, array $detail)
|
||
{
|
||
$propertyService = app(\App\Core\Service\PropertyService::class);
|
||
|
||
$name = isset($detail['hotel_name']) ? $detail['hotel_name'] : null;
|
||
$raw = isset($detail['rawData']) ? $detail['rawData'] : array();
|
||
|
||
// Stars: rawData.accuratePropertyClass > rawData.propertyClass > rawData.qualityClass
|
||
$rating = null;
|
||
foreach (array('accuratePropertyClass', 'propertyClass', 'qualityClass') as $rk) {
|
||
if (!empty($raw[$rk])) {
|
||
$rating = (int) $raw[$rk];
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Check-in/out: rawData.checkin.fromTime / rawData.checkout.untilTime
|
||
$checkIn = isset($raw['checkin']['fromTime']) ? $raw['checkin']['fromTime'] : null;
|
||
$checkOut = isset($raw['checkout']['untilTime']) ? $raw['checkout']['untilTime'] : null;
|
||
|
||
// Ülke kodu (max 5 char)
|
||
$countryCode = null;
|
||
foreach (array('countryCode', 'countrycode', 'country_code') as $ck) {
|
||
if (!empty($raw[$ck])) {
|
||
$countryCode = strtoupper($raw[$ck]);
|
||
break;
|
||
}
|
||
}
|
||
if (!$countryCode && !empty($detail['countrycode'])) {
|
||
$countryCode = strtoupper($detail['countrycode']);
|
||
}
|
||
|
||
// Konuşulan diller: "en-gb" → "en_gb", "tr" → "tr"
|
||
$spokenRaw = isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array('en');
|
||
$langCodes = array();
|
||
foreach ($spokenRaw as $lang) {
|
||
$normalized = str_replace('-', '_', strtolower($lang));
|
||
// Sadece 2 veya 5 harfli geçerli kodlar
|
||
if (preg_match('/^[a-z]{2}(_[a-z]{2})?$/', $normalized)) {
|
||
$langCodes[] = $normalized;
|
||
}
|
||
}
|
||
if (empty($langCodes)) {
|
||
$langCodes = array('en');
|
||
}
|
||
|
||
$updateParams = array(
|
||
'property_id' => $propertyId,
|
||
'user_id' => $userId,
|
||
'locale' => 'en',
|
||
'has_locale_name' => false,
|
||
'property_language_spoken' => $langCodes,
|
||
'property_info' => array(
|
||
'name' => $name,
|
||
'property_type_id' => 1, // 1 = Hotel
|
||
'chain_id' => 1, // 1 = Independent
|
||
'rating' => $rating,
|
||
'country' => $countryCode,
|
||
),
|
||
'additional_info' => array(
|
||
'checkin_time' => $checkIn,
|
||
'checkout_time' => $checkOut,
|
||
'number_of_rooms' => !empty($detail['number_of_rooms']) ? (string)$detail['number_of_rooms'] : null,
|
||
'property_timezone' => !empty($detail['timezone'])
|
||
? $this->resolveTimezoneId($detail['timezone'])
|
||
: null,
|
||
),
|
||
);
|
||
|
||
$result = $propertyService->propertyUpdate($updateParams);
|
||
if ($result['status'] != 'success') {
|
||
throw new ApiErrorException('Property güncelleme hatası: ' . $result['message']);
|
||
}
|
||
|
||
return $result['data'];
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 3: İletişim bilgilerini kaydet
|
||
// → property/contact/update
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function updatePropertyContact($propertyId, $userId, array $detail, $phone = null)
|
||
{
|
||
$propertyContactService = app(\App\Core\Service\PropertyContactService::class);
|
||
|
||
// Booking.com top-level alanlar
|
||
$email = isset($detail['email']) ? $detail['email'] : null;
|
||
$web = isset($detail['url']) ? $detail['url'] : null;
|
||
$address = isset($detail['address']) ? $detail['address'] : null;
|
||
$zip = isset($detail['zip']) ? $detail['zip'] : null;
|
||
$lat = isset($detail['latitude']) ? $detail['latitude'] : null;
|
||
$lng = isset($detail['longitude']) ? $detail['longitude'] : null;
|
||
|
||
if (!$phone && isset($detail['phone'])) {
|
||
$phone = $detail['phone'];
|
||
}
|
||
|
||
// Validator: email required|email — boşsa placeholder kullan
|
||
if (!$email) {
|
||
$email = 'property' . $propertyId . '@extranetwork.com';
|
||
}
|
||
|
||
// Validator: phone veya mobile gerekli
|
||
if (!$phone) {
|
||
$phone = '0000000000';
|
||
}
|
||
|
||
$contactParams = array(
|
||
'property_id' => $propertyId,
|
||
'user_id' => $userId,
|
||
'contact' => array(
|
||
'phone' => $phone,
|
||
'email' => $email,
|
||
'web' => $web,
|
||
'address' => $address,
|
||
'zip_code' => $zip,
|
||
'latitude' => $lat,
|
||
'longitude' => $lng,
|
||
),
|
||
);
|
||
|
||
$result = $propertyContactService->propertyContactUpdateOrCreate($contactParams);
|
||
if ($result['status'] != 'success') {
|
||
throw new ApiErrorException('İletişim kaydetme hatası: ' . $result['message']);
|
||
}
|
||
|
||
return $result['data'];
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 4: Facility eşleştir → property_fact_mapping'e ekle
|
||
// → property/fact-mapping/add
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function mapAndSaveFacilities($propertyId, $userId, array $facilData)
|
||
{
|
||
$propertyFactMappingService = app(\App\Core\Service\PropertyFactMappingService::class);
|
||
|
||
// Tüm API isimlerini topla
|
||
$apiNames = array();
|
||
$addUniq = function ($name) use (&$apiNames) {
|
||
$name = trim($name);
|
||
if ($name !== '' && !in_array($name, $apiNames)) {
|
||
$apiNames[] = $name;
|
||
}
|
||
};
|
||
|
||
if (isset($facilData['facilities']) && is_array($facilData['facilities'])) {
|
||
foreach ($facilData['facilities'] as $fac) {
|
||
if (!empty($fac['instances'])) {
|
||
foreach ($fac['instances'] as $inst) {
|
||
if (!empty($inst['title'])) {
|
||
$addUniq($inst['title']);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (isset($facilData['accommodationHighlights']) && is_array($facilData['accommodationHighlights'])) {
|
||
foreach ($facilData['accommodationHighlights'] as $h) {
|
||
if (!empty($h['title'])) {
|
||
$addUniq($h['title']);
|
||
}
|
||
}
|
||
}
|
||
if (isset($facilData['highlights']) && is_array($facilData['highlights'])) {
|
||
foreach ($facilData['highlights'] as $h) {
|
||
if (!empty($h['instances'])) {
|
||
foreach ($h['instances'] as $inst) {
|
||
if (!empty($inst['title'])) {
|
||
$addUniq($inst['title']);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// DB exact index
|
||
$allFacts = PropertyFact::select(array('id', 'name', 'type'))->get();
|
||
$exactIndex = array();
|
||
foreach ($allFacts as $fact) {
|
||
$key = mb_strtolower(trim($fact->name));
|
||
if (!isset($exactIndex[$key]) || $fact->type == 1) {
|
||
$exactIndex[$key] = $fact;
|
||
}
|
||
}
|
||
|
||
$saved = array();
|
||
$skipped = array();
|
||
|
||
foreach ($apiNames as $apiName) {
|
||
$lower = mb_strtolower(trim($apiName));
|
||
$searchKey = isset($this->aliasMap[$lower]) ? $this->aliasMap[$lower] : $lower;
|
||
$fact = null;
|
||
$matchType = 'none';
|
||
|
||
// 1. Exact / alias
|
||
if (isset($exactIndex[$searchKey])) {
|
||
$fact = $exactIndex[$searchKey];
|
||
$matchType = ($searchKey !== $lower) ? 'alias' : 'exact';
|
||
}
|
||
|
||
// 2. DB LIKE
|
||
if (!$fact) {
|
||
$like = PropertyFact::where('type', 1)
|
||
->whereRaw('LOWER(name) LIKE ?', array('%' . $lower . '%'))
|
||
->orderByRaw('LENGTH(name) ASC')
|
||
->first();
|
||
if ($like) {
|
||
$fact = $like;
|
||
$matchType = 'like';
|
||
}
|
||
}
|
||
|
||
// 3. Ters LIKE
|
||
if (!$fact) {
|
||
$reverse = PropertyFact::where('type', 1)
|
||
->whereRaw('LOWER(?) LIKE CONCAT(\'%\', LOWER(name), \'%\')', array($lower))
|
||
->whereRaw('LENGTH(name) >= 5')
|
||
->orderByRaw('LENGTH(name) DESC')
|
||
->first();
|
||
if ($reverse) {
|
||
$fact = $reverse;
|
||
$matchType = 'reverse_like';
|
||
}
|
||
}
|
||
|
||
if ($fact) {
|
||
// Kaydet
|
||
$mappingResult = $propertyFactMappingService->addPropertyFactMapping(array(
|
||
'property_id' => $propertyId,
|
||
'user_id' => $userId,
|
||
'data' => array(
|
||
array('id' => $fact->id),
|
||
),
|
||
));
|
||
$saved[] = array(
|
||
'api_name' => $apiName,
|
||
'fact_id' => $fact->id,
|
||
'fact_name' => $fact->name,
|
||
'match_type' => $matchType,
|
||
'saved' => ($mappingResult['status'] == 'success'),
|
||
);
|
||
} else {
|
||
$skipped[] = $apiName;
|
||
}
|
||
}
|
||
|
||
return array(
|
||
'api_total' => count($apiNames),
|
||
'saved_count' => count($saved),
|
||
'skipped_count' => count($skipped),
|
||
'saved' => $saved,
|
||
'skipped' => $skipped,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 5: Booking.com'daki oda bilgilerinden oda ekle
|
||
// → property/room/add-room-bed
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Booking.com bed_type ID → sistemdeki property_room_bed_type.id
|
||
//
|
||
// Booking.com standart IDs (DataCrawler API):
|
||
// 1=Twin 2=Double 3=King 4=Queen 5=Single 6=Sofa 7=Bunk 8=Futon
|
||
// Sistemdeki property_room_bed_type:
|
||
// 1=Bunk Bed 2=Double Bed 3=French Bed 4=Futon 5=King Bed
|
||
// 6=Queen Bed 7=Single Bed 8=Sofa Bed 9=Twin Bed 10=Foldable Bed
|
||
// 11=Sleeping Bag 12=Triple Bunk Bed 13=Extra bed 14=Cot
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private $bedTypeMap = array(
|
||
1 => 9, // Twin Bed(s) → Twin Bed
|
||
2 => 2, // Double Bed → Double Bed
|
||
3 => 5, // King Bed → King Bed
|
||
4 => 6, // Queen Bed → Queen Bed
|
||
5 => 7, // Single Bed → Single Bed
|
||
6 => 8, // Sofa Bed → Sofa Bed
|
||
7 => 1, // Bunk Bed → Bunk Bed
|
||
8 => 4, // Futon → Futon
|
||
9 => 3, // French Bed → French Bed
|
||
10 => 10, // Foldable Bed → Foldable Bed
|
||
37 => 14, // Cot/Cribs → Cot
|
||
40 => 13, // Extra Bed → Extra bed
|
||
);
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Booking.com view adı (highlight.translated_name) → property_room_view_type.id
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private $viewTypeMap = array(
|
||
'park view' => 1,
|
||
'pool view' => 2,
|
||
'river view' => 3,
|
||
'sea view' => 4,
|
||
'ocean view' => 22,
|
||
'street view' => 5,
|
||
'valley view' => 6,
|
||
'monument view' => 7,
|
||
'bay view' => 8,
|
||
'beach view' => 9,
|
||
'city view' => 10,
|
||
'countryside view' => 11,
|
||
'courtyard view' => 12,
|
||
'garden view' => 13,
|
||
'gulf view' => 14,
|
||
'harbor view' => 15,
|
||
'lagoon view' => 16,
|
||
'lake view' => 17,
|
||
'marina view' => 18,
|
||
'mountain view' => 19,
|
||
'nature view' => 20,
|
||
'no window' => 21,
|
||
'land view' => 23,
|
||
'forest view' => 24,
|
||
'island view' => 25,
|
||
'golf course view' => 26,
|
||
'landmark view' => 30,
|
||
'sea view' => 4,
|
||
);
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Booking.com oda adı keyword → property_room_type.id
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private $roomTypeKeywords = array(
|
||
'standard' => 24, // Standard Room
|
||
'standart' => 24,
|
||
'single' => 42, // Single Room
|
||
'double' => 43, // Double Room
|
||
'twin' => 43, // Double Room (çoğu twin=double planı)
|
||
'triple' => 44, // Triple Room
|
||
'quad' => 45, // Quadruple Room
|
||
'family' => 18, // Family Room
|
||
'suite' => 25, // Suite
|
||
'junior suite' => 20,
|
||
'grand suite' => 19,
|
||
'king suite' => 21,
|
||
'deluxe' => 16, // Deluxe
|
||
'superior' => 26, // Superior
|
||
'executive' => 48, // Executive Room
|
||
'villa' => 27, // Villa
|
||
'bungalow' => 14, // Bungalow
|
||
'loft' => 31, // Loft Room
|
||
'studio' => 29, // Studio Room
|
||
'apart' => 12, // Apartment
|
||
'apartment' => 12,
|
||
'penthouse' => 22, // Pent House
|
||
'accessible' => 11, // Accessible Room
|
||
'honeymoon' => 34, // Honeymoon Room
|
||
'economy' => 17, // Economy
|
||
'premium' => 23, // Premium Room
|
||
'club' => 15, // Club Room
|
||
'swim up' => 46, // Swim Up Room
|
||
'swim-up' => 46,
|
||
'corner' => 49, // Corner Room
|
||
'large' => 47, // Standard Large Room
|
||
);
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 5: Booking.com'daki oda bilgilerinden oda ekle
|
||
//
|
||
// Kaynak 1: detail['block'][] → room_name, max_occupancy, nr_adults,
|
||
// room_surface_in_m2, number_of_bathrooms
|
||
// Kaynak 2: detail['rooms'][id] → bed_configurations, highlights (view)
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function insertRooms($propertyId, $userId, array $detail)
|
||
{
|
||
$propertyRoomService = app(\App\Core\Service\PropertyRoomService::class);
|
||
|
||
// ── Kaynak 1: block[] → her room_id için tek kayıt ──────────────────────
|
||
$blockByRoomId = array();
|
||
$blockRaw = isset($detail['block']) ? $detail['block'] : array();
|
||
foreach ($blockRaw as $b) {
|
||
$rid = isset($b['room_id']) ? $b['room_id'] : null;
|
||
if ($rid && !isset($blockByRoomId[$rid])) {
|
||
$blockByRoomId[$rid] = $b;
|
||
}
|
||
}
|
||
|
||
// ── Kaynak 2: rooms[room_id] → bed configs + view highlights ────────────
|
||
$roomsRaw = isset($detail['rooms']) ? $detail['rooms'] : array();
|
||
|
||
// rooms[] boşsa sadece block verisini kullan
|
||
if (empty($roomsRaw) && empty($blockByRoomId)) {
|
||
// Hiç oda yoksa tek generic oda
|
||
$blockByRoomId = array(
|
||
0 => array(
|
||
'room_name' => 'Standard Room',
|
||
'max_occupancy' => 2,
|
||
'nr_adults' => 2,
|
||
'room_surface_in_m2' => null,
|
||
'number_of_bathrooms' => null,
|
||
),
|
||
);
|
||
}
|
||
|
||
// rooms[] boşsa block ID'lerini rooms olarak kullan
|
||
if (empty($roomsRaw)) {
|
||
foreach ($blockByRoomId as $rid => $b) {
|
||
$roomsRaw[$rid] = array();
|
||
}
|
||
}
|
||
if (empty($blockByRoomId)) {
|
||
foreach (array_keys($roomsRaw) as $rid) {
|
||
$blockByRoomId[$rid] = array();
|
||
}
|
||
}
|
||
|
||
// Distinct room_id birleşimi
|
||
$allRoomIds = array_unique(array_merge(array_keys($blockByRoomId), array_keys($roomsRaw)));
|
||
|
||
$inserted = array();
|
||
|
||
foreach ($allRoomIds as $roomId) {
|
||
$block = isset($blockByRoomId[$roomId]) ? $blockByRoomId[$roomId] : array();
|
||
$room = isset($roomsRaw[$roomId]) ? $roomsRaw[$roomId] : array();
|
||
|
||
// ── Oda adı ──────────────────────────────────────────────────────────
|
||
$roomName = trim(isset($block['room_name']) ? $block['room_name'] : 'Standard Room');
|
||
|
||
// ── room_type_id: oda adındaki anahtar kelimeye göre ─────────────────
|
||
$roomTypeId = $this->guessRoomTypeId($roomName);
|
||
|
||
// ── Kapasite ─────────────────────────────────────────────────────────
|
||
$maxOccupancy = isset($block['max_occupancy']) ? (int)$block['max_occupancy'] : 2;
|
||
$maxAdult = isset($block['nr_adults']) ? (int)$block['nr_adults'] : $maxOccupancy;
|
||
$maxChild = isset($block['nr_children']) ? (int)$block['nr_children'] : 0;
|
||
|
||
// ── Oda boyutu ───────────────────────────────────────────────────────
|
||
// Validator required|max:30 → null yerine 0 gönder
|
||
$roomSizeRaw = isset($block['room_surface_in_m2']) ? (int)$block['room_surface_in_m2'] : 0;
|
||
$roomSize = $roomSizeRaw > 0 ? $roomSizeRaw : 0;
|
||
$roomSizeType = $roomSizeRaw > 0 ? 1 : 0; // 1 = m²
|
||
|
||
// ── Oda adedi (kaç adet bu tipten var) ──────────────────────────────
|
||
$roomTypeCount = isset($block['room_count']) && $block['room_count'] > 0
|
||
? (int)$block['room_count'] : 1;
|
||
|
||
// ── Banyo sayısı ─────────────────────────────────────────────────────
|
||
$bathroomCount = isset($block['number_of_bathrooms']) && $block['number_of_bathrooms'] > 0
|
||
? (int)$block['number_of_bathrooms'] : null;
|
||
|
||
// ── Açıklama ─────────────────────────────────────────────────────────
|
||
$description = isset($room['description']) ? $room['description'] : null;
|
||
|
||
// ── Yatak konfigürasyonları → room_bed_group ─────────────────────────
|
||
// Sistem formatı: [[{bed_type_id:9}, {bed_type_id:9}], [{bed_type_id:2}]]
|
||
// Her conf = bir grup, içindeki bed_types[].count kadar tekrar
|
||
$bedConfigs = isset($room['bed_configurations']) ? $room['bed_configurations'] : array();
|
||
$roomBedGroup = array();
|
||
|
||
foreach ($bedConfigs as $conf) {
|
||
$groupBeds = array();
|
||
$bedTypes = isset($conf['bed_types']) ? $conf['bed_types'] : array();
|
||
foreach ($bedTypes as $bt) {
|
||
$bookingBedId = isset($bt['bed_type']) ? (int)$bt['bed_type'] : 0;
|
||
$ourBedTypeId = isset($this->bedTypeMap[$bookingBedId]) ? $this->bedTypeMap[$bookingBedId] : null;
|
||
$bedCount = isset($bt['count']) ? (int)$bt['count'] : 1;
|
||
if ($ourBedTypeId) {
|
||
for ($i = 0; $i < $bedCount; $i++) {
|
||
$groupBeds[] = array('bed_type_id' => $ourBedTypeId);
|
||
}
|
||
}
|
||
}
|
||
if (!empty($groupBeds)) {
|
||
$roomBedGroup[] = $groupBeds;
|
||
}
|
||
}
|
||
|
||
// ── View tipleri → room_view_type ────────────────────────────────────
|
||
// Booking.com highlights'daki "translated_name" → viewTypeMap eşleşmesi
|
||
// Eşleşmezse icon'a bak: "city"→10, "sea"→4, "pool"→2, "mountain"→19
|
||
$iconViewMap = array(
|
||
'city' => 10,
|
||
'sea' => 4,
|
||
'ocean' => 22,
|
||
'pool' => 2,
|
||
'mountain' => 19,
|
||
'garden' => 13,
|
||
'park' => 1,
|
||
'river' => 3,
|
||
'lake' => 17,
|
||
'beach' => 9,
|
||
'forest' => 24,
|
||
'harbor' => 15,
|
||
'marina' => 18,
|
||
'eye' => 4, // Booking.com Sea view ikonu "eye" olarak geliyor
|
||
'landmark' => 30, // Landmark view
|
||
);
|
||
$highlights = isset($room['highlights']) ? $room['highlights'] : array();
|
||
$roomViewType = array();
|
||
foreach ($highlights as $h) {
|
||
$hName = mb_strtolower(trim(isset($h['translated_name']) ? $h['translated_name'] : ''));
|
||
$hIcon = mb_strtolower(trim(isset($h['icon']) ? $h['icon'] : ''));
|
||
$viewId = null;
|
||
// 1) Tam eşleşme: "city view"
|
||
if (isset($this->viewTypeMap[$hName])) {
|
||
$viewId = $this->viewTypeMap[$hName];
|
||
}
|
||
// 2) Kısmi eşleşme: highlight adında keyword geçiyor
|
||
if (!$viewId) {
|
||
foreach ($this->viewTypeMap as $keyword => $vid) {
|
||
$base = str_replace(' view', '', $keyword);
|
||
if (strpos($hName, $base) !== false) {
|
||
$viewId = $vid;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
// 3) Icon eşleşmesi: "city" ikonu → 10
|
||
if (!$viewId && isset($iconViewMap[$hIcon])) {
|
||
$viewId = $iconViewMap[$hIcon];
|
||
}
|
||
if ($viewId && !in_array($viewId, $roomViewType)) {
|
||
$roomViewType[] = $viewId;
|
||
}
|
||
}
|
||
|
||
// ── Service'e gönder ─────────────────────────────────────────────────
|
||
$roomParams = array(
|
||
'property_id' => $propertyId,
|
||
'user_id' => $userId,
|
||
'locale' => 'en',
|
||
'name' => $roomName,
|
||
'room_type_id' => $roomTypeId,
|
||
'max_occupancy' => $maxOccupancy,
|
||
'max_adult' => $maxAdult,
|
||
'max_child' => $maxChild,
|
||
'occupancy_lock' => 0,
|
||
'exclude_occupancy' => 0,
|
||
'room_size' => $roomSize,
|
||
'room_size_type' => $roomSizeType,
|
||
'room_type_count' => $roomTypeCount,
|
||
'bathroom_count' => $bathroomCount,
|
||
'toilet_count' => null,
|
||
'lounge_count' => null,
|
||
'room_count' => null,
|
||
'max_child_number' => null,
|
||
'description' => array(
|
||
array('language_code' => 'en', 'description' => $description)
|
||
),
|
||
'room_bed_group' => $roomBedGroup,
|
||
'room_view_type' => $roomViewType,
|
||
'is_connected_room' => null,
|
||
'is_connected_room_price' => null,
|
||
'is_connected_room_availability' => null,
|
||
'connected_rooms' => array(),
|
||
);
|
||
|
||
$result = $propertyRoomService->addPropertyRoomAndBed($roomParams);
|
||
$isSaved = ($result['status'] == 'success' || $result['status'] === true);
|
||
$facSummary = array();
|
||
$photoSummary = array();
|
||
|
||
if ($isSaved) {
|
||
// addPropertyRoomAndBed data döndürmüyor — son eklenen room_id'yi çek
|
||
$newRoom = DB::table('property_room')
|
||
->where('property_id', $propertyId)
|
||
->where('name', $roomName)
|
||
->orderBy('id', 'desc')
|
||
->first();
|
||
$newRoomId = $newRoom ? $newRoom->id : null;
|
||
|
||
if ($newRoomId) {
|
||
// Oda olanakları → property_room_fact_mapping
|
||
if (!empty($room['facilities'])) {
|
||
$facSummary = $this->mapAndSaveRoomFacilities(
|
||
$newRoomId,
|
||
$propertyId,
|
||
$userId,
|
||
$room['facilities']
|
||
);
|
||
}
|
||
|
||
// Oda fotoğrafları → property_photo + property_room_photo_mapping
|
||
if (!empty($room['photos'])) {
|
||
$photoSummary = $this->mapAndSaveRoomPhotos(
|
||
$newRoomId,
|
||
$propertyId,
|
||
$userId,
|
||
$room['photos'],
|
||
isset($detail['hotel_id']) ? $detail['hotel_id'] : 0
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
$inserted[] = array(
|
||
'room_id' => $roomId,
|
||
'room_name' => $roomName,
|
||
'room_type_id' => $roomTypeId,
|
||
'beds' => array_map(function ($g) {
|
||
return $g;
|
||
}, $roomBedGroup),
|
||
'views' => $roomViewType,
|
||
'room_facts' => $facSummary,
|
||
'room_photos' => $photoSummary,
|
||
'saved' => $isSaved,
|
||
'message' => !$isSaved ? $result['message'] : null,
|
||
);
|
||
}
|
||
|
||
return array(
|
||
'room_count' => count($inserted),
|
||
'rooms' => $inserted,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Oda fotoğraflarını indir, property_photo'ya kaydet, room_photo_mapping'e bağla
|
||
// Booking.com: rooms[id]['photos'] → [{url_original, url_max750, ...}]
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function mapAndSaveRoomPhotos($roomId, $propertyId, $userId, array $photos, $hotelId)
|
||
{
|
||
if (empty($photos)) {
|
||
return array('saved_count' => 0, 'skipped_count' => 0);
|
||
}
|
||
|
||
$uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/');
|
||
$urlPath = '/property-photos/' . $propertyId . '/';
|
||
$filePath = $uploadRoot . $urlPath;
|
||
|
||
if (!File::exists($filePath)) {
|
||
File::makeDirectory($filePath, 0777, true);
|
||
}
|
||
|
||
$saved = array();
|
||
$skipped = array();
|
||
$index = 0;
|
||
|
||
foreach ($photos as $photo) {
|
||
// URL: url_original > url_max750 > url_max1280 > url_max300
|
||
$url = '';
|
||
foreach (array('url_original', 'url_max750', 'url_max1280', 'url_max300') as $key) {
|
||
if (!empty($photo[$key])) {
|
||
$url = $photo[$key];
|
||
break;
|
||
}
|
||
}
|
||
if ($url === '') {
|
||
$skipped[] = 'empty_url';
|
||
continue;
|
||
}
|
||
|
||
try {
|
||
$photoId = isset($photo['photo_id']) ? $photo['photo_id'] : '';
|
||
$fileName = time() . '_bk' . $hotelId . '_r' . $roomId . '_' . $index;
|
||
$fileExt = 'jpg';
|
||
$tempPath = sys_get_temp_dir() . '/' . $fileName . '_orig.jpg';
|
||
|
||
$this->downloadRawImage($url, $tempPath);
|
||
|
||
$image = Image::make($tempPath);
|
||
$imageWidth = $image->width();
|
||
$imageHeight = $image->height();
|
||
$fileSize = (int) ceil(filesize($tempPath) / 1024);
|
||
$quality = 80;
|
||
|
||
// Orijinal — 2000px üzeriyse küçült
|
||
if ($imageHeight > 2000 || $imageWidth > 2000) {
|
||
if ($imageHeight > $imageWidth) {
|
||
$r = $imageHeight / 2000;
|
||
$image->fit((int)($imageWidth / $r), 2000);
|
||
} else {
|
||
$r = $imageWidth / 2000;
|
||
$image->fit(2000, (int)($imageHeight / $r));
|
||
}
|
||
}
|
||
$image->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt);
|
||
|
||
// _medium (max 1600x768)
|
||
$mediumImage = Image::make($tempPath);
|
||
$ratio = $imageHeight > 0 ? ($imageHeight / 768) : 1;
|
||
$resizeWidth = (int)($imageWidth / max($ratio, 1));
|
||
if ($resizeWidth > 1599) {
|
||
$mediumImage->fit(1600, 768);
|
||
} else {
|
||
$mediumImage->fit(max($resizeWidth, 1), 768);
|
||
}
|
||
$mediumImage->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '_medium.' . $fileExt);
|
||
|
||
// _thumbnail (300x300)
|
||
Image::make($tempPath)
|
||
->fit(300, 300)
|
||
->encode($fileExt, $quality)
|
||
->orientate()
|
||
->save($filePath . $fileName . '_thumbnail.' . $fileExt);
|
||
|
||
if (File::exists($tempPath)) {
|
||
File::delete($tempPath);
|
||
}
|
||
|
||
$isCompatible = ($imageWidth >= 1150 && $imageHeight >= 600) ? 1 : 0;
|
||
|
||
$photoRecord = PropertyPhoto::create(array(
|
||
'property_id' => $propertyId,
|
||
'property_photo_category_id' => 1,
|
||
'photo_path' => $urlPath,
|
||
'photo_name' => $fileName,
|
||
'file_size' => $fileSize,
|
||
'file_ext' => $fileExt,
|
||
'photo_resolution' => $imageWidth . 'x' . $imageHeight,
|
||
'is_default' => 0,
|
||
'is_compatible_with_myweb_slider' => $isCompatible,
|
||
'photo_order' => 0,
|
||
'photo_rank' => 0,
|
||
'is_temp' => 1,
|
||
'status' => 1,
|
||
'created_by' => 1,
|
||
'updated_by' => 1,
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
|
||
// property_room_photo_mapping'e bağla
|
||
$alreadyLinked = DB::table('property_room_photo_mapping')
|
||
->where('room_id', $roomId)
|
||
->where('photo_id', $photoRecord->id)
|
||
->exists();
|
||
|
||
if (!$alreadyLinked) {
|
||
DB::table('property_room_photo_mapping')->insert(array(
|
||
'property_id' => $propertyId,
|
||
'room_id' => $roomId,
|
||
'photo_id' => $photoRecord->id,
|
||
'created_by' => $userId,
|
||
'updated_by' => $userId,
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
}
|
||
|
||
$saved[] = array(
|
||
'photo_id' => $photoRecord->id,
|
||
'file_name' => $fileName . '.' . $fileExt,
|
||
'resolution' => $imageWidth . 'x' . $imageHeight,
|
||
'source_url' => $url,
|
||
);
|
||
} catch (Exception $e) {
|
||
Log::error('propertyInsert.roomPhoto [room=' . $roomId . ', i=' . $index . ']: ' . $e->getMessage());
|
||
$skipped[] = $url;
|
||
}
|
||
|
||
$index++;
|
||
}
|
||
|
||
return array(
|
||
'saved_count' => count($saved),
|
||
'skipped_count' => count($skipped),
|
||
'photos' => $saved,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Oda olanaklarını (room facilities) property_room_fact_mapping'e kaydet
|
||
// Booking.com: rooms[id]['facilities'] → [{id, name, alt_facilitytype_id, ...}]
|
||
// View tiplerini atla (viewTypeMap ile zaten ekleniyor)
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function mapAndSaveRoomFacilities($roomId, $propertyId, $userId, array $facilities)
|
||
{
|
||
// View kategorisi facilitytype_id'leri (9=View, 14=View alt) — zaten view mapping'de ekleniyor
|
||
$skipFacilityTypeIds = array(9, 14);
|
||
|
||
// Sistemdeki tüm fact'leri küçük harfli isimle index'e al (type=1: property/room fact)
|
||
$allFacts = PropertyFact::select(array('id', 'name', 'type'))->where('type', 1)->get();
|
||
$exactIndex = array();
|
||
foreach ($allFacts as $fact) {
|
||
$key = mb_strtolower(trim($fact->name));
|
||
// Aynı isimde birden fazla fact varsa daha küçük id'liyi (ilk ekleneni) tut
|
||
if (!isset($exactIndex[$key])) {
|
||
$exactIndex[$key] = $fact;
|
||
}
|
||
}
|
||
|
||
// roomAliasMap (oda özel) + aliasMap (genel) birleştir, room öncelikli
|
||
$combinedAlias = array_merge($this->aliasMap, $this->roomAliasMap);
|
||
|
||
$saved = array();
|
||
$skipped = array();
|
||
|
||
foreach ($facilities as $fac) {
|
||
// View tiplerini atla
|
||
$faciltypeId = isset($fac['facilitytype_id']) ? (int)$fac['facilitytype_id'] : 0;
|
||
$altFaciltypeId = isset($fac['alt_facilitytype_id']) ? (int)$fac['alt_facilitytype_id'] : 0;
|
||
if (in_array($faciltypeId, $skipFacilityTypeIds) || in_array($altFaciltypeId, $skipFacilityTypeIds)) {
|
||
continue;
|
||
}
|
||
|
||
$name = trim(isset($fac['name']) ? $fac['name'] : '');
|
||
if ($name === '') {
|
||
continue;
|
||
}
|
||
|
||
$lower = mb_strtolower($name);
|
||
// Alias map'te varsa o karşılığı kullan, yoksa direkt ismi kullan
|
||
$searchKey = isset($combinedAlias[$lower]) ? mb_strtolower($combinedAlias[$lower]) : $lower;
|
||
$fact = null;
|
||
|
||
// Sadece birebir eşleşme — tahmin yok, LIKE yok
|
||
if (isset($exactIndex[$searchKey])) {
|
||
$fact = $exactIndex[$searchKey];
|
||
}
|
||
|
||
if ($fact) {
|
||
// Yalnızca mevcut property_room_fact_mapping tablosuna yaz, yeni kayıt türü oluşturma
|
||
$exists = DB::table('property_room_fact_mapping')
|
||
->where('property_id', $propertyId)
|
||
->where('room_id', $roomId)
|
||
->where('fact_id', $fact->id)
|
||
->exists();
|
||
|
||
if (!$exists) {
|
||
DB::table('property_room_fact_mapping')->insert(array(
|
||
'property_id' => $propertyId,
|
||
'room_id' => $roomId,
|
||
'fact_id' => $fact->id,
|
||
'is_feature' => 0,
|
||
'created_by' => $userId,
|
||
'updated_by' => $userId,
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
}
|
||
|
||
$saved[] = array(
|
||
'booking_name' => $name,
|
||
'fact_id' => $fact->id,
|
||
'fact_name' => $fact->name,
|
||
'via_alias' => ($searchKey !== $lower),
|
||
);
|
||
} else {
|
||
// Eşleşme yok — sisteme kesinlikle bir şey yazma, sadece logla
|
||
Log::warning('propertyInsert.roomFacility.noMatch', array(
|
||
'room_id' => $roomId,
|
||
'property_id' => $propertyId,
|
||
'booking_name' => $name,
|
||
'search_key' => $searchKey,
|
||
'hint' => 'roomAliasMap veya aliasMap\'e eklenebilir',
|
||
));
|
||
$skipped[] = $name;
|
||
}
|
||
}
|
||
|
||
return array(
|
||
'saved_count' => count($saved),
|
||
'skipped_count' => count($skipped),
|
||
'saved' => $saved,
|
||
'skipped' => $skipped,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Oda adındaki anahtar kelimelere göre room_type_id tahmin et
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function guessRoomTypeId($roomName)
|
||
{
|
||
$lower = mb_strtolower($roomName);
|
||
|
||
// Önce çift kelimeli eşleşmeleri dene (daha spesifik)
|
||
foreach ($this->roomTypeKeywords as $keyword => $typeId) {
|
||
if (strpos($keyword, ' ') !== false && strpos($lower, $keyword) !== false) {
|
||
return $typeId;
|
||
}
|
||
}
|
||
// Sonra tek kelimeli
|
||
foreach ($this->roomTypeKeywords as $keyword => $typeId) {
|
||
if (strpos($keyword, ' ') === false && strpos($lower, $keyword) !== false) {
|
||
return $typeId;
|
||
}
|
||
}
|
||
|
||
return 24; // Default: Standard Room
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ANA METOD: POST /bulut/property-insert
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
public function insert(Request $request)
|
||
{
|
||
$hotelId = $request->input('hotel_id', '');
|
||
$email = $request->input('email', '');
|
||
$password = $request->input('password', '');
|
||
$name = $request->input('name', '');
|
||
$surname = $request->input('surname', '');
|
||
$propertyName = $request->input('property_name', '');
|
||
$phone = $request->input('phone', null);
|
||
$dryRun = (bool) $request->input('dry_run', false);
|
||
$checkin = $request->input('checkin', date('Y-m-d', strtotime('+30 days')));
|
||
$checkout = $request->input('checkout', date('Y-m-d', strtotime('+31 days')));
|
||
|
||
// Zorunlu alan validasyonu
|
||
$required = array('hotel_id', 'email', 'password', 'name', 'surname', 'property_name');
|
||
$missing = array();
|
||
foreach ($required as $field) {
|
||
if (!$request->input($field)) {
|
||
$missing[] = $field;
|
||
}
|
||
}
|
||
if (count($missing)) {
|
||
return response()->json(array(
|
||
'status' => false,
|
||
'message' => 'Eksik alanlar: ' . implode(', ', $missing),
|
||
), 422);
|
||
}
|
||
|
||
$report = array(
|
||
'hotel_id' => $hotelId,
|
||
'dry_run' => $dryRun,
|
||
'steps' => array(),
|
||
);
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
// ── ADIM 0: Booking.com'dan veri çek ─────────────────────────────────
|
||
$report['steps']['0_booking_fetch'] = array('status' => 'pending');
|
||
$bookingData = $this->fetchBookingData($hotelId, $checkin, $checkout);
|
||
$detail = $bookingData['detail'];
|
||
$facilData = $bookingData['facilities'];
|
||
$photoData = $bookingData['photos'];
|
||
|
||
$report['steps']['0_booking_fetch'] = array(
|
||
'status' => 'success',
|
||
'hotel_name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null,
|
||
'facility_count' => $this->countFacilities($facilData),
|
||
'photo_count' => count($this->extractPhotoUrls($photoData)),
|
||
);
|
||
|
||
// Eğer property_name boş geldiyse Booking.com adını kullan
|
||
if (!$propertyName && !empty($detail['hotel_name'])) {
|
||
$propertyName = $detail['hotel_name'];
|
||
}
|
||
|
||
if ($dryRun) {
|
||
DB::rollBack();
|
||
$report['dry_run_note'] = 'dry_run=true → DB\'ye hiçbir şey yazılmadı. Aşağıdaki veriler oluşturulacaktı.';
|
||
$report['steps']['1_user_property'] = array('status' => 'skipped (dry_run)', 'email' => $email, 'property_name' => $propertyName);
|
||
$report['steps']['2_property_info'] = array('status' => 'skipped (dry_run)', 'data' => $this->previewPropertyInfo($detail));
|
||
$report['steps']['3_contact'] = array('status' => 'skipped (dry_run)', 'phone' => $phone ?: (isset($detail['phone']) ? $detail['phone'] : null), 'email' => isset($detail['email']) ? $detail['email'] : null);
|
||
$report['steps']['4_fact_mapping'] = array('status' => 'skipped (dry_run)', 'facility_count' => $this->countFacilities($facilData));
|
||
$report['steps']['5_rooms'] = array('status' => 'skipped (dry_run)');
|
||
$report['steps']['6_photos'] = array('status' => 'skipped (dry_run)', 'photo_count' => count($this->extractPhotoUrls($photoData)));
|
||
return response()->json(array('status' => true, 'message' => 'Dry run tamamlandı', 'report' => $report));
|
||
}
|
||
|
||
// ── ADIM 1: Kullanıcı + Property oluştur ────────────────────────────
|
||
$report['steps']['1_user_property'] = array('status' => 'pending');
|
||
$userPropertyResult = $this->createUserAndProperty(
|
||
array('name' => $name, 'surname' => $surname, 'email' => $email, 'password' => $password),
|
||
$propertyName
|
||
);
|
||
$createdUserId = $userPropertyResult['user']['id'];
|
||
$createdPropertyId = $userPropertyResult['property_id'];
|
||
|
||
$report['steps']['1_user_property'] = array(
|
||
'status' => 'success',
|
||
'user_id' => $createdUserId,
|
||
'property_id' => $createdPropertyId,
|
||
'token' => $userPropertyResult['token'],
|
||
);
|
||
|
||
// ── ADIM 2: Property info güncelle ──────────────────────────────────
|
||
$report['steps']['2_property_info'] = array('status' => 'pending');
|
||
$this->updatePropertyInfo($createdPropertyId, $createdUserId, $detail);
|
||
$raw2 = isset($detail['rawData']) ? $detail['rawData'] : array();
|
||
$report['steps']['2_property_info'] = array(
|
||
'status' => 'success',
|
||
'hotel_name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null,
|
||
'rating' => isset($raw2['accuratePropertyClass']) ? (int)$raw2['accuratePropertyClass'] : null,
|
||
'country' => isset($detail['countrycode']) ? strtoupper($detail['countrycode']) : null,
|
||
'checkin' => isset($raw2['checkin']['fromTime']) ? $raw2['checkin']['fromTime'] : null,
|
||
'checkout' => isset($raw2['checkout']['untilTime']) ? $raw2['checkout']['untilTime'] : null,
|
||
'languages' => isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array(),
|
||
);
|
||
|
||
// ── ADIM 3: İletişim bilgilerini kaydet ──────────────────────────────
|
||
$report['steps']['3_contact'] = array('status' => 'pending');
|
||
$this->updatePropertyContact($createdPropertyId, $createdUserId, $detail, $phone);
|
||
$report['steps']['3_contact'] = array(
|
||
'status' => 'success',
|
||
'phone' => $phone ?: (isset($detail['phone']) ? $detail['phone'] : null),
|
||
'email' => isset($detail['email']) ? $detail['email'] : null,
|
||
);
|
||
|
||
// ── ADIM 4: Facility → Fact mapping ─────────────────────────────────
|
||
$report['steps']['4_fact_mapping'] = array('status' => 'pending');
|
||
$facilityResult = $this->mapAndSaveFacilities($createdPropertyId, $createdUserId, $facilData);
|
||
$report['steps']['4_fact_mapping'] = array_merge(array('status' => 'success'), $facilityResult);
|
||
|
||
// ── ADIM 5: Oda ekle ─────────────────────────────────────────────────
|
||
$report['steps']['5_rooms'] = array('status' => 'pending');
|
||
$roomResult = $this->insertRooms($createdPropertyId, $createdUserId, $detail);
|
||
$report['steps']['5_rooms'] = array_merge(array('status' => 'success'), $roomResult);
|
||
|
||
// ── ADIM 6: Görselleri indir ve kaydet ───────────────────────────────
|
||
$report['steps']['6_photos'] = array('status' => 'pending');
|
||
$photoLimit = $request->input('photo_limit', 20);
|
||
$photoResult = $this->processAndSavePhotos($photoData, $hotelId, $createdPropertyId, $photoLimit);
|
||
$report['steps']['6_photos'] = array_merge(array('status' => 'success'), $photoResult);
|
||
|
||
DB::commit();
|
||
|
||
return response()->json(array(
|
||
'status' => true,
|
||
'message' => 'Property başarıyla oluşturuldu',
|
||
'property_id' => $createdPropertyId,
|
||
'user_id' => $createdUserId,
|
||
'report' => $report,
|
||
));
|
||
} catch (ApiErrorException $e) {
|
||
DB::rollBack();
|
||
Log::error('propertyInsert.ApiError: ' . $e->getMessage());
|
||
return response()->json(array(
|
||
'status' => false,
|
||
'message' => implode(', ', $e->getMessageArr()),
|
||
'report' => $report,
|
||
), 400);
|
||
} catch (Exception $e) {
|
||
DB::rollBack();
|
||
Log::error('propertyInsert.Error: ' . $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage());
|
||
return response()->json(array(
|
||
'status' => false,
|
||
'message' => $e->getMessage(),
|
||
'report' => $report,
|
||
), 500);
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// ADIM 6: Fotograflari indir, isle ve kaydet
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function processAndSavePhotos(array $responseData, $hotelId, $propertyId, $limit = 20)
|
||
{
|
||
$photoUrls = $this->extractPhotoUrls($responseData);
|
||
|
||
if (empty($photoUrls)) {
|
||
return array(
|
||
'saved_count' => 0,
|
||
'total_in_api' => 0,
|
||
'message' => 'API yanitinda fotograf URL bulunamadi',
|
||
);
|
||
}
|
||
|
||
$uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/');
|
||
$urlPath = '/property-photos/' . $propertyId . '/';
|
||
$filePath = $uploadRoot . $urlPath;
|
||
|
||
if (!File::exists($filePath)) {
|
||
File::makeDirectory($filePath, 0777, true);
|
||
}
|
||
|
||
$hasDefault = PropertyPhoto::where('property_id', $propertyId)
|
||
->where('status', 1)
|
||
->where('is_default', 1)
|
||
->exists();
|
||
|
||
$saved = array();
|
||
$isFirstPhoto = true;
|
||
$total = min(count($photoUrls), $limit);
|
||
|
||
for ($i = 0; $i < $total; $i++) {
|
||
$photoUrl = $photoUrls[$i];
|
||
try {
|
||
$fileName = time() . '_bk' . $hotelId . '_' . $i;
|
||
$fileExt = 'jpg';
|
||
$tempPath = sys_get_temp_dir() . '/' . $fileName . '_orig.jpg';
|
||
|
||
$this->downloadRawImage($photoUrl, $tempPath);
|
||
|
||
$image = Image::make($tempPath);
|
||
$imageWidth = $image->width();
|
||
$imageHeight = $image->height();
|
||
$fileSize = (int) ceil(filesize($tempPath) / 1024);
|
||
$quality = 80;
|
||
|
||
// Orijinal — 2000px uzeriyse kucult
|
||
if ($imageHeight > 2000 || $imageWidth > 2000) {
|
||
if ($imageHeight > $imageWidth) {
|
||
$r = $imageHeight / 2000;
|
||
$image->fit((int)($imageWidth / $r), 2000);
|
||
} else {
|
||
$r = $imageWidth / 2000;
|
||
$image->fit(2000, (int)($imageHeight / $r));
|
||
}
|
||
}
|
||
$image->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '.' . $fileExt);
|
||
|
||
// _medium (max 1600x768)
|
||
$mediumImage = Image::make($tempPath);
|
||
$ratio = $imageHeight > 0 ? ($imageHeight / 768) : 1;
|
||
$resizeWidth = (int)($imageWidth / max($ratio, 1));
|
||
if ($resizeWidth > 1599) {
|
||
$mediumImage->fit(1600, 768);
|
||
} else {
|
||
$mediumImage->fit(max($resizeWidth, 1), 768);
|
||
}
|
||
$mediumImage->encode($fileExt, $quality)->orientate()->save($filePath . $fileName . '_medium.' . $fileExt);
|
||
|
||
// _thumbnail (300x300)
|
||
Image::make($tempPath)
|
||
->fit(300, 300)
|
||
->encode($fileExt, $quality)
|
||
->orientate()
|
||
->save($filePath . $fileName . '_thumbnail.' . $fileExt);
|
||
|
||
if (File::exists($tempPath)) {
|
||
File::delete($tempPath);
|
||
}
|
||
|
||
$isCompatible = ($imageWidth >= 1150 && $imageHeight >= 600) ? 1 : 0;
|
||
$isDefault = (!$hasDefault && $isFirstPhoto) ? 1 : 0;
|
||
$isFirstPhoto = false;
|
||
|
||
$photoRecord = PropertyPhoto::create(array(
|
||
'property_id' => $propertyId,
|
||
'property_photo_category_id' => 1,
|
||
'photo_path' => $urlPath,
|
||
'photo_name' => $fileName,
|
||
'file_size' => $fileSize,
|
||
'file_ext' => $fileExt,
|
||
'photo_resolution' => $imageWidth . 'x' . $imageHeight,
|
||
'is_default' => $isDefault,
|
||
'is_compatible_with_myweb_slider' => $isCompatible,
|
||
'photo_order' => 0,
|
||
'photo_rank' => 0,
|
||
'is_temp' => 1,
|
||
'status' => 1,
|
||
'created_by' => 1,
|
||
'updated_by' => 1,
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
));
|
||
|
||
$saved[] = array(
|
||
'db_id' => $photoRecord->id,
|
||
'file_name' => $fileName . '.' . $fileExt,
|
||
'resolution' => $imageWidth . 'x' . $imageHeight,
|
||
'source_url' => $photoUrl,
|
||
);
|
||
} catch (Exception $e) {
|
||
Log::error('propertyInsert.photo [' . $i . ']: ' . $e->getMessage() . ' | ' . $photoUrl);
|
||
$saved[] = array('error' => $e->getMessage(), 'source_url' => $photoUrl);
|
||
}
|
||
}
|
||
|
||
$okCount = count(array_filter($saved, function ($s) {
|
||
return !isset($s['error']);
|
||
}));
|
||
|
||
return array(
|
||
'saved_count' => $okCount,
|
||
'total_in_api' => count($photoUrls),
|
||
'limit_applied' => $total,
|
||
'photos' => $saved,
|
||
);
|
||
}
|
||
|
||
private function extractPhotoUrls(array $data)
|
||
{
|
||
$urls = array();
|
||
|
||
// Format 1: data[] dizisi
|
||
if (isset($data['data']) && is_array($data['data']) && !empty($data['data'])) {
|
||
foreach ($data['data'] as $item) {
|
||
if (!is_array($item)) {
|
||
continue;
|
||
}
|
||
if (!empty($item['url_original'])) {
|
||
$urls[] = $item['url_original'];
|
||
} elseif (!empty($item['url_max'])) {
|
||
$urls[] = $item['url_max'];
|
||
} elseif (!empty($item['url'])) {
|
||
$urls[] = $item['url'];
|
||
} elseif (!empty($item['photo']['url_original'])) {
|
||
$urls[] = $item['photo']['url_original'];
|
||
}
|
||
}
|
||
}
|
||
|
||
// Format 2: data.photos[]
|
||
if (empty($urls) && isset($data['data']['photos']) && is_array($data['data']['photos'])) {
|
||
foreach ($data['data']['photos'] as $item) {
|
||
if (!empty($item['url_original'])) {
|
||
$urls[] = $item['url_original'];
|
||
} elseif (!empty($item['url'])) {
|
||
$urls[] = $item['url'];
|
||
}
|
||
}
|
||
}
|
||
|
||
// Format 3: photos[] root'ta
|
||
if (empty($urls) && isset($data['photos']) && is_array($data['photos'])) {
|
||
foreach ($data['photos'] as $item) {
|
||
if (!empty($item['url_original'])) {
|
||
$urls[] = $item['url_original'];
|
||
} elseif (!empty($item['url'])) {
|
||
$urls[] = $item['url'];
|
||
}
|
||
}
|
||
}
|
||
|
||
return $urls;
|
||
}
|
||
|
||
private function downloadRawImage($url, $savePath)
|
||
{
|
||
$client = new Client(array(
|
||
'timeout' => 30,
|
||
'verify' => false,
|
||
'headers' => array('User-Agent' => 'Mozilla/5.0'),
|
||
));
|
||
$response = $client->get($url, array('sink' => $savePath));
|
||
if ($response->getStatusCode() !== 200) {
|
||
throw new Exception('Image download failed HTTP ' . $response->getStatusCode() . ' | ' . $url);
|
||
}
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Yardımcı: timezone string → general_timezone.id
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function resolveTimezoneId($timezoneLocation)
|
||
{
|
||
$row = DB::table('general_timezone')
|
||
->where('location', $timezoneLocation)
|
||
->first();
|
||
return $row ? (string)$row->id : null;
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Yardımcı: dry_run için property_info önizleme
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function previewPropertyInfo(array $detail)
|
||
{
|
||
$raw = isset($detail['rawData']) ? $detail['rawData'] : array();
|
||
$rating = null;
|
||
foreach (array('accuratePropertyClass', 'propertyClass', 'qualityClass') as $rk) {
|
||
if (!empty($raw[$rk])) {
|
||
$rating = (int)$raw[$rk];
|
||
break;
|
||
}
|
||
}
|
||
return array(
|
||
'name' => isset($detail['hotel_name']) ? $detail['hotel_name'] : null,
|
||
'rating' => $rating,
|
||
'country' => isset($detail['countrycode']) ? strtoupper($detail['countrycode']) : null,
|
||
'address' => isset($detail['address']) ? $detail['address'] : null,
|
||
'city' => isset($detail['city_name_en']) ? $detail['city_name_en'] : null,
|
||
'district' => isset($detail['district']) ? $detail['district'] : null,
|
||
'zip' => isset($detail['zip']) ? $detail['zip'] : null,
|
||
'latitude' => isset($detail['latitude']) ? $detail['latitude'] : null,
|
||
'longitude' => isset($detail['longitude']) ? $detail['longitude'] : null,
|
||
'timezone' => isset($detail['timezone']) ? $detail['timezone'] : null,
|
||
'checkin' => isset($raw['checkin']['fromTime']) ? $raw['checkin']['fromTime'] : null,
|
||
'checkout' => isset($raw['checkout']['untilTime']) ? $raw['checkout']['untilTime'] : null,
|
||
'languages' => isset($detail['spoken_languages']) ? $detail['spoken_languages'] : array(),
|
||
'review_score' => isset($raw['reviewScore']) ? $raw['reviewScore'] : null,
|
||
);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Yardımcı: facility sayısını döndür
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
private function countFacilities(array $facilData)
|
||
{
|
||
$count = 0;
|
||
if (isset($facilData['facilities'])) {
|
||
foreach ($facilData['facilities'] as $fac) {
|
||
$count += count(isset($fac['instances']) ? $fac['instances'] : array());
|
||
}
|
||
}
|
||
return $count;
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// GET /bulut/property-check?email=...
|
||
// Verilen email'e ait kullanıcı ve property bilgilerini döndür
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
public function check(Request $request)
|
||
{
|
||
$email = trim($request->input('email', ''));
|
||
if (!$email) {
|
||
return response()->json(array('status' => false, 'message' => 'email parametresi gerekli'), 422);
|
||
}
|
||
|
||
$user = DB::table('user')->where('email', $email)->first();
|
||
if (!$user) {
|
||
return response()->json(array(
|
||
'status' => false,
|
||
'message' => 'Kullanıcı bulunamadı',
|
||
'email' => $email,
|
||
));
|
||
}
|
||
|
||
// Kullanıcıya bağlı property'ler
|
||
$mappings = DB::table('user_property_mapping as upm')
|
||
->join('property as p', 'p.id', '=', 'upm.property_id')
|
||
->where('upm.user_id', $user->id)
|
||
->select(array(
|
||
'p.id as property_id',
|
||
'p.name as property_name',
|
||
'p.status as property_status',
|
||
'upm.created_at',
|
||
))
|
||
->get();
|
||
|
||
$properties = array();
|
||
foreach ($mappings as $m) {
|
||
$roomCount = DB::table('property_room')->where('property_id', $m->property_id)->count();
|
||
$photoCount = DB::table('property_photo')->where('property_id', $m->property_id)->count();
|
||
$factCount = DB::table('property_fact_mapping')->where('property_id', $m->property_id)->count();
|
||
|
||
$properties[] = array(
|
||
'property_id' => $m->property_id,
|
||
'property_name' => $m->property_name,
|
||
'status' => $m->property_status,
|
||
'room_count' => $roomCount,
|
||
'photo_count' => $photoCount,
|
||
'fact_count' => $factCount,
|
||
'created_at' => date('Y-m-d H:i:s', $m->created_at),
|
||
);
|
||
}
|
||
|
||
return response()->json(array(
|
||
'status' => true,
|
||
'user_id' => $user->id,
|
||
'email' => $user->email,
|
||
'name' => $user->name . ' ' . $user->surname,
|
||
'user_status' => $user->status,
|
||
'properties' => $properties,
|
||
));
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// DELETE /bulut/property-delete
|
||
// Body: { "property_id": 123 } veya { "email": "..." } (emailde tüm property'ler)
|
||
// Odalar, fotoğraflar (dosya dahil), fact mapping, iletişim, user-property mapping,
|
||
// property kaydı ve kullanıcı silinir.
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
public function delete(Request $request)
|
||
{
|
||
$propertyId = $request->input('property_id');
|
||
$email = trim($request->input('email', ''));
|
||
|
||
if (!$propertyId && !$email) {
|
||
return response()->json(array('status' => false, 'message' => 'property_id veya email gerekli'), 422);
|
||
}
|
||
|
||
$report = array();
|
||
|
||
try {
|
||
DB::beginTransaction();
|
||
|
||
// Email ile geldiyse önce kullanıcı + property_id'leri bul
|
||
$userIdToDelete = null;
|
||
$propertyIds = array();
|
||
|
||
if ($email) {
|
||
$user = DB::table('user')->where('email', $email)->first();
|
||
if (!$user) {
|
||
return response()->json(array('status' => false, 'message' => 'Kullanıcı bulunamadı'));
|
||
}
|
||
$userIdToDelete = $user->id;
|
||
$pids = DB::table('user_property_mapping')
|
||
->where('user_id', $userIdToDelete)
|
||
->pluck('property_id')
|
||
->toArray();
|
||
$propertyIds = $pids;
|
||
} else {
|
||
$propertyIds = array((int)$propertyId);
|
||
// Bu property'nin tek sahibi varsa onu da sil
|
||
$ownerCount = DB::table('user_property_mapping')
|
||
->where('property_id', $propertyId)
|
||
->count();
|
||
if ($ownerCount === 1) {
|
||
$userIdToDelete = DB::table('user_property_mapping')
|
||
->where('property_id', $propertyId)
|
||
->value('user_id');
|
||
}
|
||
}
|
||
|
||
foreach ($propertyIds as $pid) {
|
||
$deleted = array('property_id' => $pid);
|
||
|
||
// 1. Fotoğraf dosyalarını sil
|
||
$photos = DB::table('property_photo')->where('property_id', $pid)->get();
|
||
$filesDeleted = 0;
|
||
$uploadRoot = rtrim(Config::get('app.fileSystemDriver'), '/');
|
||
foreach ($photos as $photo) {
|
||
$base = $uploadRoot . $photo->photo_path . $photo->photo_name;
|
||
foreach (array('', '_medium', '_thumbnail') as $suffix) {
|
||
$path = $base . $suffix . '.' . $photo->file_ext;
|
||
if (File::exists($path)) {
|
||
File::delete($path);
|
||
$filesDeleted++;
|
||
}
|
||
}
|
||
}
|
||
$deleted['photos_db'] = DB::table('property_photo')->where('property_id', $pid)->delete();
|
||
$deleted['photos_files'] = $filesDeleted;
|
||
|
||
// 2. Oda ilişkileri
|
||
$roomIds = DB::table('property_room')->where('property_id', $pid)->pluck('id')->toArray();
|
||
if (!empty($roomIds)) {
|
||
DB::table('property_room_bed')->whereIn('room_id', $roomIds)->delete();
|
||
DB::table('property_room_view_mapping')->whereIn('room_id', $roomIds)->delete();
|
||
DB::table('property_room_fact_mapping')->whereIn('room_id', $roomIds)->delete();
|
||
DB::table('property_room_photo_mapping')->whereIn('room_id', $roomIds)->delete();
|
||
}
|
||
$deleted['rooms'] = DB::table('property_room')->where('property_id', $pid)->delete();
|
||
|
||
// 3. Fact mapping
|
||
$deleted['fact_mapping'] = DB::table('property_fact_mapping')->where('property_id', $pid)->delete();
|
||
|
||
// 4. İletişim
|
||
$deleted['contact'] = DB::table('property_contact')->where('property_id', $pid)->delete();
|
||
|
||
// 5. Property info tabloları
|
||
DB::table('property_additional_info')->where('property_id', $pid)->delete();
|
||
DB::table('property_language_spoken')->where('property_id', $pid)->delete();
|
||
|
||
// 6. User-property mapping
|
||
$deleted['user_mapping'] = DB::table('user_property_mapping')->where('property_id', $pid)->delete();
|
||
|
||
// 7. Property kaydı
|
||
$deleted['property'] = DB::table('property')->where('id', $pid)->delete();
|
||
|
||
$report[] = $deleted;
|
||
}
|
||
|
||
// 8. Kullanıcıyı sil (başka property'si yoksa)
|
||
if ($userIdToDelete) {
|
||
$remaining = DB::table('user_property_mapping')->where('user_id', $userIdToDelete)->count();
|
||
if ($remaining === 0) {
|
||
DB::table('api_access_token')->where('user_id', $userIdToDelete)->delete();
|
||
DB::table('user')->where('id', $userIdToDelete)->delete();
|
||
$report['user_deleted'] = $userIdToDelete;
|
||
}
|
||
}
|
||
|
||
DB::commit();
|
||
|
||
return response()->json(array(
|
||
'status' => true,
|
||
'message' => 'Silme tamamlandı',
|
||
'report' => $report,
|
||
));
|
||
} catch (Exception $e) {
|
||
DB::rollBack();
|
||
Log::error('propertyDelete.Error: ' . $e->getFile() . ':' . $e->getLine() . ' ' . $e->getMessage());
|
||
return response()->json(array(
|
||
'status' => false,
|
||
'message' => $e->getMessage(),
|
||
'report' => $report,
|
||
), 500);
|
||
}
|
||
}
|
||
}
|