'artwork','id'=>123], ...] * @return Post */ public function createPost( User $user, string $type, string $visibility, ?string $body, array $targets = [], ?array $linkPreview = null, ?array $taggedUsers = null, ?Carbon $publishAt = null, ): Post { $sanitizedBody = $body ? ContentSanitizer::render($body) : null; $status = ($publishAt && $publishAt->isFuture()) ? Post::STATUS_SCHEDULED : Post::STATUS_PUBLISHED; $meta = []; if ($linkPreview && ! empty($linkPreview['url'])) { $meta['link_preview'] = array_intersect_key($linkPreview, array_flip(['url', 'title', 'description', 'image', 'site_name'])); } if ($taggedUsers && count($taggedUsers) > 0) { $meta['tagged_users'] = array_map( fn ($u) => ['id' => (int) $u['id'], 'username' => (string) $u['username'], 'name' => (string) ($u['name'] ?? $u['username'])], array_slice($taggedUsers, 0, 10), ); } return DB::transaction(function () use ($user, $type, $visibility, $sanitizedBody, $targets, $meta, $status, $publishAt) { $post = Post::create([ 'user_id' => $user->id, 'type' => $type, 'visibility' => $visibility, 'body' => $sanitizedBody, 'meta' => $meta ?: null, 'status' => $status, 'publish_at' => $publishAt, ]); foreach ($targets as $target) { PostTarget::create([ 'post_id' => $post->id, 'target_type' => $target['type'], 'target_id' => $target['id'], ]); } // Sync hashtags extracted from the body if ($sanitizedBody) { $this->hashtags->sync($post, $sanitizedBody); } return $post; }); } /** * Update body/visibility of an existing post. */ public function updatePost(Post $post, ?string $body, ?string $visibility): Post { $updates = []; if ($body !== null) { $updates['body'] = ContentSanitizer::render($body); } if ($visibility !== null) { $updates['visibility'] = $visibility; } $post->update($updates); // Re-sync hashtags whenever body changes if (isset($updates['body'])) { $this->hashtags->sync($post, $updates['body']); } return $post->fresh(); } /** * Soft-delete a post (cascades to targets/reactions/comments via DB). */ public function deletePost(Post $post): void { $post->delete(); } }