Why are you using transactions?
They are normally only used when there are multiple steps that all needs to be successfull before they are stored in the database, and I see no exception handling or rollbacks in your code.
Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.
I have a database transaction in a controller as follows:
public function store(StoreRequestRequest $request, StoreRequestAction $action): JsonResource
{
$insertedWorkflowRequest = DB::transaction(function () use ($request, $action) {
$workflowRequest = $action->handle(Auth::user(), $request->validated());
(new StoreStepsAction($workflowRequest))->handle();
return $workflowRequest;
});
return new RequestResource($insertedWorkflowRequest);
}
Here are the actions:
public function handle(User $user, array $attributes): WorkflowRequest
{
Gate::authorize('create', WorkflowRequest::class);
$attributes['status'] = Status::PENDING;
$attributes['priority'] = Priority::LOW;
$request = $user->requests()->create($attributes);
return $request->load('workflow', 'user', 'steps');
}
and
public function handle(): void
{
$steps = WorkflowStep::where('workflow_id', $this->request->workflow_id)->get();
foreach ($steps as $step) {
WorkflowRequestStep::create([
'workflow_request_id' => $this->request->id,
'workflow_step_id' => $step->id,
'approver_id' => $step->approver_id,
'status' => Status::PENDING,
]);
}
}
in the test file, I tested the creation of the first resource and it works fine, but when I test the second step in the database transaction it does not assert any assertions. Based on my search and understanding , once the first transaction happened, it is rolled back immediately and therefore the second one is not tested properly. My question is how to test the database transaction.
Here is my test (only for the first transaction)
test('authenticated user can create a request', function () {
$this->seed([UserSeeder::class, WorkflowSeeder::class]);
$user = User::factory()->create();
$response = $this->actingAs($user)->postJson(route('requests.store'), [
'workflow_id' => Workflow::first()->id,
]);
$response->assertStatus(201);
expect($response['status'])->toBe(Status::PENDING->value);
expect($response['priority'])->toBe(Priority::LOW->value);
expect($response['data'])->toBeNull();
});
What I have done is to create a unit test to test the second action separtely; I could not create a feature test for the transaction.
Here is the unit test
test('making a request make fill the relevant steps', function () {
Role::create(['name' => 'admin']);
$this->seed([UserSeeder::class, WorkflowSeeder::class, WorkflowStepSeeder::class]);
$request = WorkflowRequest::factory()->create();
(new StoreStepsAction($request))->handle();
$steps = WorkflowRequestStep::all();
expect($steps)->not->toBeNull();
});
Please or to participate in this conversation.