Skip to content

Instantly share code, notes, and snippets.

Last active August 1, 2016 13:04
What would you like to do?
[Laravel] Nested transactions wrapper class
* Class for Laravel 3.x that provides ability to use nested transactions via SAVEPOINTs
* (inspired from
* 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()) {
} else {
public static function begin_transaction() {
static::process_pdo('beginTransaction', 'SAVEPOINT LEVEL'.static::$transaction_level);
public static function commit_transaction() {
static::process_pdo('commit', 'RELEASE SAVEPOINT LEVEL'.static::$transaction_level);
public static function rollback_transaction() {
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) {
try {
} catch (\Exception $e) {
throw $e;
return static::commit_transaction();
Copy link

jan-j commented Aug 29, 2013

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 missed Exception 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 :)

Copy link

Thanks for the note, @jan-j. Just have added global namespace prefix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment