Skip to content

Commit 27ac702

Browse files
committed
Use DB tx manager in ManagesTransactions trait
`DatabaseTransactionsManager` was introduced in Laravel to keep track of things like pending transactions and fire transaction events, so that e.g. dispatches could hook into transaction state. Our trait was not using it yet, resulting in missing `afterCommit` behavior (see PHPLIB-373)
1 parent c483b99 commit 27ac702

File tree

2 files changed

+342
-3
lines changed

2 files changed

+342
-3
lines changed

src/Concerns/ManagesTransactions.php

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
use MongoDB\Client;
99
use MongoDB\Driver\Exception\RuntimeException;
1010
use MongoDB\Driver\Session;
11+
use MongoDB\Laravel\Connection;
1112
use Throwable;
1213

14+
use function max;
1315
use function MongoDB\with_transaction;
16+
use function property_exists;
1417

1518
/**
1619
* @internal
@@ -55,32 +58,93 @@ private function getSessionOrThrow(): Session
5558
*/
5659
public function beginTransaction(array $options = []): void
5760
{
61+
$this->runCallbacksBeforeTransaction();
62+
5863
$this->getSessionOrCreate()->startTransaction($options);
64+
65+
$this->handleInitialTransactionState();
66+
}
67+
68+
private function handleInitialTransactionState(): void
69+
{
5970
$this->transactions = 1;
71+
72+
$this->transactionsManager?->begin(
73+
$this->getName(),
74+
$this->transactions,
75+
);
76+
77+
$this->fireConnectionEvent('beganTransaction');
6078
}
6179

6280
/**
6381
* Commit transaction in this session.
6482
*/
6583
public function commit(): void
6684
{
85+
$this->fireConnectionEvent('committing');
6786
$this->getSessionOrThrow()->commitTransaction();
68-
$this->transactions = 0;
87+
88+
$this->handleCommitState();
89+
}
90+
91+
private function handleCommitState(): void
92+
{
93+
[$levelBeingCommitted, $this->transactions] = [
94+
$this->transactions,
95+
max(0, $this->transactions - 1),
96+
];
97+
98+
$this->transactionsManager?->commit(
99+
$this->getName(),
100+
$levelBeingCommitted,
101+
$this->transactions,
102+
);
103+
104+
$this->fireConnectionEvent('committed');
69105
}
70106

71107
/**
72108
* Abort transaction in this session.
73109
*/
74110
public function rollBack($toLevel = null): void
75111
{
76-
$this->getSessionOrThrow()->abortTransaction();
112+
$session = $this->getSessionOrThrow();
113+
if ($session->isInTransaction()) {
114+
$session->abortTransaction();
115+
}
116+
117+
$this->handleRollbackState();
118+
}
119+
120+
private function handleRollbackState(): void
121+
{
77122
$this->transactions = 0;
123+
124+
$this->transactionsManager?->rollback(
125+
$this->getName(),
126+
$this->transactions,
127+
);
128+
129+
$this->fireConnectionEvent('rollingBack');
130+
}
131+
132+
private function runCallbacksBeforeTransaction(): void
133+
{
134+
// ToDo: remove conditional once we stop supporting Laravel 10.x
135+
if (property_exists(Connection::class, 'beforeStartingTransaction')) {
136+
foreach ($this->beforeStartingTransaction as $beforeTransactionCallback) {
137+
$beforeTransactionCallback($this);
138+
}
139+
}
78140
}
79141

80142
/**
81143
* Static transaction function realize the with_transaction functionality provided by MongoDB.
82144
*
83-
* @param int $attempts
145+
* @param int $attempts
146+
*
147+
* @throws Throwable
84148
*/
85149
public function transaction(Closure $callback, $attempts = 1, array $options = []): mixed
86150
{
@@ -93,15 +157,20 @@ public function transaction(Closure $callback, $attempts = 1, array $options = [
93157

94158
if ($attemptsLeft < 0) {
95159
$session->abortTransaction();
160+
$this->handleRollbackState();
96161

97162
return;
98163
}
99164

165+
$this->runCallbacksBeforeTransaction();
166+
$this->handleInitialTransactionState();
167+
100168
// Catch, store, and re-throw any exception thrown during execution
101169
// of the callable. The last exception is re-thrown if the transaction
102170
// was aborted because the number of callback attempts has been exceeded.
103171
try {
104172
$callbackResult = $callback($this);
173+
$this->fireConnectionEvent('committing');
105174
} catch (Throwable $throwable) {
106175
throw $throwable;
107176
}
@@ -110,9 +179,12 @@ public function transaction(Closure $callback, $attempts = 1, array $options = [
110179
with_transaction($this->getSessionOrCreate(), $callbackFunction, $options);
111180

112181
if ($attemptsLeft < 0 && $throwable) {
182+
$this->handleRollbackState();
113183
throw $throwable;
114184
}
115185

186+
$this->handleCommitState();
187+
116188
return $callbackResult;
117189
}
118190
}

0 commit comments

Comments
 (0)