diff --git a/src/Database/Concerns/HasPending.php b/src/Database/Concerns/HasPending.php index 34a66544..0a572680 100644 --- a/src/Database/Concerns/HasPending.php +++ b/src/Database/Concerns/HasPending.php @@ -49,13 +49,15 @@ trait HasPending */ public static function createPending(array $attributes = []): Model&Tenant { + $tenant = null; + try { - $tenant = static::create($attributes); + $tenant = static::create(array_merge(static::getPendingAttributes($attributes), $attributes)); event(new CreatingPendingTenant($tenant)); } finally { // Update the pending_since value only after the tenant is created so it's // not marked as pending until after migrations, seeders, etc are run. - $tenant->update([ + $tenant?->update([ 'pending_since' => now()->timestamp, ]); } @@ -65,6 +67,17 @@ trait HasPending return $tenant; } + /** + * Attributes to be set when a pending tenant is initially created. + * + * @param array $attributes The attributes passed to createPending() (will be merged with the returned array) + * @return array + */ + public static function getPendingAttributes(array $attributes): array + { + return []; + } + /** * Pull a pending tenant from the pool or create a new one if the pool is empty. * diff --git a/tests/Etc/Tenant.php b/tests/Etc/Tenant.php index 731a179b..72570c50 100644 --- a/tests/Etc/Tenant.php +++ b/tests/Etc/Tenant.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Stancl\Tenancy\Tests\Etc; +use Closure; use Stancl\Tenancy\Database\Contracts\TenantWithDatabase; use Stancl\Tenancy\Database\Concerns\HasDatabase; use Stancl\Tenancy\Database\Concerns\HasDomains; @@ -16,6 +17,7 @@ use Stancl\Tenancy\Database\Models; class Tenant extends Models\Tenant implements TenantWithDatabase { public static array $extraCustomColumns = []; + public static ?Closure $getPendingAttributesUsing = null; use HasDatabase, HasDomains, HasPending; @@ -23,4 +25,9 @@ class Tenant extends Models\Tenant implements TenantWithDatabase { return array_merge(parent::getCustomColumns(), static::$extraCustomColumns); } + + public static function getPendingAttributes(array $attributes): array + { + return static::$getPendingAttributesUsing ? (static::$getPendingAttributesUsing)($attributes) : []; + } } diff --git a/tests/PendingTenantsTest.php b/tests/PendingTenantsTest.php index 3339baaf..a90aceed 100644 --- a/tests/PendingTenantsTest.php +++ b/tests/PendingTenantsTest.php @@ -2,8 +2,12 @@ declare(strict_types=1); +use Illuminate\Database\QueryException; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Event; +use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Str; use Stancl\Tenancy\Commands\ClearPendingTenants; use Stancl\Tenancy\Commands\CreatePendingTenants; use Stancl\Tenancy\Events\CreatingPendingTenant; @@ -13,6 +17,13 @@ use Stancl\Tenancy\Events\PullingPendingTenant; use Stancl\Tenancy\Tests\Etc\Tenant; use function Stancl\Tenancy\Tests\pest; +beforeEach($cleanup = function () { + Tenant::$extraCustomColumns = []; + Tenant::$getPendingAttributesUsing = null; +}); + +afterEach($cleanup); + test('tenants are correctly identified as pending', function (){ Tenant::createPending(); @@ -191,3 +202,25 @@ test('commands run for pending tenants too if the with pending option is passed' $artisan->assertExitCode(0); }); + +test('pending tenants can have default attributes for non-nullable columns', function (bool $withPendingAttributes) { + Schema::table('tenants', function (Blueprint $table) { + $table->string('slug')->unique(); + }); + + Tenant::$extraCustomColumns = ['slug']; + if ($withPendingAttributes) Tenant::$getPendingAttributesUsing = fn () => [ + 'slug' => Str::random(8), + ]; + + $fn = fn () => Tenant::createPending(); + + // If there are non-nullable custom columns, and createPending() is called + // on its own without any values passed for those columns (as it would be called + // by the tenants:pending-create artisan command), we expect it to fail, unless + // getPendingAttributes() provides default values for those custom columns. + if ($withPendingAttributes) + expect($fn)->not()->toThrow(QueryException::class); + else + expect($fn)->toThrow(QueryException::class); +})->with([true, false]);