Files
Investidor-V2/app/Services/InvestmentGeography.php
T
nala 67dad461d9
tests / ci (push) Canceled after 0s
provincias
2026-09-29 14:14:06 +02:00

92 lines
3.8 KiB
PHP

<?php
namespace App\Services;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
final class InvestmentGeography
{
public const FIELDS = ['province_id' => 'province', 'district_id' => 'district', 'admin_post_id' => 'admin_post', 'locality_id' => 'locality'];
public function publicReferences(): Builder
{
return DB::table('geo_locations')->whereNull('deleted_at')->where('data_classification', 'public')
->whereRaw("NOT EXISTS (WITH RECURSIVE ancestors AS (
SELECT id,parent_id,deleted_at,data_classification,ARRAY[id] AS visited,false AS cycle FROM geo_locations a WHERE a.id=geo_locations.id
UNION ALL SELECT a.id,a.parent_id,a.deleted_at,a.data_classification,p.visited||a.id,a.id=ANY(p.visited)
FROM geo_locations a JOIN ancestors p ON a.id=p.parent_id WHERE NOT p.cycle
) SELECT 1 FROM ancestors WHERE deleted_at IS NOT NULL OR data_classification<>'public' OR cycle)")
->where(function ($query) {
$query->where(function ($workbook) {
$workbook->where('source_system', 'cartografia-workbook')
->where('external_id', 'like', '964a187ad32ea553b9e112cd159caa90b900050c42b7329594a75bcd798a96ff:%');
})->orWhereIn('id', app(TerritorialRegistry::class)->published()->select('g.id'));
});
}
public function references(bool $management = false): Builder
{
return $management
? DB::table('geo_locations')->whereNull('deleted_at')->whereIn('data_classification', ['public', 'internal'])
: $this->publicReferences();
}
/** @param array<string,mixed> $input
* @return list<\stdClass>
*/
public function selection(array $input, bool $management = false): array
{
$path = [];
$parent = null;
$gap = false;
foreach (self::FIELDS as $field => $type) {
if (empty($input[$field])) {
$gap = true;
continue;
}
$node = $this->references($management)->where('id', $input[$field])->where('location_type', $type)->first(['id', 'name', 'parent_id', 'location_type']);
if ($gap || ! $node || ($parent && $node->parent_id !== $parent)) {
throw ValidationException::withMessages([$field => 'Seleccione a localização dentro da hierarquia indicada.']);
}
$path[] = $node;
$parent = $node->id;
}
return $path;
}
/** @return list<string> */
public function descendants(string $id): array
{
$rows = DB::select('WITH RECURSIVE tree AS (
SELECT id, ARRAY[id] AS visited FROM geo_locations WHERE id=? AND deleted_at IS NULL
UNION ALL SELECT g.id, tree.visited||g.id FROM geo_locations g JOIN tree ON g.parent_id=tree.id
WHERE g.deleted_at IS NULL AND NOT g.id=ANY(tree.visited) AND cardinality(tree.visited)<5
) SELECT id FROM tree', [$id]);
return array_values($this->publicReferences()->whereIn('id', array_map(fn ($row) => $row->id, $rows))->pluck('id')->map(fn ($id) => (string) $id)->all());
}
/** @return list<\stdClass> */
public function path(?string $id, bool $management = false): array
{
$path = [];
for ($i = 0; $id && $i < 5; $i++) {
$query = $management ? DB::table('geo_locations') : $this->publicReferences();
$node = $query->where('id', $id)->first(['id', 'name', 'parent_id', 'location_type', 'deleted_at']);
if (! $node) {
return [];
}
if ($node->location_type !== 'country') {
array_unshift($path, $node);
}
$id = $node->parent_id;
}
return $path;
}
}