File manager - Edit - /var/www/html/portalHomolog/app/Http/Resources/Importacoes/MapeamentoUnidadeResource.php
Back
<?php namespace App\Http\Resources\Importacoes; use App\Models\Structure; use Illuminate\Console\Command; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Storage; /** * Responsavel pelo DE-PARA entre as unidades textuais vindas do CSV de * importacao de servidores municipais (Prodata) e as `Structures` cadastradas * no banco. A classe centraliza: * * - normalizacao dos nomes (caixa alta, remocao de acentos, colapsar espacos); * - match automatico contra `structures.name`/`abbreviation`; * - persistencia/recuperacao do mapeamento manual em arquivo JSON; * - prompt CLI interativo quando uma unidade desconhecida aparece, listando * todas as Structures disponiveis para escolha do operador. * * O mapeamento é carregado em camadas: * * 1. `database/data/importacao-prodata-contaangra/de-para-unidades-predefinido.json` * (versionado — texto CSV exato em `mapa` → `structure_id`, com `detalhes` * opcionais só para conferência humana); * 2. `storage/app/importacao-prodata-contaangra/de-para-unidades.json` * (sobrescreve/amplia o predefinido — ex.: respostas de CLI anteriores); * 3. Match automático contra `structures` no banco; * 4. Prompt CLI, quando fornecido. * * Para regenerar o predefinido a partir do CSV e do banco: * `php database/scripts/gerar-de-para-prodata-conta-angra.php` */ class MapeamentoUnidadeResource { public const STORAGE_DISK = 'local'; public const STORAGE_PATH = 'importacao-prodata-contaangra/de-para-unidades.json'; /** * Arquivo versionado com DE-PARA extraído do CSV + banco (revisão humana). * Chaves em `mapa` devem ser o texto **exato** da coluna UNIDADE da planilha. * * @var string Caminho relativo a `database/` */ public const PREDEFINIDO_DATABASE_RELATIVE = 'data/importacao-prodata-contaangra/de-para-unidades-predefinido.json'; /** @var array<string,string> Cache normalized unidade => structure_id */ protected array $mapa = []; /** @var Collection<int,Structure>|null Lista de Structures ativas (lazy) */ protected ?Collection $structures = null; /** * Carrega o predefinido versionado e depois o JSON em `storage/app/`. */ public function carregar(): self { $this->mapa = []; $this->mesclarMapaDeArquivoJson($this->caminhoPredefinido()); $this->mesclarMapaDeStorage(); return $this; } /** * Caminho absoluto do JSON pré-definido em `database/data/...`. */ public function caminhoPredefinido(): string { return database_path(self::PREDEFINIDO_DATABASE_RELATIVE); } /** * Lê um arquivo JSON com seção `mapa`: texto CSV => structure_id (string) ou null. * Entradas nulas ou vazias são ignoradas. */ protected function mesclarMapaDeArquivoJson(string $path): void { if (!is_file($path)) { return; } $decodificado = json_decode((string) file_get_contents($path), true); if (!is_array($decodificado) || !isset($decodificado['mapa']) || !is_array($decodificado['mapa'])) { return; } foreach ($decodificado['mapa'] as $textoCsv => $structureId) { if (!is_string($textoCsv) || trim($textoCsv) === '') { continue; } if (!is_string($structureId) || $structureId === '') { continue; } $this->mapa[$this->normalizar($textoCsv)] = $structureId; } } protected function mesclarMapaDeStorage(): void { if (!Storage::disk(self::STORAGE_DISK)->exists(self::STORAGE_PATH)) { return; } $conteudo = Storage::disk(self::STORAGE_DISK)->get(self::STORAGE_PATH); $decodificado = json_decode((string) $conteudo, true); if (!is_array($decodificado)) { return; } foreach ($decodificado as $unidade => $structureId) { if (is_string($unidade) && is_string($structureId) && $structureId !== '') { $this->mapa[$this->normalizar($unidade)] = $structureId; } } } /** * Persiste o estado atual do mapeamento em disco. */ public function salvar(): void { Storage::disk(self::STORAGE_DISK)->put( self::STORAGE_PATH, json_encode($this->mapa, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) ); } /** * Retorna o `structure_id` correspondente a uma unidade do CSV. * * Estrategia: * 1. Consulta o cache normalizado; * 2. Tenta match automatico contra `structures.name`; * 3. Se receber um `$command`, abre prompt interativo; * 4. Caso contrario, retorna `null` (chamador decide o que fazer). */ public function resolver(?string $unidade, ?Command $command = null): ?string { $unidade = trim((string) $unidade); if ($unidade === '') { return null; } $chave = $this->normalizar($unidade); if (isset($this->mapa[$chave])) { return $this->mapa[$chave]; } $autoMatch = $this->matchAutomatico($unidade); if ($autoMatch !== null) { $this->mapa[$chave] = $autoMatch; return $autoMatch; } if ($command === null) { return null; } $escolhido = $this->perguntarInterativo($unidade, $command); if ($escolhido !== null) { $this->mapa[$chave] = $escolhido; $this->salvar(); } return $escolhido; } /** * Indica se a unidade ja esta resolvida no cache em memoria. */ public function temMapeamento(?string $unidade): bool { $unidade = trim((string) $unidade); if ($unidade === '') { return false; } return isset($this->mapa[$this->normalizar($unidade)]); } /** * Retorna o mapa completo (chave normalizada => structure_id). * * @return array<string,string> */ public function todos(): array { return $this->mapa; } /** * Pre-resolve todas as unidades distintas conhecidas, abrindo o prompt CLI * somente para aquelas sem match automatico ou cache previo. * * @param iterable<string> $unidades */ public function prepararParaUnidades(iterable $unidades, ?Command $command = null): void { foreach ($unidades as $unidade) { $this->resolver($unidade, $command); } } /** * Normaliza o texto da unidade para comparacao: caixa alta, sem acentos, * sem caracteres especiais e com espacos colapsados. */ public function normalizar(string $valor): string { $valor = trim($valor); if ($valor === '') { return ''; } if (class_exists(\Transliterator::class)) { $tr = \Transliterator::create('Any-Latin; Latin-ASCII; Upper()'); if ($tr instanceof \Transliterator) { $valor = $tr->transliterate($valor) ?: $valor; } } else { $valor = @iconv('UTF-8', 'ASCII//TRANSLIT', $valor) ?: $valor; $valor = mb_strtoupper($valor); } $valor = preg_replace('/[^A-Z0-9 ]+/u', ' ', $valor); $valor = preg_replace('/\s+/u', ' ', (string) $valor); return trim((string) $valor); } /** * Tenta encontrar uma Structure por nome ou abreviacao normalizados. */ protected function matchAutomatico(string $unidade): ?string { $normalUnidade = $this->normalizar($unidade); if ($normalUnidade === '') { return null; } foreach ($this->getStructures() as $structure) { if ($this->normalizar((string) $structure->name) === $normalUnidade) { return (string) $structure->id; } $abrev = trim((string) $structure->abbreviation); if ($abrev !== '' && $this->normalizar($abrev) === $normalUnidade) { return (string) $structure->id; } } return null; } /** * Apresenta a lista completa de Structures e pergunta ao operador qual deve * ser usada para a unidade `$unidade`. */ protected function perguntarInterativo(string $unidade, Command $command): ?string { $structures = $this->getStructures(); if ($structures->isEmpty()) { $command->error(__('Nao ha Structures cadastradas no banco. Cadastre as secretarias antes de importar.')); return null; } $command->newLine(); $command->warn(__('DE-PARA necessario para a unidade do CSV:')); $command->line(" \"{$unidade}\""); $command->newLine(); $command->line(__('Selecione a Structure correspondente (digite o numero ou parte do nome):')); $opcoes = []; $defaultIndice = null; $idDefault = env('STRUCTURE_DEFAULT_ID'); foreach ($structures as $structure) { $abrev = trim((string) $structure->abbreviation); $rotulo = $abrev !== '' ? sprintf('[%s] %s', $abrev, $structure->name) : (string) $structure->name; $opcoes[(string) $structure->id] = $rotulo; if ($idDefault !== null && (string) $structure->id === (string) $idDefault) { $defaultIndice = $rotulo; } } $opcoes['__pular__'] = __('-- Pular esta unidade (registros serao marcados como invalidos)'); $escolha = $command->choice( __('Structure para ":unidade"', ['unidade' => $unidade]), $opcoes, $defaultIndice ); if ($escolha === $opcoes['__pular__']) { return null; } foreach ($opcoes as $structureId => $rotulo) { if ($rotulo === $escolha) { return $structureId === '__pular__' ? null : $structureId; } } return null; } /** * Carrega (lazy) todas as Structures ativas para usar no match automatico * e no prompt interativo. * * @return Collection<int,Structure> */ protected function getStructures(): Collection { if ($this->structures === null) { $this->structures = Structure::query() ->select(['id', 'name', 'abbreviation']) ->where('status', 'ATIVADO') ->orderBy('name') ->get(); } return $this->structures; } }
| ver. 1.4 |
Github
|
.
| PHP 8.3.28 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings