feat: add post_comments table, model, and factory

This commit is contained in:
Paulo Castellano 2026-04-15 20:08:16 -03:00
parent e1965f9f7c
commit 7f491b2c2c
5 changed files with 159 additions and 2 deletions

View file

@ -4,15 +4,18 @@
namespace App\Models;
use App\DataTransferObjects\MediaItem;
use App\Enums\Post\Status as PostStatus;
use Database\Factories\PostFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
class Post extends Model
{
@ -22,8 +25,9 @@ class Post extends Model
protected $fillable = [
'workspace_id',
'user_id',
'content',
'media',
'status',
'synced',
'scheduled_at',
'published_at',
];
@ -32,12 +36,24 @@ protected function casts(): array
{
return [
'status' => PostStatus::class,
'synced' => 'boolean',
'media' => 'array',
'scheduled_at' => 'datetime',
'published_at' => 'datetime',
];
}
/**
* Get media items as a collection of MediaItem DTOs.
*
* @return Collection<int, MediaItem>
*/
protected function mediaItems(): Attribute
{
return Attribute::make(
get: fn () => collect($this->media ?? [])->map(fn (array $item) => MediaItem::fromArray($item)),
);
}
public function workspace(): BelongsTo
{
return $this->belongsTo(Workspace::class);
@ -53,6 +69,11 @@ public function postPlatforms(): HasMany
return $this->hasMany(PostPlatform::class);
}
public function comments(): HasMany
{
return $this->hasMany(PostComment::class);
}
public function labels(): BelongsToMany
{
return $this->belongsToMany(WorkspaceLabel::class);

View file

@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Database\Factories\PostCommentFactory;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class PostComment extends Model
{
/** @use HasFactory<PostCommentFactory> */
use HasFactory, HasUuids;
protected $fillable = [
'post_id',
'user_id',
'parent_id',
'body',
'reactions',
];
protected function casts(): array
{
return [
'reactions' => 'array',
];
}
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function parent(): BelongsTo
{
return $this->belongsTo(self::class, 'parent_id');
}
public function replies(): HasMany
{
return $this->hasMany(self::class, 'parent_id')->oldest();
}
public function addReaction(string $userId, string $emoji): void
{
$reactions = $this->reactions ?? [];
$reactions = array_filter($reactions, fn (array $r) => ! (data_get($r, 'user_id') === $userId && data_get($r, 'emoji') === $emoji));
if (count($reactions) === count($this->reactions ?? [])) {
$reactions[] = ['user_id' => $userId, 'emoji' => $emoji];
}
$this->update(['reactions' => array_values($reactions)]);
}
}

View file

@ -12,6 +12,7 @@
use App\Models\NotificationPreference;
use App\Models\Plan;
use App\Models\Post;
use App\Models\PostComment;
use App\Models\PostPlatform;
use App\Models\SocialAccount;
use App\Models\Subscription;
@ -95,6 +96,7 @@ protected function configureMorphMap(): void
'plan' => Plan::class,
'notificationPreference' => NotificationPreference::class,
'post' => Post::class,
'postComment' => PostComment::class,
'postPlatform' => PostPlatform::class,
'socialAccount' => SocialAccount::class,
'subscription' => Subscription::class,

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Database\Factories;
use App\Models\Post;
use App\Models\PostComment;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/** @extends Factory<PostComment> */
class PostCommentFactory extends Factory
{
public function definition(): array
{
return [
'post_id' => Post::factory(),
'user_id' => User::factory(),
'body' => $this->faker->sentence(),
'reactions' => [],
];
}
public function reply(PostComment $parent): static
{
return $this->state(fn () => [
'post_id' => $parent->post_id,
'parent_id' => $parent->id,
]);
}
}

View file

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('post_comments', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->uuid('post_id');
$table->uuid('user_id')->nullable();
$table->uuid('parent_id')->nullable();
$table->text('body');
$table->json('reactions')->default('[]');
$table->timestamps();
$table->foreign('post_id')->references('id')->on('posts')->cascadeOnDelete();
$table->foreign('user_id')->references('id')->on('users')->nullOnDelete();
$table->index(['post_id', 'created_at']);
});
Schema::table('post_comments', function (Blueprint $table) {
$table->foreign('parent_id')->references('id')->on('post_comments')->cascadeOnDelete();
});
}
public function down(): void
{
Schema::dropIfExists('post_comments');
}
};