Skip to content

Commit 545a2b5

Browse files
committed
PHPORM-323 Add $onFailure 3rd parameter to Connection::transaction method
Fix compatibility with Laravel 12.9.0 laravel/framework#55338
1 parent fa6c0c2 commit 545a2b5

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

src/Concerns/ManagesTransactions.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
use MongoDB\Driver\Exception\RuntimeException;
1010
use MongoDB\Driver\Session;
1111
use Throwable;
12+
use TypeError;
1213

14+
use function assert;
15+
use function get_debug_type;
1316
use function MongoDB\with_transaction;
17+
use function sprintf;
1418

1519
/**
1620
* @internal
@@ -78,15 +82,27 @@ public function rollBack($toLevel = null): void
7882
}
7983

8084
/**
81-
* Static transaction function realize the with_transaction functionality provided by MongoDB.
85+
* Static transaction function realize the {@see with_transaction} functionality provided by MongoDB.
8286
*
83-
* @param int $attempts
87+
* @param int $attempts
88+
* @param array|Closure|null $options
8489
*/
85-
public function transaction(Closure $callback, $attempts = 1, array $options = []): mixed
90+
public function transaction(Closure $callback, $attempts = 1, array|Closure|null $options = null): mixed
8691
{
92+
$options ??= [];
93+
$onFailure = null;
94+
/** $onFailure is a 3rd parameter introduced in Laravel 12.9.0 to {@see \Illuminate\Database\ConnectionInterface} */
95+
if ($options instanceof Closure) {
96+
$onFailure = $options;
97+
$options = [];
98+
} elseif (isset($options['onFailure'])) {
99+
assert($options['onFailure'] instanceof Closure, new TypeError(sprintf('Expected "onFailure" option to be a Closure or null, got %s', get_debug_type($options['onFailure']))));
100+
$onFailure = $options['onFailure'];
101+
unset($options['onFailure']);
102+
}
103+
87104
$attemptsLeft = $attempts;
88105
$callbackResult = null;
89-
$throwable = null;
90106

91107
$callbackFunction = function (Session $session) use ($callback, &$attemptsLeft, &$callbackResult, &$throwable) {
92108
$attemptsLeft--;
@@ -110,6 +126,10 @@ public function transaction(Closure $callback, $attempts = 1, array $options = [
110126
with_transaction($this->getSessionOrCreate(), $callbackFunction, $options);
111127

112128
if ($attemptsLeft < 0 && $throwable) {
129+
if ($onFailure) {
130+
$onFailure($throwable);
131+
}
132+
113133
throw $throwable;
114134
}
115135

tests/TransactionTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,52 @@ public function testRollBackWithoutSession(): void
446446
DB::rollback();
447447
}
448448

449+
public function testOnErrorCallbackIsCalled()
450+
{
451+
$executed = 0;
452+
try {
453+
DB::connection('mongodb')->transaction(function () {
454+
throw new class extends \MongoDB\Driver\Exception\RuntimeException {
455+
protected $errorLabels = ['TransientTransactionError'];
456+
};
457+
}, 1, function () use (&$executed) {
458+
$executed++;
459+
});
460+
461+
self::fail('Expected an exception to be thrown.');
462+
} catch (\MongoDB\Driver\Exception\RuntimeException) {
463+
}
464+
465+
$this->assertSame(1, $executed);
466+
}
467+
468+
public function testOnErrorCallbackIsCalledWithDeadlockRetry()
469+
{
470+
$executed = $attempts = 0;
471+
472+
$connection = DB::connection('mongodb');
473+
self::assertInstanceOf(Connection::class, $connection);
474+
475+
try {
476+
$connection->transaction(function () use (&$attempts) {
477+
$attempts += 1;
478+
throw new class extends \MongoDB\Driver\Exception\RuntimeException {
479+
protected $errorLabels = ['TransientTransactionError'];
480+
};
481+
}, 3, [
482+
'onFailure' => function () use (&$executed) {
483+
$executed++;
484+
},
485+
]);
486+
487+
self::fail('Expected an exception to be thrown.');
488+
} catch (\MongoDB\Driver\Exception\RuntimeException) {
489+
}
490+
491+
$this->assertSame(3, $attempts);
492+
$this->assertSame(1, $executed);
493+
}
494+
449495
private function getPrimaryServerType(): int
450496
{
451497
return DB::getMongoClient()->getManager()->selectServer()->getType();

0 commit comments

Comments
 (0)