Le importazioni massive in PHP spesso iniziano con un CSV inviato da un cliente, un export di un fornitore o un'estrazione da un sistema legacy. Il rischio emerge quando vengono trattate come una semplice lettura di file seguita da inserimenti nel database. Una riga può avere un formato valido e, tuttavia, creare un duplicato, violare una regola di business, sovrascrivere informazioni correnti o attivare due volte un effetto esterno.
Un'importazione deve essere progettata come un processo operativo con un ciclo definito: ricezione, analisi, validazione, anteprima, conferma, esecuzione, revisione e recupero. Questo approccio permette al prodotto di comprendere cosa verrà acquisito, alle operations di intervenire in caso di eccezioni e alla tecnologia di limitare l'impatto dei dati difettosi.
Trattare l'importazione come un processo di business

Il file non è di per sé la fonte di verità: è una richiesta di modificare lo stato dell'applicazione. Per questo, è opportuno creare un'entità di importazione con un proprio identificatore, l'utente o il sistema che l'ha avviata, data di ricezione, tipo di dati, versione del contratto, file o riferimento sicuro allo stesso, stato globale e riepilogo dei risultati.
Gli stati globali devono esprimere una situazione gestibile, non solo un booleano. Per esempio: ricevuto, analizzato, in attesa di conferma, in elaborazione, completato, completato con anomalie, interrotto o annullato. Un'importazione con 9.800 righe accettate e 200 rifiutate non è necessariamente un fallimento; può essere un'esecuzione completata con anomalie se le righe rifiutate sono isolate e spiegate.
Occorre anche decidere quali conseguenze appartengono al caricamento. Creare un ordine, aggiornare un catalogo o inserire contatti può richiedere calcoli, audit o notifiche. Separare la persistenza principale dagli effetti collaterali riduce il rischio che un nuovo tentativo invii messaggi ripetuti o esegua integrazioni in modo non controllato.
Definire un contratto di input prima di accettare file
Il contratto di importazione specifica cosa ci si aspetta di ricevere e come verrà interpretato. Deve includere formato consentito, codifica, separatore, intestazioni, tipi di dato, campi obbligatori, formato delle date, regole di normalizzazione, limiti di dimensione e numero massimo di righe. Se si accettano fogli di calcolo, occorre definire anche il foglio rilevante e come verranno trattate le celle vuote, le formule e i valori convertiti automaticamente.
I campi che collegano l'origine alla destinazione meritano particolare attenzione. Un identificatore esterno stabile, come il codice cliente nel sistema di origine, è preferibile all'uso del numero di riga o di un nome come riferimento. Deve essere chiarito se tale identificatore crea un record, ne aggiorna uno esistente o se entrambe le operazioni sono consentite.
Separare i livelli di validazione
La validazione strutturale risponde a domande meccaniche: il file può essere letto? Esistono le colonne richieste? La data ha un formato supportato? L'importo è numerico? La riga rispetta il limite di lunghezza? Questo livello deve rilevare tempestivamente i problemi che impediscono di interpretare i dati.
La validazione di dominio applica regole di business: uno stato può non essere consentito, una data di cessazione non può precedere quella di attivazione, una percentuale deve rimanere nel proprio intervallo oppure una combinazione di campi può risultare incompatibile. Infine, le verifiche rispetto ai dati esistenti controllano riferimenti, permessi, unicità e transizioni consentite. Per esempio, che un codice fornitore esista, che l'utente possa operare su tale organizzazione o che un record non sia bloccato.
Questa separazione migliora i messaggi e la diagnosi. Non è lo stesso segnalare una colonna mancante, un riferimento inesistente o una modifica non autorizzata. Inoltre, le regole di dominio devono essere riutilizzate dall'applicazione e dall'importatore per evitare che il caricamento diventi una scorciatoia che aggira i controlli normali.
Visualizzare in anteprima le modifiche e confermare un'intenzione esplicita
L'anteprima non deve promettere un'esecuzione esatta se i dati possono cambiare tra l'analisi e la conferma, ma deve offrire una stima verificabile. È opportuno mostrare il totale delle righe lette, delle righe valide, delle righe con avvisi, delle righe rifiutate, delle nuove creazioni previste, degli aggiornamenti previsti e dei record che non subiranno modifiche.
Gli avvisi servono per i casi che richiedono attenzione, ma non invalidano automaticamente una riga: per esempio, un telefono normalizzato, una descrizione troncata secondo una regola nota o un campo opzionale vuoto. Gli avvisi non devono nascondere i rifiuti. Ogni risultato necessita di un codice stabile, un messaggio comprensibile e, quando è sicuro, del valore ricevuto e del valore normalizzato.
La conferma deve essere associata a una versione specifica dell'analisi. Se l'utente sostituisce il file, corregge righe in un'interfaccia o modifica parametri rilevanti, il sistema deve invalidare l'anteprima precedente e richiedere una nuova analisi. In questo modo si evita di confermare un riepilogo che non rappresenta più il caricamento reale.
Elaborare a batch senza stati ambigui
Elaborare tutte le righe all'interno di un'unica transazione sembra sicuro, ma può mantenere i lock troppo a lungo, superare i limiti di esecuzione o trasformare un errore puntuale in un rollback costoso. Elaborare invece una riga per transazione può generare troppo overhead e rende difficile coordinare operazioni correlate.
L'unità di lavoro deve essere scelta in base alla dipendenza tra record, al volume e al costo del rollback. In molti casi, un batch piccolo e delimitato consente di confermare le modifiche progressivamente. Ogni batch deve registrare inizio, conclusione, numero di righe trattate e risultato. Il worker deve poter riprendere senza dipendere da una sessione HTTP aperta: il caricamento viene confermato dall'interfaccia, ma la sua esecuzione avviene come attività in background.
Occorre evitare di mantenere file completi in memoria. È necessario leggere in modo sequenziale, normalizzare ogni riga e archiviare una rappresentazione di lavoro o un risultato di validazione quando necessario per eseguire audit e riprendere. Vanno imposti limiti di dimensione, righe, tempo e concorrenza. Un file inaspettatamente grande non deve bloccare le risorse che gestiscono il lavoro quotidiano.
per ogni batch in sospeso:
contrassegnare il batch come in_elaborazione
per ogni riga del batch:
applicare le validazioni in sospeso
persistere o inviare in quarantena
registrare il risultato per riga
confermare il batch
contrassegnare il batch come completatoSe un worker viene interrotto, non basta rieseguire il batch alla cieca. Serve un meccanismo di lock con scadenza o recupero e lo stato per riga deve consentire di distinguere ciò che è in sospeso da ciò che è già stato confermato.
Isolare gli errori in quarantena e conservare le evidenze
La quarantena consente alle righe non valide di non bloccare quelle corrette senza scomparire dal processo. Una riga in quarantena deve conservare il numero o l'identificatore di origine, i dati ricevuti soggetti a controlli di accesso, i dati normalizzati se esistono, codici di errore, momento di rilevamento e stato di revisione.
Non tutti gli errori hanno lo stesso trattamento. Un file senza l'intestazione richiesta è un errore del file e può interrompere l'intera analisi. Un riferimento a un fornitore inesistente può essere un rifiuto di riga. Un'interruzione temporanea del database o di un'integrazione è un errore tecnico ritentabile e non deve essere etichettato come difetto del dato.
L'interfaccia operativa deve consentire di filtrare per motivo, esportare solo le anomalie autorizzate e comprendere quale correzione è prevista. Correggere all'interno dell'applicazione può essere utile per pochi casi; per molti record, di solito è più controllabile scaricare le anomalie, correggerle all'origine e presentare un nuovo caricamento. In entrambi i casi, occorre mantenere lo storico: modificare una riga in quarantena non deve cancellare il valore originale né il motivo iniziale.
Garantire l'idempotenza ed evitare duplicati
L'idempotenza significa che ripetere un'operazione con la stessa intenzione non altera il risultato più di una volta. È essenziale perché i nuovi tentativi accadono: scade un timeout, un processo si riavvia o un operatore conferma di nuovo a fronte di una risposta incerta.
Una chiave di idempotenza può essere costruita a partire dal tipo di importazione, dall'organizzazione di destinazione e da un identificatore esterno stabile. Deve essere supportata da vincoli di unicità nel database quando il modello lo consente; verificare prima e poi inserire non elimina le race condition tra worker concorrenti.
Per gli aggiornamenti, occorre definire una policy esplicita: sostituire i campi, applicare solo valori non vuoti, rifiutare conflitti o richiedere una versione attesa del record. L'ultima opzione aiuta a rilevare che un utente ha modificato il dato dopo l'anteprima. Non utilizzare l'hash del file come unico meccanismo: lo stesso contenuto può rappresentare un'intenzione diversa e un file corretto può conservare molte righe già elaborate.
Tracciabilità, nuovi tentativi e recupero selettivo

