Skip to content

Commit f753abd

Browse files
[12.x] Easily run pipelines inside of a transactions with Pipeline::inTransaction() (#56377)
* pipeline transaction * pipeline transaction test * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent f748796 commit f753abd

File tree

3 files changed

+82
-1
lines changed

3 files changed

+82
-1
lines changed

src/Illuminate/Pipeline/Pipeline.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ class Pipeline implements PipelineContract
4848
*/
4949
protected $finally;
5050

51+
/**
52+
* Indicates whether to wrap the pipeline in a database transaction.
53+
*
54+
* @var bool
55+
*/
56+
protected $withinTransactions = false;
57+
5158
/**
5259
* Create a new class instance.
5360
*
@@ -123,7 +130,9 @@ public function then(Closure $destination)
123130
);
124131

125132
try {
126-
return $pipeline($this->passable);
133+
return $this->withinTransactions
134+
? $this->container->make('db')->transaction(fn () => $pipeline($this->passable))
135+
: $pipeline($this->passable);
127136
} finally {
128137
if ($this->finally) {
129138
($this->finally)($this->passable);
@@ -245,6 +254,19 @@ protected function pipes()
245254
return $this->pipes;
246255
}
247256

257+
/**
258+
* Execute each pipeline step within a database transaction.
259+
*
260+
* @param bool $withinTransactions
261+
* @return $this
262+
*/
263+
public function withinTransactions(bool $withinTransactions = true)
264+
{
265+
$this->withinTransactions = $withinTransactions;
266+
267+
return $this;
268+
}
269+
248270
/**
249271
* Get the container instance.
250272
*

src/Illuminate/Pipeline/composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323
"Illuminate\\Pipeline\\": ""
2424
}
2525
},
26+
"suggest": {
27+
"illuminate/database": "Required to use database transactions (^12.0)."
28+
},
2629
"extra": {
2730
"branch-alias": {
2831
"dev-master": "12.x-dev"
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
<?php
2+
3+
namespace Illuminate\Tests\Pipeline;
4+
5+
use Exception;
6+
use Illuminate\Database\Events\TransactionBeginning;
7+
use Illuminate\Database\Events\TransactionCommitted;
8+
use Illuminate\Database\Events\TransactionRolledBack;
9+
use Illuminate\Support\Facades\Event;
10+
use Illuminate\Support\Facades\Pipeline;
11+
use Orchestra\Testbench\TestCase;
12+
13+
class PipelineTransactionTest extends TestCase
14+
{
15+
public function testPipelineTransaction()
16+
{
17+
Event::fake();
18+
19+
$result = Pipeline::withinTransactions()
20+
->send('some string')
21+
->through([function ($value, $next) {
22+
return $next($value);
23+
}])
24+
->thenReturn();
25+
26+
$this->assertEquals('some string', $result);
27+
Event::assertDispatched(TransactionBeginning::class);
28+
Event::assertDispatched(TransactionCommitted::class);
29+
}
30+
31+
public function testExceptionThrownRollsBackTransaction()
32+
{
33+
Event::fake();
34+
35+
$finallyRan = false;
36+
try {
37+
Pipeline::withinTransactions()
38+
->send('some string')
39+
->through([
40+
function ($value, $next) {
41+
throw new Exception('I was thrown');
42+
},
43+
])
44+
->finally(function () use (&$finallyRan) {
45+
$finallyRan = true;
46+
})
47+
->thenReturn();
48+
$this->fail('No exception was thrown');
49+
} catch (Exception) {
50+
}
51+
52+
$this->assertTrue($finallyRan);
53+
Event::assertDispatched(TransactionBeginning::class);
54+
Event::assertDispatched(TransactionRolledBack::class);
55+
}
56+
}

0 commit comments

Comments
 (0)