-
Notifications
You must be signed in to change notification settings - Fork 11.5k
Closed
Description
Laravel Version
11.45.1
PHP Version
8.3.8
Database Driver & Version
No response
Description
When a job is using the ShouldQueueAfterCommit
contract it will always be dispatched after the commit even when the beforeCommit
method is used.
DB::transaction(function(){
$user = User::create(...);
SendWelcomeEmail::dispatch($user)->beforeCommit();
});
The PR #35422 introduced a beforeCommit
method to override the afterCommit
behaviour.
The PR #48705 added a ShouldQueueAfterCommit
contract but it ignores if the $job->afterCommit is already set to false.
protected function shouldDispatchAfterCommit($job)
{
if ($job instanceof ShouldQueueAfterCommit) {
return true;
}
if (! $job instanceof Closure && is_object($job) && isset($job->afterCommit)) {
return $job->afterCommit;
}
return $this->dispatchAfterCommit ?? false;
}
I think it should still respect the beforeCommit
behaviour even when the ShouldQueueAfterCommit
contract is used.
Could be solved by changing the shouldDispatchAfterCommit
method to:
protected function shouldDispatchAfterCommit($job)
{
if ($job instanceof ShouldQueueAfterCommit) {
return isset($job->afterCommit) && $job->afterCommit === false ? false : true;
}
if (! $job instanceof Closure && is_object($job) && isset($job->afterCommit)) {
return $job->afterCommit;
}
return $this->dispatchAfterCommit ?? false;
}
Steps To Reproduce
This test that I added now fails:
<?php
namespace Illuminate\Tests\Integration\Queue;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
use Illuminate\Database\DatabaseTransactionsManager;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\Bus;
use Mockery as m;
use Orchestra\Testbench\Attributes\WithConfig;
use Orchestra\Testbench\TestCase;
use Throwable;
#[WithConfig('queue.default', 'sqs')]
#[WithConfig('queue.connections.sqs.after_commit', true)]
class QueueConnectionTest extends TestCase
{
protected function tearDown(): void
{
QueueConnectionTestShouldQueueAfterCommitJob::$ran = false;
parent::tearDown();
}
public function testJobWithShouldQueueAfterCommitContractWillGetDispatchedInsideATransactionWhenExplicitlyIndicated()
{
$this->app->singleton('db.transactions', function () {
$transactionManager = m::mock(DatabaseTransactionsManager::class);
$transactionManager->shouldNotReceive('addCallback')->andReturn(null);
$transactionManager->shouldNotReceive('addCallbackForRollback');
return $transactionManager;
});
try {
Bus::dispatch((new QueueConnectionTestShouldQueueAfterCommitJob)->beforeCommit());
} catch (Throwable $exception) {
// This job was dispatched
}
}
}
class QueueConnectionTestShouldQueueAfterCommitJob implements ShouldQueueAfterCommit
{
use Dispatchable, Queueable;
public static $ran = false;
public function handle()
{
static::$ran = true;
}
}
Metadata
Metadata
Assignees
Labels
No labels