*/ use HasFactory, HasUuids; protected $appends = [ 'display_avatar', ]; protected $fillable = [ 'post_id', 'social_account_id', 'enabled', 'platform', 'platform_name', 'platform_username', 'platform_avatar', 'content_type', 'status', 'platform_post_id', 'platform_url', 'error_message', 'error_context', 'published_at', 'meta', 'connection_warning_sent_at', ]; protected function casts(): array { return [ 'enabled' => 'boolean', 'platform' => SocialPlatform::class, 'content_type' => ContentType::class, 'status' => Status::class, 'published_at' => 'datetime', 'meta' => 'array', 'error_context' => 'array', 'connection_warning_sent_at' => 'datetime', ]; } public function post(): BelongsTo { return $this->belongsTo(Post::class); } public function socialAccount(): BelongsTo { return $this->belongsTo(SocialAccount::class); } /** * Only platforms still enabled for publishing — disabled ones are * excluded from PublishPost, so anything else that mirrors publish * eligibility (previews, validation, proactive checks) must too. */ public function scopeEnabled(Builder $query): Builder { return $query->where('post_platforms.enabled', true); } /** * Get display name, falling back to snapshot if account was deleted. */ public function getDisplayNameAttribute(): string { return $this->socialAccount?->accountDisplayName() ?? $this->platform_name ?? $this->platform->label(); } /** * Get username, falling back to snapshot if account was deleted. */ public function getDisplayUsernameAttribute(): ?string { return $this->socialAccount?->username ?? $this->platform_username; } /** * "Facebook Page (@handle)" for emails and in-app notifications. * Username first, then display name (live account or the snapshot * kept on this row). When neither is set — or the account is gone * and there is no snapshot — just the platform name, never "(@)". */ public function notificationLabel(): string { $identifier = $this->display_username ?: $this->socialAccount?->display_name ?: $this->platform_name; if (! filled($identifier) || $identifier === $this->platform->label()) { return $this->platform->label(); } return "{$this->platform->label()} (@{$identifier})"; } /** * Get avatar URL, falling back to snapshot if account was deleted. */ public function getDisplayAvatarAttribute(): ?string { if ($this->socialAccount?->avatar_url) { return $this->socialAccount->avatar_url; } if (! $this->platform_avatar) { return null; } if (str_starts_with($this->platform_avatar, 'http://') || str_starts_with($this->platform_avatar, 'https://')) { return $this->platform_avatar; } if (Storage::disk('public')->exists($this->platform_avatar)) { return Storage::disk('public')->url($this->platform_avatar); } return Storage::url($this->platform_avatar); } protected function platformAvatar(): Attribute { return Attribute::make( get: function (?string $value): ?string { if (! $value) { return null; } if (str_starts_with($value, 'http://') || str_starts_with($value, 'https://')) { return $value; } if (Storage::disk('public')->exists($value)) { return Storage::disk('public')->url($value); } return Storage::url($value); }, ); } public function markAsPublishing(): void { $this->update(['status' => Status::Publishing]); } public function markAsPublished(string $platformPostId, ?string $platformUrl = null): void { $now = now(); $this->update([ 'status' => Status::Published, 'platform_post_id' => $platformPostId, 'platform_url' => $platformUrl, 'published_at' => $now, 'error_message' => null, 'error_context' => null, ]); $this->socialAccount?->update(['last_used_at' => $now]); } public function markAsFailed(string $errorMessage, ?array $errorContext = null): void { $this->update([ 'status' => Status::Failed, 'error_message' => $errorMessage, 'error_context' => $errorContext, 'platform_post_id' => null, 'platform_url' => null, ]); } }