Il risultato per riga è l'elemento centrale per supporto e recupero. Occorre registrare uno stato come in sospeso, elaborata, rifiutata, in quarantena, nuovo tentativo in sospeso o omessa; l'identificatore esterno; il batch; codici motivo; timestamp; e un riferimento al record creato o aggiornato. Vanno protetti dati personali e segreti: la tracciabilità deve essere sufficiente per indagare, non una copia indiscriminata di informazioni sensibili nei log.
I nuovi tentativi devono essere selettivi. È opportuno ritentare automaticamente gli errori tecnici transitori con limiti e backoff progressivo; non si deve ritentare indefinitamente una regola di dominio non rispettata. Quando viene corretto un riferimento mancante o un dato non valido, vanno rielaborate solo le righe in quarantena corrispondenti. Quando un batch fallisce, si deve continuare dalle righe in sospeso e usare quelle già elaborate come evidenza di avanzamento.
Prima di mettere il flusso in produzione, occorre testare file vuoti, intestazioni alterate, codifiche inattese, duplicati all'interno dello stesso file, duplicati rispetto al database, interruzioni tra batch, riprese e permessi insufficienti. Le importazioni massive in PHP sono affidabili quando il loro comportamento in caso di errore è progettato con la stessa precisione del loro percorso corretto.



