126 lines
7.0 KiB
PHP
126 lines
7.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\ExistingBusiness;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Schema;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
/** Optional supplement of the approved listing; never grants publication authority. */
|
|
final class BusinessPointPublication
|
|
{
|
|
public function available(): bool
|
|
{
|
|
return Schema::hasColumn('existing_business_locations', 'public_source_reference');
|
|
}
|
|
|
|
/** @param array<string,mixed> $payload */
|
|
public function applicable(array $payload): bool
|
|
{
|
|
return ($payload['location_point_enabled'] ?? null) === 'yes'
|
|
&& in_array($payload['geography_scope'] ?? null, ['specific', 'multiple'], true)
|
|
&& ($payload['disclosure_precision'] ?? null) === 'exact'
|
|
&& ($payload['location_unknown'] ?? null) === 'no';
|
|
}
|
|
|
|
/** @param array<string,mixed> $payload
|
|
* @return list<array<string,mixed>> */
|
|
public function paths(array $payload): array
|
|
{
|
|
if (($payload['geography_scope'] ?? null) === 'specific') {
|
|
return [array_intersect_key($payload, InvestmentGeography::FIELDS)];
|
|
}
|
|
if (($payload['geography_scope'] ?? null) !== 'multiple') {
|
|
return [];
|
|
}
|
|
$rows = json_decode((string) ($payload['locations_json'] ?? ''), true);
|
|
|
|
return is_array($rows) && array_is_list($rows) ? array_values(array_filter($rows, 'is_array')) : [];
|
|
}
|
|
|
|
/** @param array<string,mixed> $path */
|
|
public function pathKey(array $path): string
|
|
{
|
|
return implode('|', array_map(fn ($field) => (string) ($path[$field] ?? ''), array_keys(InvestmentGeography::FIELDS)));
|
|
}
|
|
|
|
/** @param array<string,mixed> $payload
|
|
* @return array<string,mixed>|null */
|
|
public function candidate(array $payload): ?array
|
|
{
|
|
if (! $this->applicable($payload)) {
|
|
return null;
|
|
}
|
|
abort_unless($this->available(), 409, 'O suplemento de localização ainda não está instalado neste destino. O ponto não será publicado.');
|
|
$rules = [
|
|
'location_unknown' => ['required', 'in:no'],
|
|
'location_latitude' => ['required', 'numeric', 'between:-90,90'],
|
|
'location_longitude' => ['required', 'numeric', 'between:-180,180'],
|
|
'location_point_source_mode' => ['required', 'in:listing,custom'],
|
|
'location_point_share_source' => ['nullable', 'in:yes,no'],
|
|
'location_point_source_reference' => ['required_if:location_point_source_mode,custom', 'nullable', 'string', 'max:2000'],
|
|
'location_point_source_date' => ($payload['location_point_source_mode'] ?? null) === 'custom' ? ['nullable', 'date_format:Y-m-d'] : ['nullable'],
|
|
'location_point_territory' => ['required_if:geography_scope,multiple', 'nullable', 'string', 'max:150'],
|
|
];
|
|
Validator::make($payload, $rules)->validate();
|
|
$paths = $this->paths($payload);
|
|
$path = ($payload['geography_scope'] ?? null) === 'specific' ? ($paths[0] ?? null)
|
|
: collect($paths)->first(fn ($row) => $this->pathKey($row) === $payload['location_point_territory']);
|
|
if (! $path || empty($path['province_id'])) {
|
|
throw ValidationException::withMessages(['location_point_territory' => 'Seleccione uma das localizações já declaradas para o ponto primário. Não é atribuída uma localização por ordem ou por nome.']);
|
|
}
|
|
$nodes = app(InvestmentGeography::class)->selection($path);
|
|
$listingSource = $payload['location_point_source_mode'] === 'listing';
|
|
$source = $listingSource ? ($payload['source_reference'] ?? null) : ($payload['location_point_source_reference'] ?? null);
|
|
$date = $listingSource ? ($payload['source_date'] ?? null) : ($payload['location_point_source_date'] ?? null);
|
|
Validator::make(['location_point_source_reference' => $source, 'location_point_source_date' => $date], [
|
|
'location_point_source_reference' => ['required', 'string', 'max:2000'], 'location_point_source_date' => ['nullable', 'date_format:Y-m-d'],
|
|
])->validate();
|
|
|
|
return ['path' => array_combine(array_keys(InvestmentGeography::FIELDS), array_map(fn ($field) => ($path[$field] ?? null) ?: null, array_keys(InvestmentGeography::FIELDS))),
|
|
'reference_snapshot' => array_map(fn ($node) => (array) $node, $nodes),
|
|
'latitude' => (float) $payload['location_latitude'], 'longitude' => (float) $payload['location_longitude'],
|
|
'source_reference' => $source, 'source_date' => $date ?: null,
|
|
'public_source_reference' => ($payload['location_point_share_source'] ?? null) === 'yes' ? $source : null];
|
|
}
|
|
|
|
/** Called only within the existing authorized, locked publication transaction.
|
|
* @return array<string,mixed> */
|
|
public function publish(ExistingBusiness $business): array
|
|
{
|
|
$point = $this->candidate($business->payload);
|
|
if ($point === null) {
|
|
return [];
|
|
}
|
|
$previous = DB::table('existing_business_locations')->where('existing_business_id', $business->id)->where('is_primary', true)
|
|
->select('*')->selectRaw('ST_AsEWKT(geom) AS geometry_snapshot')->first();
|
|
$geometry = DB::selectOne('SELECT ST_AsEWKT(ST_SetSRID(ST_MakePoint(?::float8, ?::float8),4326)) AS value', [$point['longitude'], $point['latitude']])->value;
|
|
$values = [...$point['path'], 'precision_level' => 'exact', 'is_public' => true,
|
|
'geom' => $geometry,
|
|
'public_source_reference' => $point['public_source_reference'], 'source_date' => $point['source_date'],
|
|
'source_system' => 'portal-business-form', 'source_payload_version' => $business->version_no + 1, 'updated_at' => now()];
|
|
if ($previous) {
|
|
DB::table('existing_business_locations')->where('id', $previous->id)->update($values);
|
|
} else {
|
|
DB::table('existing_business_locations')->insert([...$values, 'id' => (string) Str::uuid(), 'existing_business_id' => $business->id, 'is_primary' => true, 'created_at' => now()]);
|
|
}
|
|
|
|
return ['point_publication' => ['previous' => $previous ? (array) $previous : null, 'used' => $point,
|
|
'business_version' => $business->version_no + 1, 'source' => 'explicit_listing_point_supplement']];
|
|
}
|
|
|
|
/** Private view, only after the listing's normal view authorization.
|
|
* @return array<string,mixed>|null */
|
|
public function registered(ExistingBusiness $business): ?array
|
|
{
|
|
$row = DB::table('existing_business_locations')->where('existing_business_id', $business->id)->where('is_primary', true)
|
|
->select('id', 'precision_level', 'is_public', 'province_id', 'district_id', 'admin_post_id', 'locality_id')
|
|
->selectRaw("geom IS NOT NULL AS has_geometry, CASE WHEN GeometryType(geom)='POINT' THEN ST_Y(geom) END AS latitude, CASE WHEN GeometryType(geom)='POINT' THEN ST_X(geom) END AS longitude")->first();
|
|
|
|
return $row ? (array) $row : null;
|
|
}
|
|
}
|