Rámec nemá žádný způsob, jak zjistit, zda jste zahájili transakci. Můžete dokonce použít $db->query('START TRANSACTION')
o kterých by framework nevěděl, protože neanalyzuje příkazy SQL, které provádíte.
Jde o to, že je odpovědností aplikace sledovat, zda jste zahájili transakci nebo ne. Není to něco, co framework dokáže.
Vím, že některé frameworky se o to pokoušejí a dělají cockamamie věci, jako je počítání, kolikrát jste zahájili transakci, a vyřeší to pouze tehdy, když jste provedli commit nebo rollback odpovídající počet opakování. Ale to je zcela falešné, protože žádná z vašich funkcí nemůže vědět, zda to commit nebo rollback skutečně provede, nebo jestli jsou v jiné vrstvě vnoření.
(Můžete říct, že jsem tuto diskuzi vedl několikrát? :-)
Aktualizace 1: Propel je knihovna pro přístup k databázi PHP, která podporuje koncept "vnitřní transakce", která se nepotvrdí, když jí to řeknete. Zahájení transakce pouze zvýší počítadlo a potvrzení/vrácení sníží počítadlo. Níže je výňatek z vlákna konference, kde popisuji několik scénářů, kdy selže.
Aktualizace 2: Doktrína DBAL má také tuto vlastnost. Říkají tomu Transaction Nesting.
Ať se vám to líbí nebo ne, transakce jsou „globální“ a nepodléhají objektově orientovanému zapouzdření.
Problémový scénář č. 1
Volám commit()
, jsou mé změny potvrzeny? Pokud běžím uvnitř "vnitřní transakce", nejsou. Kód, který spravuje vnější transakci, by se mohl rozhodnout vrátit zpět a moje změny by byly bez mého vědomí nebo kontroly zahozeny.
Například:
- Model A:zahájení transakce
- Model A:Proveďte některé změny
- Model B:zahájení transakce (tiché ne-op)
- Model B:proveďte některé změny
- Model B:potvrzení (tiché ne-op)
- Model A:vrácení zpět (zahodí změny modelu A i změny modelu B)
- Model B:WTF!? Co se stalo s mými změnami?
Problémový scénář č. 2
Vnitřní transakce se vrátí zpět, mohla by zrušit legitimní změny provedené vnější transakcí. Když je kontrola vrácena vnějšímu kódu, věří, že jeho transakce je stále aktivní a je k dispozici pro potvrzení. S vaším patchem by mohli zavolat commit()
a protože transDepth je nyní 0, nastavilo by tiše $transDepth
na -1 a vrátí hodnotu true poté, co nic neprovedete.
Scénář problému č. 3
Pokud zavolám commit()
nebo rollback()
když není aktivní žádná transakce, nastaví $transDepth
na -1. Další beginTransaction()
zvýší úroveň na 0, což znamená, že transakci nelze vrátit zpět ani potvrdit. Následná volání funkce commit()
pouze sníží transakci na -1 nebo více a nikdy nebudete moci provést potvrzení, dokud neprovedete další zbytečnou beginTransaction()
pro opětovné zvýšení úrovně.
V zásadě je pokus o správu transakcí v aplikační logice bez toho, aby databáze vedla účetnictví, nápadem odsouzeným k zániku. Pokud požadujete, aby dva modely používaly explicitní řízení transakcí v jedné žádosti aplikace, musíte otevřít dvě DB připojení, jedno pro každý model. Každý model pak může mít svou vlastní aktivní transakci, kterou lze potvrdit nebo vrátit zpět nezávisle na sobě.