Last active
August 1, 2016 13:04
-
-
Save neoascetic/5269127 to your computer and use it in GitHub Desktop.
[Laravel] Nested transactions wrapper class
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Class for Laravel 3.x that provides ability to use nested transactions via SAVEPOINTs | |
* (inspired from http://www.kennynet.co.uk/2008/12/02/php-pdo-nested-transactions/). | |
* For now, only the default connection is supported. | |
* | |
* Useful in testing, when you want to start a transaction before running a test | |
* and revert all its changes after it finished. | |
* | |
* To use this, you need to replace value under "DB" key in `application.aliases` config | |
* with this class name. After this, you can continue to use Laravel-style transactions | |
* (`DB::transaction`), but you need to replace all "raw PDO" transaction calls | |
* (such as `DB::connection()->pdo->beginTransaction()`) with alternatives provided | |
* by this class. | |
*/ | |
class NestedTransactor extends \Laravel\Database { | |
protected static $supported_drivers = ['pgsql', 'mysql', 'mysqli', 'sqlite']; | |
protected static $transaction_level = 0; | |
protected static function get_pdo() { | |
return static::connection()->pdo; | |
} | |
protected static function nestable() { | |
$current_driver = static::get_pdo()->getAttribute(\PDO::ATTR_DRIVER_NAME); | |
return in_array($current_driver, static::$supported_drivers); | |
} | |
protected static function process_pdo($pdo_method, $sql) { | |
$pdo = static::get_pdo(); | |
if (static::$transaction_level == 0 || !static::nestable()) { | |
$pdo->{$pdo_method}(); | |
} else { | |
$pdo->exec($sql); | |
} | |
} | |
public static function begin_transaction() { | |
static::process_pdo('beginTransaction', 'SAVEPOINT LEVEL'.static::$transaction_level); | |
static::$transaction_level++; | |
} | |
public static function commit_transaction() { | |
static::$transaction_level--; | |
static::process_pdo('commit', 'RELEASE SAVEPOINT LEVEL'.static::$transaction_level); | |
} | |
public static function rollback_transaction() { | |
static::$transaction_level--; | |
static::process_pdo('rollBack', 'ROLLBACK TO SAVEPOINT LEVEL'.static::$transaction_level); | |
} | |
/** | |
* Redefined transaction method of Laravel\Database\Connection | |
* @param callback $callback | |
* @return void | |
*/ | |
public static function transaction($callback) { | |
static::begin_transaction(); | |
try { | |
call_user_func($callback); | |
} catch (\Exception $e) { | |
static::rollback_transaction(); | |
throw $e; | |
} | |
return static::commit_transaction(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Really useful. Thanks.
To everybody that will use this class not under global namespace:
Check if you prepend \ before class used in all these methods(or other way ensure that you're using right classes). Trying to retrieve
PDO::ATTR_DRIVER_NAME
without global namespace prefix will trigger error and I caught that quickly. But I missedException
in catch in transaction method and it was really unpleasent bug, which caused some unrelated with database functions of laravel to not work as it supposed to. I spent half an hour to finally found the real cause. So watch out :)