197 lines
7.4 KiB
PHP
197 lines
7.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\FattureElettroniche;
|
|
|
|
use App\Models\FeXsdCode;
|
|
use App\Models\FeXsdCodeDomain;
|
|
use App\Models\FeXsdVersion;
|
|
use App\Models\User;
|
|
use DOMDocument;
|
|
use DOMXPath;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class FeXsdCodesImporter
|
|
{
|
|
/**
|
|
* Importa tutte le enumerazioni (simpleType/restriction/enumeration) da un file XSD.
|
|
*
|
|
* @return array{version: FeXsdVersion, summary: array<string,mixed>}
|
|
*/
|
|
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<int,array{type_name:string,base_type:?string,documentation:?string,codes:array<int,array{code:string,label:?string,documentation:?string}>}>, 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,
|
|
];
|
|
}
|
|
}
|