Il framework non ha modo di sapere se hai avviato una transazione. Puoi anche usare $db->query('START TRANSACTION')
di cui il framework non sarebbe a conoscenza perché non analizza le istruzioni SQL eseguite.
Il punto è che è responsabilità dell'applicazione tenere traccia dell'avvio o meno di una transazione. Non è qualcosa che il framework può fare.
So che alcuni framework provano a farlo e fanno cose stravaganti come contare quante volte hai iniziato una transazione, risolvendola solo quando hai eseguito il commit o il rollback un numero corrispondente di volte. Ma questo è totalmente falso perché nessuna delle tue funzioni può sapere se il commit o il rollback lo faranno effettivamente, o se si trovano in un altro livello di annidamento.
(Puoi dire che ho avuto questa discussione un paio di volte? :-)
Aggiornamento 1: Spingi è una libreria di accesso al database PHP che supporta il concetto di "transazione interna" che non si impegna quando glielo dici. L'inizio di una transazione incrementa solo un contatore e il commit/rollback decrementa il contatore. Di seguito è riportato un estratto da un thread di una mailing list in cui descrivo alcuni scenari in cui non riesce.
Aggiornamento 2: Dottrina DBAL ha anche questa caratteristica. Lo chiamano Transaction Nesting.
Piaccia o no, le transazioni sono "globali" e non obbediscono all'incapsulamento orientato agli oggetti.
Scenario problematico n. 1
Chiamo commit()
, le mie modifiche vengono salvate? Se sto correndo all'interno di una "transazione interna", non lo sono. Il codice che gestisce la transazione esterna potrebbe scegliere di eseguire il rollback e le mie modifiche verrebbero scartate a mia insaputa o controllo.
Ad esempio:
- Modello A:inizia la transazione
- Modello A:esegui alcune modifiche
- Modello B:inizia la transazione (silenzioso no-op)
- Modello B:esegui alcune modifiche
- Modello B:commit (no-op silenzioso)
- Modello A:rollback (elimina sia le modifiche del modello A che quelle del modello B)
- Modello B:WTF!? Che cosa è successo alle mie modifiche?
Scenario problematico n. 2
Una transazione interna esegue il rollback, potrebbe scartare le modifiche legittime apportate da una transazione esterna. Quando il controllo viene restituito al codice esterno, ritiene che la sua transazione sia ancora attiva e disponibile per il commit. Con la tua patch, potrebbero chiamare commit()
e poiché transDepth ora è 0, imposterebbe silenziosamente $transDepth
a -1 e restituisce true, dopo non aver commesso nulla.
Scenario problematico n. 3
Se chiamo commit()
o rollback()
quando non c'è nessuna transazione attiva, imposta il $transDepth
a -1. Il prossimo beginTransaction()
incrementa il livello a 0, il che significa che la transazione non può essere né annullata né sottoposta a commit. Chiamate successive a commit()
diminuirà semplicemente la transazione a -1 o oltre, e non sarai mai in grado di eseguire il commit fino a quando non eseguirai un altro superfluo beginTransaction()
per aumentare nuovamente il livello.
Fondamentalmente, provare a gestire le transazioni nella logica dell'applicazione senza consentire al database di fare la contabilità è un'idea condannata. Se è necessario che due modelli utilizzino il controllo esplicito delle transazioni in una richiesta dell'applicazione, è necessario aprire due connessioni DB, una per ciascun modello. Quindi ogni modello può avere la propria transazione attiva, che può essere sottoposta a commit o rollback indipendentemente l'una dall'altra.