Skip to content

Commit 0b475bb

Browse files
Better transaction manager object design (#49103)
* better object design * Apply fixes from StyleCI * use method for testing * add or * fix condition --------- Co-authored-by: StyleCI Bot <[email protected]>
1 parent 99d21f3 commit 0b475bb

File tree

6 files changed

+81
-88
lines changed

6 files changed

+81
-88
lines changed

src/Illuminate/Database/Concerns/ManagesTransactions.php

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,16 @@ public function transaction(Closure $callback, $attempts = 1)
4747
$this->getPdo()->commit();
4848
}
4949

50-
$this->transactionsManager?->stageTransactions($this->getName(), $this->transactions);
51-
52-
$this->transactions = max(0, $this->transactions - 1);
53-
54-
if ($this->afterCommitCallbacksShouldBeExecuted()) {
55-
$this->transactionsManager?->commit($this->getName());
56-
}
50+
[$levelBeingCommitted, $this->transactions] = [
51+
$this->transactions,
52+
max(0, $this->transactions - 1),
53+
];
54+
55+
$this->transactionsManager?->commit(
56+
$this->getName(),
57+
$levelBeingCommitted,
58+
$this->transactions
59+
);
5760
} catch (Throwable $e) {
5861
$this->handleCommitTransactionException(
5962
$e, $currentAttempt, $attempts
@@ -196,27 +199,18 @@ public function commit()
196199
$this->getPdo()->commit();
197200
}
198201

199-
$this->transactionsManager?->stageTransactions($this->getName(), $this->transactions);
202+
[$levelBeingCommitted, $this->transactions] = [
203+
$this->transactions,
204+
max(0, $this->transactions - 1),
205+
];
200206

201-
$this->transactions = max(0, $this->transactions - 1);
202-
203-
if ($this->afterCommitCallbacksShouldBeExecuted()) {
204-
$this->transactionsManager?->commit($this->getName());
205-
}
207+
$this->transactionsManager?->commit(
208+
$this->getName(), $levelBeingCommitted, $this->transactions
209+
);
206210

207211
$this->fireConnectionEvent('committed');
208212
}
209213

210-
/**
211-
* Determine if after commit callbacks should be executed.
212-
*
213-
* @return bool
214-
*/
215-
protected function afterCommitCallbacksShouldBeExecuted()
216-
{
217-
return $this->transactionsManager?->afterCommitCallbacksShouldBeExecuted($this->transactions) || $this->transactions == 0;
218-
}
219-
220214
/**
221215
* Handle an exception encountered when committing a transaction.
222216
*

src/Illuminate/Database/DatabaseTransactionsManager.php

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,39 +59,26 @@ public function begin($connection, $level)
5959
}
6060

6161
/**
62-
* Move relevant pending transactions to a committed state.
62+
* Commit the root database transaction and execute callbacks.
6363
*
6464
* @param string $connection
6565
* @param int $levelBeingCommitted
66-
* @return void
66+
* @param int $newTransactionLevel
67+
* @return array
6768
*/
68-
public function stageTransactions($connection, $levelBeingCommitted)
69+
public function commit($connection, $levelBeingCommitted, $newTransactionLevel)
6970
{
70-
$this->committedTransactions = $this->committedTransactions->merge(
71-
$this->pendingTransactions->filter(
72-
fn ($transaction) => $transaction->connection === $connection &&
73-
$transaction->level >= $levelBeingCommitted
74-
)
75-
);
76-
77-
$this->pendingTransactions = $this->pendingTransactions->reject(
78-
fn ($transaction) => $transaction->connection === $connection &&
79-
$transaction->level >= $levelBeingCommitted
80-
);
71+
$this->stageTransactions($connection, $levelBeingCommitted);
8172

8273
if (isset($this->currentTransaction[$connection])) {
8374
$this->currentTransaction[$connection] = $this->currentTransaction[$connection]->parent;
8475
}
85-
}
8676

87-
/**
88-
* Commit the root database transaction and execute callbacks.
89-
*
90-
* @param string $connection
91-
* @return void
92-
*/
93-
public function commit($connection)
94-
{
77+
if (! $this->afterCommitCallbacksShouldBeExecuted($newTransactionLevel) &&
78+
$newTransactionLevel !== 0) {
79+
return [];
80+
}
81+
9582
// This method is only called when the root database transaction is committed so there
9683
// shouldn't be any pending transactions, but going to clear them here anyways just
9784
// in case. This method could be refactored to receive a level in the future too.
@@ -106,6 +93,30 @@ public function commit($connection)
10693
$this->committedTransactions = $forOtherConnections->values();
10794

10895
$forThisConnection->map->executeCallbacks();
96+
97+
return $forThisConnection;
98+
}
99+
100+
/**
101+
* Move relevant pending transactions to a committed state.
102+
*
103+
* @param string $connection
104+
* @param int $levelBeingCommitted
105+
* @return void
106+
*/
107+
public function stageTransactions($connection, $levelBeingCommitted)
108+
{
109+
$this->committedTransactions = $this->committedTransactions->merge(
110+
$this->pendingTransactions->filter(
111+
fn ($transaction) => $transaction->connection === $connection &&
112+
$transaction->level >= $levelBeingCommitted
113+
)
114+
);
115+
116+
$this->pendingTransactions = $this->pendingTransactions->reject(
117+
fn ($transaction) => $transaction->connection === $connection &&
118+
$transaction->level >= $levelBeingCommitted
119+
);
109120
}
110121

