Skip to content

Commit 6b0066c

Browse files
committed
Pending tenants refactor (BC break)
- [BC BREAK] Make pullPendingFromPool() $firstOrCreate arg default to false (pullPending() is now a direct alias for pullPendingFromPool() with default $firstOrCreate=true) - Resolve race conditions in pullPendingFromPool() - Make createPending() set pending_since regardless of exceptions - Make pullPending() accept $attributes - Fire PullingPendingTenant from within a DB transaction - Clarify --count arg description for CreatePendingTenants command - Add docblock to PullingPendingTenant with a notice
1 parent 7089efb commit 6b0066c

File tree

3 files changed

+45
-25
lines changed

3 files changed

+45
-25
lines changed

src/Commands/CreatePendingTenants.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
class CreatePendingTenants extends Command
1010
{
11-
protected $signature = 'tenants:pending-create {--count= : The number of pending tenants to be created}';
11+
protected $signature = 'tenants:pending-create {--count= : The number of pending tenants to maintain}';
1212

1313
protected $description = 'Create pending tenants.';
1414

src/Database/Concerns/HasPending.php

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66

77
use Carbon\Carbon;
88
use Illuminate\Database\Eloquent\Model;
9+
use Illuminate\Support\Facades\DB;
910
use Stancl\Tenancy\Contracts\Tenant;
1011
use Stancl\Tenancy\Events\CreatingPendingTenant;
1112
use Stancl\Tenancy\Events\PendingTenantCreated;
1213
use Stancl\Tenancy\Events\PendingTenantPulled;
1314
use Stancl\Tenancy\Events\PullingPendingTenant;
1415

15-
// todo consider adding a method that sets pending_since to null — to flag tenants as not-pending
16-
1716
/**
1817
* @property ?Carbon $pending_since
1918
*
@@ -50,46 +49,62 @@ public function pending(): bool
5049
*/
5150
public static function createPending(array $attributes = []): Model&Tenant
5251
{
53-
$tenant = static::create($attributes);
54-
55-
event(new CreatingPendingTenant($tenant));
56-
57-
// Update the pending_since value only after the tenant is created so it's
58-
// Not marked as pending until finishing running the migrations, seeders, etc.
59-
$tenant->update([
60-
'pending_since' => now()->timestamp,
61-
]);
52+
try {
53+
$tenant = static::create($attributes);
54+
event(new CreatingPendingTenant($tenant));
55+
} finally {
56+
// Update the pending_since value only after the tenant is created so it's
57+
// not marked as pending until after migrations, seeders, etc are run.
58+
$tenant->update([
59+
'pending_since' => now()->timestamp,
60+
]);
61+
}
6262

6363
event(new PendingTenantCreated($tenant));
6464

6565
return $tenant;
6666
}
6767

68-
/** Pull a pending tenant. */
69-
public static function pullPending(): Model&Tenant
68+
/**
69+
* Pull a pending tenant from the pool or create a new one if the pool is empty.
70+
*
71+
* @param array $attributes The attributes to set on the tenant.
72+
*/
73+
public static function pullPending(array $attributes = []): Model&Tenant
7074
{
7175
/** @var Model&Tenant $pendingTenant */
72-
$pendingTenant = static::pullPendingFromPool(true);
76+
$pendingTenant = static::pullPendingFromPool(true, $attributes);
7377

7478
return $pendingTenant;
7579
}
7680

77-
/** Try to pull a tenant from the pool of pending tenants. */
78-
public static function pullPendingFromPool(bool $firstOrCreate = true, array $attributes = []): ?Tenant
81+
/**
82+
* Try to pull a tenant from the pool of pending tenants.
83+
*
84+
* @param bool $firstOrCreate If true, a tenant will be *created* if the pool is empty. Otherwise null is returned.
85+
* @param array $attributes The attributes to set on the tenant.
86+
*/
87+
public static function pullPendingFromPool(bool $firstOrCreate = false, array $attributes = []): ?Tenant
7988
{
80-
/** @var (Model&Tenant)|null $tenant */
81-
$tenant = static::onlyPending()->first();
89+
$tenant = DB::transaction(function () use ($attributes): ?Tenant {
90+
/** @var (Model&Tenant)|null $tenant */
91+
$tenant = static::onlyPending()->first();
92+
93+
if ($tenant !== null) {
94+
event(new PullingPendingTenant($tenant));
95+
$tenant->update(array_merge($attributes, [
96+
'pending_since' => null,
97+
]));
98+
}
99+
100+
return $tenant;
101+
});
82102

83103
if ($tenant === null) {
84104
return $firstOrCreate ? static::create($attributes) : null;
85105
}
86106

87-
event(new PullingPendingTenant($tenant));
88-
89-
$tenant->update(array_merge($attributes, [
90-
'pending_since' => null,
91-
]));
92-
107+
// Only triggered if a tenant that was pulled from the pool is returned
93108
event(new PendingTenantPulled($tenant));
94109

95110
return $tenant;

src/Events/PullingPendingTenant.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@
44

55
namespace Stancl\Tenancy\Events;
66

7+
/**
8+
* Importantly, listeners for this event should not switch tenancy context.
9+
*
10+
* This event is fired from within a database transaction.
11+
*/
712
class PullingPendingTenant extends Contracts\TenantEvent {}

0 commit comments

Comments
 (0)