mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Add SS_Database::withTransaction for nice enclosed transactions
This commit is contained in:
parent
633eb0163e
commit
8abede1339
@ -489,6 +489,42 @@ abstract class SS_Database {
|
|||||||
*/
|
*/
|
||||||
abstract public function supportsTransactions();
|
abstract public function supportsTransactions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke $callback within a transaction
|
||||||
|
*
|
||||||
|
* @param callable $callback Callback to run
|
||||||
|
* @param callable $errorCallback Optional callback to run after rolling back transaction.
|
||||||
|
* @param bool|string $transactionMode Optional transaction mode to use
|
||||||
|
* @param bool $errorIfTransactionsUnsupported If true, this method will fail if transactions are unsupported.
|
||||||
|
* Otherwise, the $callback will potentially be invoked outside of a transaction.
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function withTransaction(
|
||||||
|
$callback, $errorCallback = null, $transactionMode = false, $errorIfTransactionsUnsupported = false
|
||||||
|
) {
|
||||||
|
$supported = $this->supportsTransactions();
|
||||||
|
if(!$supported && $errorIfTransactionsUnsupported) {
|
||||||
|
throw new BadMethodCallException("Transactions not supported by this database.");
|
||||||
|
}
|
||||||
|
if($supported) {
|
||||||
|
$this->transactionStart($transactionMode);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
call_user_func($callback);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
if($supported) {
|
||||||
|
$this->transactionRollback();
|
||||||
|
}
|
||||||
|
if($errorCallback) {
|
||||||
|
call_user_func($errorCallback);
|
||||||
|
}
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
if($supported) {
|
||||||
|
$this->transactionEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determines if the current database connection supports a given list of extensions
|
* Determines if the current database connection supports a given list of extensions
|
||||||
*
|
*
|
||||||
|
@ -151,6 +151,44 @@ class DatabaseTest extends SapphireTest {
|
|||||||
$this->assertTrue($db->canLock('DatabaseTest'), 'Can lock again after releasing it');
|
$this->assertTrue($db->canLock('DatabaseTest'), 'Can lock again after releasing it');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTransactions() {
|
||||||
|
$conn = DB::getConn();
|
||||||
|
if(!$conn->supportsTransactions()) {
|
||||||
|
$this->markTestSkipped("DB Doesn't support transactions");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that successful transactions are comitted
|
||||||
|
$obj = new DatabaseTest_MyObject();
|
||||||
|
$failed = false;
|
||||||
|
$conn->withTransaction(function() use (&$obj) {
|
||||||
|
$obj->MyField = 'Save 1';
|
||||||
|
$obj->write();
|
||||||
|
}, function() use (&$failed) {
|
||||||
|
$failed = true;
|
||||||
|
});
|
||||||
|
$this->assertEquals('Save 1', DatabaseTest_MyObject::get()->first()->MyField);
|
||||||
|
$this->assertFalse($failed);
|
||||||
|
|
||||||
|
// Test failed transactions are rolled back
|
||||||
|
$ex = null;
|
||||||
|
$failed = false;
|
||||||
|
try {
|
||||||
|
$conn->withTransaction(function() use (&$obj) {
|
||||||
|
$obj->MyField = 'Save 2';
|
||||||
|
$obj->write();
|
||||||
|
throw new Exception("error");
|
||||||
|
}, function() use (&$failed) {
|
||||||
|
$failed = true;
|
||||||
|
});
|
||||||
|
} catch ( Exception $ex) {}
|
||||||
|
$this->assertTrue($failed);
|
||||||
|
$this->assertEquals('Save 1', DatabaseTest_MyObject::get()->first()->MyField);
|
||||||
|
$this->assertInstanceOf('Exception', $ex);
|
||||||
|
$this->assertEquals('error', $ex->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class DatabaseTest_MyObject extends DataObject implements TestOnly {
|
class DatabaseTest_MyObject extends DataObject implements TestOnly {
|
||||||
|
Loading…
Reference in New Issue
Block a user