111122
/**

tests/Database/DatabaseConnectionTest.php

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Exception;
88
use Illuminate\Contracts\Events\Dispatcher;
99
use Illuminate\Database\Connection;
10-
use Illuminate\Database\DatabaseTransactionsManager;
1110
use Illuminate\Database\Events\QueryExecuted;
1211
use Illuminate\Database\Events\TransactionBeginning;
1312
use Illuminate\Database\Events\TransactionCommitted;
@@ -290,20 +289,6 @@ public function testCommittingFiresEventsIfSet()
290289
$connection->commit();
291290
}
292291

293-
public function testAfterCommitIsExecutedOnFinalCommit()
294-
{
295-
$pdo = $this->getMockBuilder(DatabaseConnectionTestMockPDO::class)->onlyMethods(['beginTransaction', 'commit'])->getMock();
296-
$transactionsManager = $this->getMockBuilder(DatabaseTransactionsManager::class)->onlyMethods(['afterCommitCallbacksShouldBeExecuted'])->getMock();
297-
$transactionsManager->expects($this->once())->method('afterCommitCallbacksShouldBeExecuted')->with(0)->willReturn(true);
298-
299-
$connection = $this->getMockConnection([], $pdo);
300-
$connection->setTransactionManager($transactionsManager);
301-
302-
$connection->transaction(function () {
303-
// do nothing
304-
});
305-
}
306-
307292
public function testRollBackedFiresEventsIfSet()
308293
{
309294
$pdo = $this->createMock(DatabaseConnectionTestMockPDO::class);

tests/Database/DatabaseTransactionsManagerTest.php

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,24 @@ public function testCommittingTransactions()
6666
$manager->begin('default', 1);
6767
$manager->begin('default', 2);
6868
$manager->begin('admin', 1);
69+
$manager->begin('admin', 2);
6970

70-
$manager->stageTransactions('default', 1);
71-
$manager->stageTransactions('admin', 1);
72-
$manager->commit('default');
71+
$manager->commit('default', 2, 1);
72+
$executedTransactions = $manager->commit('default', 1, 0);
7373

74-
$this->assertCount(0, $manager->getPendingTransactions());
75-
$this->assertCount(1, $manager->getCommittedTransactions());
74+
$executedAdminTransactions = $manager->commit('admin', 2, 1);
75+
76+
$this->assertCount(1, $manager->getPendingTransactions()); // One pending "admin" transaction left...
77+
$this->assertCount(2, $executedTransactions); // Two committed tranasctions on "default"
78+
$this->assertCount(0, $executedAdminTransactions); // Zero executed committed tranasctions on "default"
7679

80+
// Level 2 "admin" callback has been staged...
7781
$this->assertSame('admin', $manager->getCommittedTransactions()[0]->connection);
78-
$this->assertEquals(1, $manager->getCommittedTransactions()[0]->level);
82+
$this->assertEquals(2, $manager->getCommittedTransactions()[0]->level);
83+
84+
// Level 1 "admin" callback still pending...
85+
$this->assertSame('admin', $manager->getPendingTransactions()[0]->connection);
86+
$this->assertEquals(1, $manager->getPendingTransactions()[0]->level);
7987
}
8088

8189
public function testCallbacksAreAddedToTheCurrentTransaction()
@@ -121,12 +129,12 @@ public function testCommittingTransactionsExecutesCallbacks()
121129

122130
$manager->begin('admin', 1);
123131

124-
$manager->stageTransactions('default', 1);
125-
$manager->commit('default');
132+
$manager->commit('default', 2, 1);
133+
$manager->commit('default', 1, 0);
126134

127135
$this->assertCount(2, $callbacks);
128-
$this->assertEquals(['default', 1], $callbacks[0]);
129-
$this->assertEquals(['default', 2], $callbacks[1]);
136+
$this->assertEquals(['default', 2], $callbacks[0]);
137+
$this->assertEquals(['default', 1], $callbacks[1]);
130138
}
131139

132140
public function testCommittingExecutesOnlyCallbacksOfTheConnection()
@@ -148,8 +156,8 @@ public function testCommittingExecutesOnlyCallbacksOfTheConnection()
148156
$callbacks[] = ['admin', 1];
149157
});
150158

151-
$manager->stageTransactions('default', 1);
152-
$manager->commit('default');
159+
$manager->commit('default', 2, 1);
160+
$manager->commit('default', 1, 0);
153161

154162
$this->assertCount(1, $callbacks);
155163
$this->assertEquals(['default', 1], $callbacks[0]);

tests/Database/DatabaseTransactionsTest.php

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ public function testTransactionIsRecordedAndCommitted()
6464
{
6565
$transactionManager = m::mock(new DatabaseTransactionsManager);
6666
$transactionManager->shouldReceive('begin')->once()->with('default', 1);
67-
$transactionManager->shouldReceive('stageTransactions')->once()->with('default', 1);
68-
$transactionManager->shouldReceive('commit')->once()->with('default');
67+
$transactionManager->shouldReceive('commit')->once()->with('default', 1, 0);
6968

7069
$this->connection()->setTransactionManager($transactionManager);
7170

@@ -84,8 +83,7 @@ public function testTransactionIsRecordedAndCommittedUsingTheSeparateMethods()
8483
{
8584
$transactionManager = m::mock(new DatabaseTransactionsManager);
8685
$transactionManager->shouldReceive('begin')->once()->with('default', 1);
87-
$transactionManager->shouldReceive('stageTransactions')->once()->with('default', 1);
88-
$transactionManager->shouldReceive('commit')->once()->with('default');
86+
$transactionManager->shouldReceive('commit')->once()->with('default', 1, 0);
8987

9088
$this->connection()->setTransactionManager($transactionManager);
9189

@@ -105,9 +103,8 @@ public function testNestedTransactionIsRecordedAndCommitted()
105103
$transactionManager = m::mock(new DatabaseTransactionsManager);
106104
$transactionManager->shouldReceive('begin')->once()->with('default', 1);
107105
$transactionManager->shouldReceive('begin')->once()->with('default', 2);
108-
$transactionManager->shouldReceive('stageTransactions')->once()->with('default', 1);
109-
$transactionManager->shouldReceive('stageTransactions')->once()->with('default', 2);
110-
$transactionManager->shouldReceive('commit')->once()->with('default');
106+
$transactionManager->shouldReceive('commit')->once()->with('default', 2, 1);
107+
$transactionManager->shouldReceive('commit')->once()->with('default', 1, 0);
111108

112109
$this->connection()->setTransactionManager($transactionManager);
113110

@@ -134,11 +131,9 @@ public function testNestedTransactionIsRecordeForDifferentConnectionsdAndCommitt
134131
$transactionManager->shouldReceive('begin')->once()->with('default', 1);
135132
$transactionManager->shouldReceive('begin')->once()->with('second_connection', 1);
136133
$transactionManager->shouldReceive('begin')->once()->with('second_connection', 2);
137-
$transactionManager->shouldReceive('stageTransactions')->once()->with('default', 1);
138-
$transactionManager->shouldReceive('stageTransactions')->once()->with('second_connection', 1);
139-
$transactionManager->shouldReceive('stageTransactions')->once()->with('second_connection', 2);
140-
$transactionManager->shouldReceive('commit')->once()->with('default');
141-
$transactionManager->shouldReceive('commit')->once()->with('second_connection');
134+
$transactionManager->shouldReceive('commit')->once()->with('default', 1, 0);
135+
$transactionManager->shouldReceive('commit')->once()->with('second_connection', 2, 1);
136+
$transactionManager->shouldReceive('commit')->once()->with('second_connection', 1, 0);
142137

143138
$this->connection()->setTransactionManager($transactionManager);
144139
$this->connection('second_connection')->setTransactionManager($transactionManager);
@@ -196,7 +191,7 @@ public function testTransactionIsRolledBackUsingSeparateMethods()
196191
$transactionManager = m::mock(new DatabaseTransactionsManager);
197192
$transactionManager->shouldReceive('begin')->once()->with('default', 1);
198193
$transactionManager->shouldReceive('rollback')->once()->with('default', 0);
199-
$transactionManager->shouldNotReceive('commit');
194+
$transactionManager->shouldNotReceive('commit', 1, 0);
200195

201196
$this->connection()->setTransactionManager($transactionManager);
202197

tests/Foundation/Testing/DatabaseTransactionsManagerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ public function testItExecutesCallbacksForTheSecondTransaction()
4242

4343
$this->assertFalse($testObject->ran);
4444

45-
$manager->stageTransactions('foo', 1);
46-
$manager->commit('foo');
45+
$manager->commit('foo', 2, 1);
46+
$manager->commit('foo', 1, 0);
4747
$this->assertTrue($testObject->ran);
4848
$this->assertEquals(1, $testObject->runs);
4949
}

0 commit comments

Comments
 (0)