Files
Investidor-V2/app/Services/BusinessProximitySearch.php
T

54 lines
3.4 KiB
PHP

<?php
namespace App\Services;
use App\Models\ExistingBusiness;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class BusinessProximitySearch
{
/** @return array<string,mixed> */
public function origin(string $id): array
{
$row = DB::query()->fromSub(app(PublicBusinessLocations::class)->points(), 'origin_point')
->joinSub(ExistingBusiness::published()->select('id', 'public_title'), 'origin_business', 'origin_business.id', '=', 'origin_point.existing_business_id')
->where('origin_point.existing_business_id', $id)->whereNotNull('origin_point.point')
->select('origin_point.location_recorded_at', 'origin_point.location_source_reference', 'origin_point.location_source_date', 'origin_business.public_title')->selectRaw('ST_AsEWKT(origin_point.point) AS ewkt')->first();
if (! $row) {
throw ValidationException::withMessages(['near_business' => 'A ficha de referência não tem um ponto primário público exacto utilizável. Escolha outra referência ou pesquise por área.']);
}
return ['id' => $id, 'title' => $row->public_title, 'ewkt' => $row->ewkt, 'source_reference' => $row->location_source_reference, 'source_date' => $row->location_source_date, 'location_recorded_at' => $row->location_recorded_at ? CarbonImmutable::parse($row->location_recorded_at)->toIso8601String() : null];
}
public function available(string $id): bool
{
return DB::query()->fromSub(app(PublicBusinessLocations::class)->points(), 'point')
->whereIn('existing_business_id', ExistingBusiness::published()->select('id'))
->where('existing_business_id', $id)->whereNotNull('point')->exists();
}
/** @param Builder<ExistingBusiness> $query
* @param array<string,mixed> $origin
* @return array<string,mixed> */
public function constrain(Builder $query, array $origin, float $radius): array
{
$query->leftJoinSub(app(PublicBusinessLocations::class)->points(), 'public_point', 'public_point.existing_business_id', '=', 'existing_businesses.id');
$withPoint = (clone $query)->whereNotNull('public_point.point')->count();
$withoutPoint = (clone $query)->whereNull('public_point.point')->count();
$query->whereNotNull('public_point.point')->whereRaw('ST_DWithin(public_point.point::geography, ST_GeomFromEWKT(?)::geography, ?)', [$origin['ewkt'], $radius * 1000])
->addSelect('public_point.location_recorded_at')
->selectRaw('ST_Distance(public_point.point::geography, ST_GeomFromEWKT(?)::geography) / 1000.0 AS distance_km', [$origin['ewkt']])
->orderBy('distance_km')->orderBy('existing_businesses.id');
return ['origin_id' => $origin['id'], 'origin_title' => $origin['title'], 'radius_km' => $radius, 'origin_recorded_at' => $origin['location_recorded_at'],
'origin_source_reference' => $origin['source_reference'], 'origin_source_date' => $origin['source_date'],
'with_public_point' => $withPoint, 'without_public_point' => $withoutPoint,
'source' => 'Localizações primárias públicas do cadastro de negócios do portal',
'method' => 'Distância geodésica em linha recta entre pontos publicados; não é distância por estrada nem validação da localização real.'];
}
}