id)->first(); $blockedProviderIds = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id); $providersWithWorkingDays = ScheduleBusinessRules::getProviderIdsWithWorkingDays(); $clientPrimaryAddress = Address::where('source', 'client') ->where('source_id', $cliente->id) ->orderBy('is_primary', 'desc') ->first(); $distanceSelect = $this->distanceSelect( $clientPrimaryAddress?->latitude !== null ? (float) $clientPrimaryAddress->latitude : null, $clientPrimaryAddress?->longitude !== null ? (float) $clientPrimaryAddress->longitude : null, ); $baseQuery = Provider::leftJoin('users as provider_user', 'provider_user.id', '=', 'providers.user_id') ->leftJoin(DB::raw(" ( SELECT DISTINCT ON (source_id) * FROM addresses WHERE source = 'provider' AND deleted_at IS NULL ORDER BY source_id, is_primary DESC ) as provider_address "), 'provider_address.source_id', '=', 'providers.id') ->whereNotNull('provider_address.id') ->whereNotIn('providers.id', $blockedProviderIds) ->whereIn('providers.id', $providersWithWorkingDays) ->whereNull('providers.deleted_at') ->whereNotNull('providers.daily_price_8h') ->whereNotNull('providers.daily_price_6h') ->whereNotNull('providers.daily_price_4h') ->whereNotNull('providers.daily_price_2h') ->when($name, fn ($q) => $q->where('provider_user.name', 'ILIKE', "%{$name}%")) ->select( 'providers.id as provider_id', 'provider_user.name as provider_name', 'provider_address.district', 'provider_address.latitude as provider_latitude', 'provider_address.longitude as provider_longitude', 'providers.average_rating', 'providers.total_services', 'providers.daily_price_8h', 'providers.daily_price_6h', 'providers.daily_price_4h', 'providers.daily_price_2h', 'providers.created_at', DB::raw("( SELECT COUNT(*) FROM reviews LEFT JOIN schedules ON schedules.id = reviews.schedule_id WHERE reviews.origin = 'provider' AND schedules.provider_id = providers.id ) as total_reviews"), $distanceSelect, ) ->orderBy('providers.average_rating', 'desc'); $providers = (clone $baseQuery) ->when( $clientPrimaryAddress?->city_id, fn ($query, int $cityId) => $query->where('provider_address.city_id', $cityId) ) ->get(); if ($providers->isEmpty() && $clientPrimaryAddress?->city_id) { $providers = $baseQuery->get(); } return $providers ->when( $date, fn ($collection) => $collection->whereIn( 'provider_id', ScheduleBusinessRules::getAvailableProviderIdsForDate( $date, $collection->pluck('provider_id') )->toArray() )->values() ) ->toArray(); } private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression { if ($clientLatitude === null || $clientLongitude === null) { return DB::raw('NULL as distance_km'); } return DB::raw(" CASE WHEN provider_address.latitude IS NOT NULL AND provider_address.longitude IS NOT NULL THEN ROUND(( 6371 * acos( least(1, greatest(-1, cos(radians({$clientLatitude})) * cos(radians(provider_address.latitude)) * cos(radians(provider_address.longitude) - radians({$clientLongitude})) + sin(radians({$clientLatitude})) * sin(radians(provider_address.latitude)) )) ) )::numeric, 1) ELSE NULL END as distance_km "); } }