} */ public function importFromUploadedXsd( string $tempPath, ?string $versionLabel, ?string $note, ?User $createdBy ): array { $content = Storage::disk('local')->get($tempPath); $sha256 = hash('sha256', $content); $existing = FeXsdVersion::query()->where('sha256', $sha256)->first(); if ($existing) { // File giĆ  importato: pulisco il temporaneo e ritorno la versione esistente. try { Storage::disk('local')->delete($tempPath); } catch (\Throwable) { // ignore } return [ 'version' => $existing, 'summary' => [ 'already_imported' => true, 'sha256' => $sha256, ], ]; } $originalFilename = basename($tempPath); $finalPath = 'fe/xsd/' . $sha256 . '.xsd'; return DB::transaction(function () use ($content, $sha256, $tempPath, $finalPath, $originalFilename, $versionLabel, $note, $createdBy): array { // Sposto/normalizzo il path del file appena possibile (se fallisce, andiamo avanti). try { if (!Storage::disk('local')->exists($finalPath)) { Storage::disk('local')->move($tempPath, $finalPath); } else { Storage::disk('local')->delete($tempPath); } } catch (\Throwable) { // ignore } $parsed = $this->parseXsdEnumerations($content); $version = FeXsdVersion::query()->create([ 'version_label' => $versionLabel ? trim($versionLabel) : null, 'original_filename' => $originalFilename, 'stored_path' => $finalPath, 'sha256' => $sha256, 'created_by_user_id' => $createdBy?->id, 'imported_at' => now(), 'note' => $note ? trim($note) : null, 'meta' => [ 'domains' => count($parsed['domains']), 'codes' => $parsed['codes_count'], ], ]); foreach ($parsed['domains'] as $domain) { $domainRow = FeXsdCodeDomain::query()->create([ 'version_id' => $version->id, 'type_name' => (string) $domain['type_name'], 'base_type' => Arr::get($domain, 'base_type'), 'documentation' => Arr::get($domain, 'documentation'), 'codes_count' => count($domain['codes'] ?? []), ]); $i = 0; foreach (($domain['codes'] ?? []) as $codeRow) { $i++; FeXsdCode::query()->create([ 'domain_id' => $domainRow->id, 'code' => (string) ($codeRow['code'] ?? ''), 'label' => Arr::get($codeRow, 'label'), 'documentation' => Arr::get($codeRow, 'documentation'), 'sort_order' => $i, ]); } } return [ 'version' => $version, 'summary' => [ 'already_imported' => false, 'sha256' => $sha256, 'domains' => count($parsed['domains']), 'codes' => $parsed['codes_count'], ], ]; }); } /** * @return array{domains: array}>, codes_count:int} */ private function parseXsdEnumerations(string $content): array { $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; $doc->formatOutput = false; $ok = @$doc->loadXML($content, LIBXML_NONET | LIBXML_NOERROR | LIBXML_NOWARNING); if (!$ok) { throw new \RuntimeException('XSD non valido o non leggibile.'); } $xpath = new DOMXPath($doc); // Trova simpleType con name e con almeno una enumeration. $simpleTypes = $xpath->query("//*[local-name()='simpleType'][@name][.//*[local-name()='enumeration']]"); $domains = []; $codesCount = 0; if ($simpleTypes) { foreach ($simpleTypes as $simpleTypeNode) { $typeName = (string) $simpleTypeNode->attributes?->getNamedItem('name')?->nodeValue; $typeName = trim($typeName); if ($typeName === '') { continue; } // baseType dal restriction base="..." $baseTypeNode = $xpath->query(".//*[local-name()='restriction'][@base]", $simpleTypeNode)?->item(0); $baseType = $baseTypeNode?->attributes?->getNamedItem('base')?->nodeValue; $baseType = is_string($baseType) ? trim($baseType) : null; $docTextNode = $xpath->query(".//*[local-name()='annotation']/*[local-name()='documentation']", $simpleTypeNode)?->item(0); $documentation = $docTextNode ? trim((string) $docTextNode->textContent) : null; $documentation = $documentation !== '' ? $documentation : null; $enumNodes = $xpath->query(".//*[local-name()='restriction']/*[local-name()='enumeration'][@value]", $simpleTypeNode); if (!$enumNodes || $enumNodes->length === 0) { continue; } $codes = []; foreach ($enumNodes as $enumNode) { $value = (string) $enumNode->attributes?->getNamedItem('value')?->nodeValue; $value = trim($value); if ($value === '') { continue; } $enumDocNode = $xpath->query(".//*[local-name()='annotation']/*[local-name()='documentation']", $enumNode)?->item(0); $enumDoc = $enumDocNode ? trim((string) $enumDocNode->textContent) : null; $enumDoc = $enumDoc !== '' ? $enumDoc : null; $codes[] = [ 'code' => $value, 'label' => null, 'documentation' => $enumDoc, ]; $codesCount++; } $domains[] = [ 'type_name' => $typeName, 'base_type' => $baseType, 'documentation' => $documentation, 'codes' => $codes, ]; } } // Ordina stabilmente per nome per evitare fluttuazioni tra import. usort($domains, fn(array $a, array $b) => strcmp((string) $a['type_name'], (string) $b['type_name'])); return [ 'domains' => $domains, 'codes_count' => $codesCount, ]; } }