id)->first(); $blockedProviderIds = ScheduleBusinessRules::getBlockedProviderIdsForClient($cliente->id); $providersWithWorkingDays = ScheduleBusinessRules::getProviderIdsWithWorkingDays(); $clientPrimaryAddress = Address::where('source', 'client') ->where('source_id', $cliente->id) ->orderByDesc('is_primary') ->orderByDesc('id') ->first(); $clientDistanceAddress = $this->addressForDistance($cliente->id, $clientPrimaryAddress); $clientCoordinates = $this->zipCodeCoordinatesService->resolve( $clientDistanceAddress?->latitude !== null ? (float) $clientDistanceAddress->latitude : null, $clientDistanceAddress?->longitude !== null ? (float) $clientDistanceAddress->longitude : null, $clientDistanceAddress?->zip_code, ); $distanceSelect = $this->distanceSelect( data_get($clientCoordinates, 'latitude'), data_get($clientCoordinates, 'longitude'), ); $baseQuery = Provider::leftJoin( 'users as provider_user', 'provider_user.id', '=', 'providers.user_id' ) ->visibleToCustomers() ->leftJoin( DB::raw(" ( SELECT DISTINCT ON (source_id) * FROM addresses WHERE source = 'provider' AND deleted_at IS NULL ORDER BY source_id, (latitude IS NOT NULL AND longitude IS NOT NULL) DESC, is_primary DESC, id 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', 'providers.id as provider_id', 'providers.profile_media_id', 'provider_user.name as provider_name', 'providers.gender', 'provider_address.zip_code as provider_zip_code', '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, ) ->orderByRaw('distance_km ASC NULLS LAST'); $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(); } $this->zipCodeCoordinatesService->preload( $providers->whereNull('distance_km')->pluck('provider_zip_code') ); $providers->each(function ($provider) use ($clientDistanceAddress) { if ($provider->distance_km !== null) { return; } $provider->distance_km = $this->zipCodeCoordinatesService->calculateDistance( $clientDistanceAddress?->latitude !== null ? (float) $clientDistanceAddress->latitude : null, $clientDistanceAddress?->longitude !== null ? (float) $clientDistanceAddress->longitude : null, $clientDistanceAddress?->zip_code, $provider->provider_latitude !== null ? (float) $provider->provider_latitude : null, $provider->provider_longitude !== null ? (float) $provider->provider_longitude : null, $provider->provider_zip_code, ); }); $providers = $providers ->sortBy(fn ($provider) => $provider->distance_km ?? PHP_FLOAT_MAX) ->values(); $filtered = $providers->when( $date, fn ($collection) => $collection->whereIn( 'provider_id', ScheduleBusinessRules::getAvailableProviderIdsForDate( $date, $collection->pluck('provider_id') )->toArray() )->values() ); $filtered->load('profileMedia'); return $filtered->map(function ($item) { $arr = is_array($item) ? $item : $item->toArray(); $arr['profile_media_url'] = $item->profileMedia?->path ? Storage::temporaryUrl($item->profileMedia->path, now()->addMinutes(60)) : null; $arr['gender_label'] = GenderEnum::labelFor(data_get($arr, 'gender')); $arr['daily_price_8h_base'] = data_get($arr, 'daily_price_8h'); $arr['daily_price_6h_base'] = data_get($arr, 'daily_price_6h'); $arr['daily_price_4h_base'] = data_get($arr, 'daily_price_4h'); $arr['daily_price_2h_base'] = data_get($arr, 'daily_price_2h'); $arr['daily_price_8h'] = $this->applyCreditCardFee(data_get($arr, 'daily_price_8h')); $arr['daily_price_6h'] = $this->applyCreditCardFee(data_get($arr, 'daily_price_6h')); $arr['daily_price_4h'] = $this->applyCreditCardFee(data_get($arr, 'daily_price_4h')); $arr['daily_price_2h'] = $this->applyCreditCardFee(data_get($arr, 'daily_price_2h')); unset( $arr['id'], $arr['profile_media_id'], $arr['profile_media'], $arr['provider_zip_code'], ); return $arr; })->values()->toArray(); } // private function applyCreditCardFee(?float $price): ?float { if ($price === null) { return null; } $rate = $this->platformCreditCardFeeRate(); return round($price * (1 + $rate), 2); } private function platformCreditCardFeeRate(): float { $rate = config('services.pagarme.platform_credit_card_fee_rate', 0.16); if (is_string($rate)) { $rate = str_replace(',', '.', trim($rate)); } $rate = (float) $rate; return $rate > 1 ? $rate / 100 : $rate; } private function addressForDistance(int $clientId, ?Address $primaryAddress): ?Address { if ($primaryAddress?->latitude !== null && $primaryAddress?->longitude !== null) { return $primaryAddress; } return Address::where('source', 'client') ->where('source_id', $clientId) ->whereNotNull('latitude') ->whereNotNull('longitude') ->orderByDesc('is_primary') ->orderByDesc('id') ->first() ?? $primaryAddress; } private function distanceSelect(?float $clientLatitude, ?float $clientLongitude): \Illuminate\Contracts\Database\Query\Expression { return DistanceService::sqlExpression($clientLatitude, $clientLongitude); } }