*/ use HasFactory; protected $table = 'managed_tenant_onboarding_sessions'; /** * @var array */ public const STATE_ALLOWED_KEYS = [ 'entra_tenant_id', 'tenant_id', 'tenant_name', 'environment', 'primary_domain', 'notes', 'provider_connection_id', 'selected_provider_connection_id', 'verification_operation_run_id', 'verification_run_id', 'bootstrap_operation_types', 'bootstrap_operation_runs', 'bootstrap_run_ids', 'connection_recently_updated', ]; protected $guarded = []; protected $casts = [ 'state' => 'array', 'version' => 'integer', 'lifecycle_state' => OnboardingLifecycleState::class, 'current_checkpoint' => OnboardingCheckpoint::class, 'last_completed_checkpoint' => OnboardingCheckpoint::class, 'completed_at' => 'datetime', 'cancelled_at' => 'datetime', ]; /** * @param array|null $value */ public function setStateAttribute(?array $value): void { if ($value === null) { $this->attributes['state'] = null; return; } $allowed = array_intersect_key($value, array_flip(self::STATE_ALLOWED_KEYS)); $encoded = json_encode($allowed, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); $this->attributes['state'] = $encoded !== false ? $encoded : json_encode([], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); } /** * @return BelongsTo */ public function workspace(): BelongsTo { return $this->belongsTo(Workspace::class); } /** * @return BelongsTo */ public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } /** * @return BelongsTo */ public function startedByUser(): BelongsTo { return $this->belongsTo(User::class, 'started_by_user_id'); } /** * @return BelongsTo */ public function updatedByUser(): BelongsTo { return $this->belongsTo(User::class, 'updated_by_user_id'); } /** * @param Builder $query * @return Builder */ public function scopeResumable(Builder $query): Builder { return $query ->whereNull('completed_at') ->whereNull('cancelled_at'); } public function isCompleted(): bool { return $this->completed_at !== null || $this->lifecycleState() === OnboardingLifecycleState::Completed; } public function isCancelled(): bool { return $this->cancelled_at !== null || $this->lifecycleState() === OnboardingLifecycleState::Cancelled; } public function status(): OnboardingDraftStatus { return OnboardingDraftStatus::fromLifecycleState($this->lifecycleState()); } public function isResumable(): bool { return $this->status()->isResumable(); } public function isTerminal(): bool { return $this->lifecycleState()->isTerminal(); } public function lifecycleState(): OnboardingLifecycleState { if ($this->lifecycle_state instanceof OnboardingLifecycleState) { return $this->lifecycle_state; } if (is_string($this->lifecycle_state) && OnboardingLifecycleState::tryFrom($this->lifecycle_state) instanceof OnboardingLifecycleState) { return OnboardingLifecycleState::from($this->lifecycle_state); } if ($this->completed_at !== null) { return OnboardingLifecycleState::Completed; } if ($this->cancelled_at !== null) { return OnboardingLifecycleState::Cancelled; } return OnboardingLifecycleState::Draft; } public function currentCheckpoint(): ?OnboardingCheckpoint { if ($this->current_checkpoint instanceof OnboardingCheckpoint) { return $this->current_checkpoint; } if (is_string($this->current_checkpoint) && OnboardingCheckpoint::tryFrom($this->current_checkpoint) instanceof OnboardingCheckpoint) { return OnboardingCheckpoint::from($this->current_checkpoint); } return OnboardingCheckpoint::fromCurrentStep($this->current_step); } public function lastCompletedCheckpoint(): ?OnboardingCheckpoint { if ($this->last_completed_checkpoint instanceof OnboardingCheckpoint) { return $this->last_completed_checkpoint; } if (is_string($this->last_completed_checkpoint) && OnboardingCheckpoint::tryFrom($this->last_completed_checkpoint) instanceof OnboardingCheckpoint) { return OnboardingCheckpoint::from($this->last_completed_checkpoint); } return null; } public function expectedVersion(): int { return max(1, (int) ($this->version ?? 1)); } }