Adds a private workspace channel (WorkspaceChannel, authorised by
membership) so list views can subscribe once and receive events for
every post in the workspace, instead of opening N per-post channels.
New events:
- PostCreated (broadcast on workspace.{id})
- PostDeleted (broadcast on workspace.{id}; carries primitive ids so
it fires after \$post->delete())
- PostPlatformStatusUpdated now broadcasts on both post.{id} and
workspace.{id} so focused views and lists share the same trigger.
All broadcast events migrated to the namespaced 'entity.action'
convention from Laravel's broadcasting docs and switched from
ShouldBroadcastNow to ShouldBroadcast on a dedicated 'broadcasts'
queue (added to the supervisor-1 list in config/horizon.php) to keep
HTTP responses fast:
PostCreated -> post.created
PostDeleted -> post.deleted
PostPlatformStatusUpdated -> post.platform.status.updated
PostCommentCreated -> post.comment.created
NotificationCreated -> notification.created
PostCreationReady -> ai.creation.completed
Drops the SubscriptionCreated event — it was broadcast on
users.{owner_id} but had no listeners (frontend or backend) and the
billing/PostHog flow already handles plan-change tracking elsewhere.
PostPlatformStatusUpdated payload trimmed to {post_id} since every
consumer (Show, Edit, Index) does router.reload({ only: [...] }) and
ignored the rich shape.
95 lines
2.9 KiB
PHP
95 lines
2.9 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Actions\Post;
|
|
|
|
use App\Enums\Post\Status as PostStatus;
|
|
use App\Events\PostCreated;
|
|
use App\Models\Post;
|
|
use App\Models\User;
|
|
use App\Models\Workspace;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class CreatePost
|
|
{
|
|
/**
|
|
* Create a Post with optional platform selection.
|
|
*
|
|
* `platforms[]` enables specific social accounts. Each entry takes
|
|
* `social_account_id` and an optional `content_type` (defaults to the
|
|
* platform's default). Accounts not listed remain disabled but are still
|
|
* created via SyncPostPlatforms so the user can toggle them later in the
|
|
* editor.
|
|
*
|
|
* `label_ids[]` are attached after creation so the same set of UUIDs
|
|
* works for REST, MCP, and web callers.
|
|
*
|
|
* @param array{
|
|
* content?: ?string,
|
|
* media?: array<int, mixed>,
|
|
* date?: ?string,
|
|
* scheduled_at?: ?string,
|
|
* platforms?: array<int, array{social_account_id: string, content_type?: string}>,
|
|
* label_ids?: array<int, string>
|
|
* } $data
|
|
*/
|
|
public static function execute(Workspace $workspace, User $user, array $data): Post
|
|
{
|
|
$scheduledAt = self::resolveScheduledAt($data);
|
|
|
|
$post = DB::transaction(function () use ($workspace, $user, $data, $scheduledAt): Post {
|
|
$post = $workspace->posts()->create([
|
|
'user_id' => $user->id,
|
|
'content' => data_get($data, 'content', ''),
|
|
'media' => data_get($data, 'media', []),
|
|
'status' => PostStatus::Draft,
|
|
'scheduled_at' => $scheduledAt,
|
|
]);
|
|
|
|
SyncPostPlatforms::execute($post);
|
|
|
|
foreach (data_get($data, 'platforms', []) as $platformData) {
|
|
$accountId = data_get($platformData, 'social_account_id');
|
|
if (! $accountId) {
|
|
continue;
|
|
}
|
|
|
|
$updates = ['enabled' => true];
|
|
|
|
if ($contentType = data_get($platformData, 'content_type')) {
|
|
$updates['content_type'] = $contentType;
|
|
}
|
|
|
|
$post->postPlatforms()
|
|
->where('social_account_id', $accountId)
|
|
->update($updates);
|
|
}
|
|
|
|
if ($labelIds = data_get($data, 'label_ids')) {
|
|
$post->labels()->sync($labelIds);
|
|
}
|
|
|
|
return $post;
|
|
});
|
|
|
|
PostCreated::dispatch($post);
|
|
|
|
return $post;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $data
|
|
*/
|
|
private static function resolveScheduledAt(array $data): Carbon
|
|
{
|
|
if ($scheduledAt = data_get($data, 'scheduled_at')) {
|
|
return Carbon::parse($scheduledAt)->utc();
|
|
}
|
|
|
|
$date = data_get($data, 'date') ?: Carbon::now('UTC')->format('Y-m-d');
|
|
|
|
return Carbon::parse($date, 'UTC')->setTime(9, 0)->utc();
|
|
}
|
|
}
|