54 lines
3.4 KiB
PHP
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.'];
|
|
}
|
|
}
|