refactor: rename for Laravel conventions and better DX

- Rename IsCommenter trait to CanComment, Commenter interface to Commentator
- Move models to Models/ namespace (Comment, Reaction, Attachment, Subscription)
- Rename user_type/user_id polymorphic columns to commenter_type/commenter_id
- Rename Config class to CommentsConfig, update config key tables->table_names
- Rename getCommentName() to getCommentDisplayName() on commentator models
- Add column_names config section for commenter morph customization
- Add table_names config with all 5 tables individually configurable
- Expand translation file with structured i18n groups
- Update all Blade views, Livewire components, events, listeners, and tests
This commit is contained in:
manukminasyan
2026-03-27 14:53:12 +04:00
parent 43b66f60f3
commit fd5bc5271b
62 changed files with 733 additions and 653 deletions

View File

@@ -1,19 +1,29 @@
<?php <?php
declare(strict_types=1);
use App\Models\User; use App\Models\User;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Mentions\DefaultMentionResolver; use Relaticle\Comments\Mentions\DefaultMentionResolver;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Policies\CommentPolicy; use Relaticle\Comments\Policies\CommentPolicy;
return [ return [
'tables' => [
'comments' => 'comments',
],
'models' => [ 'models' => [
'comment' => Comment::class, 'comment' => Comment::class,
], ],
'table_names' => [
'comments' => 'comments',
'reactions' => 'comment_reactions',
'mentions' => 'comment_mentions',
'subscriptions' => 'comment_subscriptions',
'attachments' => 'comment_attachments',
],
'column_names' => [
'commenter_morph' => 'commenter',
],
'commenter' => [ 'commenter' => [
'model' => User::class, 'model' => User::class,
], ],

View File

@@ -3,7 +3,7 @@
namespace Relaticle\Comments\Database\Factories; namespace Relaticle\Comments\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Relaticle\Comments\Comment; use Relaticle\Comments\Models\Comment;
class CommentFactory extends Factory class CommentFactory extends Factory
{ {

View File

@@ -8,10 +8,10 @@ return new class extends Migration
{ {
public function up(): void public function up(): void
{ {
Schema::create('comment_attachments', function (Blueprint $table) { Schema::create(config('comments.table_names.attachments', 'comment_attachments'), function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->string('file_path'); $table->string('file_path');
$table->string('original_name'); $table->string('original_name');

View File

@@ -8,15 +8,15 @@ return new class extends Migration
{ {
public function up(): void public function up(): void
{ {
Schema::create('comment_mentions', function (Blueprint $table) { Schema::create(config('comments.table_names.mentions', 'comment_mentions'), function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->morphs('user'); $table->morphs('commenter');
$table->timestamps(); $table->timestamps();
$table->unique(['comment_id', 'user_id', 'user_type']); $table->unique(['comment_id', 'commenter_id', 'commenter_type']);
}); });
} }
}; };

View File

@@ -8,16 +8,16 @@ return new class extends Migration
{ {
public function up(): void public function up(): void
{ {
Schema::create('comment_reactions', function (Blueprint $table) { Schema::create(config('comments.table_names.reactions', 'comment_reactions'), function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->morphs('user'); $table->morphs('commenter');
$table->string('reaction'); $table->string('reaction');
$table->timestamps(); $table->timestamps();
$table->unique(['comment_id', 'user_id', 'user_type', 'reaction']); $table->unique(['comment_id', 'commenter_id', 'commenter_type', 'reaction']);
}); });
} }
}; };

View File

@@ -8,13 +8,13 @@ return new class extends Migration
{ {
public function up(): void public function up(): void
{ {
Schema::create('comment_subscriptions', function (Blueprint $table) { Schema::create(config('comments.table_names.subscriptions', 'comment_subscriptions'), function (Blueprint $table) {
$table->id(); $table->id();
$table->morphs('commentable'); $table->morphs('commentable');
$table->morphs('user'); $table->morphs('commenter');
$table->timestamp('created_at')->nullable(); $table->timestamp('created_at')->nullable();
$table->unique(['commentable_type', 'commentable_id', 'user_type', 'user_id'], 'comment_subscriptions_unique'); $table->unique(['commentable_type', 'commentable_id', 'commenter_type', 'commenter_id'], 'comment_subscriptions_unique');
}); });
} }
}; };

View File

@@ -8,13 +8,13 @@ return new class extends Migration
{ {
public function up(): void public function up(): void
{ {
Schema::create(config('comments.tables.comments', 'comments'), function (Blueprint $table) { Schema::create(config('comments.table_names.comments', 'comments'), function (Blueprint $table) {
$table->id(); $table->id();
$table->morphs('commentable'); $table->morphs('commentable');
$table->morphs('user'); $table->morphs('commenter');
$table->foreignId('parent_id') $table->foreignId('parent_id')
->nullable() ->nullable()
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->text('body'); $table->text('body');
$table->timestamp('edited_at')->nullable(); $table->timestamp('edited_at')->nullable();

Binary file not shown.

View File

@@ -1,9 +1,58 @@
<?php <?php
return [ return [
'deleted_comment' => 'This comment was deleted.', 'comments' => [
'deleted' => 'This comment was deleted.',
'edited' => 'edited', 'edited' => 'edited',
'load_more' => 'Load more comments',
'no_comments' => 'No comments yet.', 'no_comments' => 'No comments yet.',
'comment_placeholder' => 'Write a comment...', 'placeholder' => 'Write a comment...',
'load_more' => 'Load more comments',
'sort_newest' => 'Newest first',
'sort_oldest' => 'Oldest first',
],
'actions' => [
'reply' => 'Reply',
'edit' => 'Edit',
'delete' => 'Delete',
'cancel' => 'Cancel',
'save' => 'Save',
'submit' => 'Submit',
],
'reactions' => [
'thumbs_up' => 'Thumbs up',
'heart' => 'Heart',
'celebrate' => 'Celebrate',
'laugh' => 'Laugh',
'thinking' => 'Thinking',
'sad' => 'Sad',
'reacted_by' => ':names reacted with :reaction',
'and_others' => 'and :count others',
],
'subscriptions' => [
'subscribe' => 'Subscribe to replies',
'unsubscribe' => 'Unsubscribe from replies',
'subscribed' => 'You will be notified of new replies.',
'unsubscribed' => 'You will no longer be notified.',
],
'mentions' => [
'no_results' => 'No users found',
],
'attachments' => [
'add' => 'Add attachment',
'remove' => 'Remove',
'too_large' => 'File is too large. Maximum size: :max KB.',
'invalid_type' => 'File type not allowed.',
],
'notifications' => [
'reply_subject' => 'New reply to your comment',
'reply_body' => ':name replied to your comment.',
'mention_subject' => 'You were mentioned in a comment',
'mention_body' => ':name mentioned you in a comment.',
],
]; ];

View File

@@ -3,11 +3,11 @@
<div class="shrink-0"> <div class="shrink-0">
@if ($comment->trashed()) @if ($comment->trashed())
<div class="h-8 w-8 rounded-full bg-gray-200 dark:bg-gray-700"></div> <div class="h-8 w-8 rounded-full bg-gray-200 dark:bg-gray-700"></div>
@elseif ($comment->user?->getCommentAvatarUrl()) @elseif ($comment->commenter?->getCommentAvatarUrl())
<img src="{{ $comment->user->getCommentAvatarUrl() }}" alt="{{ $comment->user->getCommentName() }}" class="h-8 w-8 rounded-full object-cover"> <img src="{{ $comment->commenter->getCommentAvatarUrl() }}" alt="{{ $comment->commenter->getCommentDisplayName() }}" class="h-8 w-8 rounded-full object-cover">
@else @else
<div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-100 text-sm font-medium text-primary-700 dark:bg-primary-800 dark:text-primary-300"> <div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-100 text-sm font-medium text-primary-700 dark:bg-primary-800 dark:text-primary-300">
{{ str($comment->user?->getCommentName() ?? '?')->substr(0, 1)->upper() }} {{ str($comment->commenter?->getCommentDisplayName() ?? '?')->substr(0, 1)->upper() }}
</div> </div>
@endif @endif
</div> </div>
@@ -20,7 +20,7 @@
{{-- Header: name + timestamp --}} {{-- Header: name + timestamp --}}
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-sm font-medium text-gray-900 dark:text-gray-100"> <span class="text-sm font-medium text-gray-900 dark:text-gray-100">
{{ $comment->user?->getCommentName() ?? 'Unknown' }} {{ $comment->commenter?->getCommentDisplayName() ?? 'Unknown' }}
</span> </span>
<span class="text-xs text-gray-500 dark:text-gray-400" title="{{ $comment->created_at->format('M j, Y g:i A') }}"> <span class="text-xs text-gray-500 dark:text-gray-400" title="{{ $comment->created_at->format('M j, Y g:i A') }}">
{{ $comment->created_at->diffForHumans() }} {{ $comment->created_at->diffForHumans() }}
@@ -200,14 +200,14 @@
<p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p> <p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p>
@enderror @enderror
@if (\Relaticle\Comments\Config::areAttachmentsEnabled()) @if (\Relaticle\Comments\CommentsConfig::areAttachmentsEnabled())
<div class="mt-2"> <div class="mt-2">
<label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> <label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" /> <path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg> </svg>
Attach files Attach files
<input type="file" wire:model="replyAttachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\Config::getAttachmentAllowedTypes()) }}" /> <input type="file" wire:model="replyAttachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\CommentsConfig::getAttachmentAllowedTypes()) }}" />
</label> </label>
</div> </div>

View File

@@ -1,6 +1,6 @@
<div class="space-y-4" <div class="space-y-4"
@if (!\Relaticle\Comments\Config::isBroadcastingEnabled()) @if (!\Relaticle\Comments\CommentsConfig::isBroadcastingEnabled())
wire:poll.{{ \Relaticle\Comments\Config::getPollingInterval() }} wire:poll.{{ \Relaticle\Comments\CommentsConfig::getPollingInterval() }}
@endif @endif
> >
{{-- Sort toggle --}} {{-- Sort toggle --}}
@@ -59,7 +59,7 @@
{{-- New comment form - only for authorized users --}} {{-- New comment form - only for authorized users --}}
@auth @auth
@can('create', \Relaticle\Comments\Config::getCommentModel()) @can('create', \Relaticle\Comments\CommentsConfig::getCommentModel())
<form wire:submit="addComment" class="relative mt-4" <form wire:submit="addComment" class="relative mt-4"
x-data="{ x-data="{
showMentions: false, showMentions: false,
@@ -152,14 +152,14 @@
<p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p> <p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p>
@enderror @enderror
@if (\Relaticle\Comments\Config::areAttachmentsEnabled()) @if (\Relaticle\Comments\CommentsConfig::areAttachmentsEnabled())
<div class="mt-2"> <div class="mt-2">
<label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"> <label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" /> <path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg> </svg>
Attach files Attach files
<input type="file" wire:model="attachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\Config::getAttachmentAllowedTypes()) }}" /> <input type="file" wire:model="attachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\CommentsConfig::getAttachmentAllowedTypes()) }}" />
</label> </label>
</div> </div>

View File

@@ -25,7 +25,7 @@
{{-- Emoji picker dropdown --}} {{-- Emoji picker dropdown --}}
<div x-show="open" x-cloak @click.outside="open = false" <div x-show="open" x-cloak @click.outside="open = false"
class="absolute bottom-full left-0 z-50 mb-1 flex gap-1 rounded-lg border border-gray-200 bg-white p-2 shadow-lg dark:border-gray-600 dark:bg-gray-800"> class="absolute bottom-full left-0 z-50 mb-1 flex gap-1 rounded-lg border border-gray-200 bg-white p-2 shadow-lg dark:border-gray-600 dark:bg-gray-800">
@foreach (\Relaticle\Comments\Config::getReactionEmojiSet() as $key => $emoji) @foreach (\Relaticle\Comments\CommentsConfig::getReactionEmojiSet() as $key => $emoji)
<button wire:click="toggleReaction('{{ $key }}')" type="button" <button wire:click="toggleReaction('{{ $key }}')" type="button"
class="rounded p-1 text-base hover:bg-gray-100 dark:hover:bg-gray-700" class="rounded p-1 text-base hover:bg-gray-100 dark:hover:bg-gray-700"
title="{{ str_replace('_', ' ', $key) }}"> title="{{ str_replace('_', ' ', $key) }}">

View File

@@ -5,9 +5,10 @@ namespace Relaticle\Comments;
use App\Models\User; use App\Models\User;
use Closure; use Closure;
use Relaticle\Comments\Mentions\DefaultMentionResolver; use Relaticle\Comments\Mentions\DefaultMentionResolver;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Policies\CommentPolicy; use Relaticle\Comments\Policies\CommentPolicy;
class Config class CommentsConfig
{ {
protected static ?Closure $resolveAuthenticatedUser = null; protected static ?Closure $resolveAuthenticatedUser = null;
@@ -23,7 +24,25 @@ class Config
public static function getCommentTable(): string public static function getCommentTable(): string
{ {
return config('comments.tables.comments', 'comments'); return static::getTableName('comments');
}
public static function getTableName(string $table): string
{
$defaults = [
'comments' => 'comments',
'reactions' => 'comment_reactions',
'mentions' => 'comment_mentions',
'subscriptions' => 'comment_subscriptions',
'attachments' => 'comment_attachments',
];
return config("comments.table_names.{$table}", $defaults[$table] ?? $table);
}
public static function getCommenterMorphName(): string
{
return config('comments.column_names.commenter_morph', 'commenter');
} }
public static function getMaxDepth(): int public static function getMaxDepth(): int

View File

@@ -42,20 +42,20 @@ class CommentsServiceProvider extends PackageServiceProvider
public function packageRegistered(): void public function packageRegistered(): void
{ {
Relation::morphMap([ Relation::morphMap([
'comment' => Config::getCommentModel(), 'comment' => CommentsConfig::getCommentModel(),
]); ]);
$this->app->bind( $this->app->bind(
MentionResolver::class, MentionResolver::class,
fn () => new (Config::getMentionResolver()) fn () => new (CommentsConfig::getMentionResolver())
); );
} }
public function packageBooted(): void public function packageBooted(): void
{ {
Gate::policy( Gate::policy(
Config::getCommentModel(), CommentsConfig::getCommentModel(),
Config::getPolicyClass(), CommentsConfig::getPolicyClass(),
); );
Event::listen(CommentCreated::class, SendCommentRepliedNotification::class); Event::listen(CommentCreated::class, SendCommentRepliedNotification::class);

View File

@@ -5,9 +5,9 @@ namespace Relaticle\Comments\Concerns;
use Filament\Models\Contracts\HasAvatar; use Filament\Models\Contracts\HasAvatar;
use Filament\Models\Contracts\HasName; use Filament\Models\Contracts\HasName;
trait IsCommenter trait CanComment
{ {
public function getCommentName(): string public function getCommentDisplayName(): string
{ {
if ($this instanceof HasName) { if ($this instanceof HasName) {
return $this->getFilamentName(); return $this->getFilamentName();

View File

@@ -3,13 +3,13 @@
namespace Relaticle\Comments\Concerns; namespace Relaticle\Comments\Concerns;
use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphMany;
use Relaticle\Comments\Config; use Relaticle\Comments\CommentsConfig;
trait HasComments trait HasComments
{ {
public function comments(): MorphMany public function comments(): MorphMany
{ {
return $this->morphMany(Config::getCommentModel(), 'commentable'); return $this->morphMany(CommentsConfig::getCommentModel(), 'commentable');
} }
public function topLevelComments(): MorphMany public function topLevelComments(): MorphMany

View File

@@ -2,13 +2,13 @@
namespace Relaticle\Comments\Contracts; namespace Relaticle\Comments\Contracts;
interface Commenter interface Commentator
{ {
public function getKey(); public function getKey();
public function getMorphClass(); public function getMorphClass();
public function getCommentName(): string; public function getCommentDisplayName(): string;
public function getCommentAvatarUrl(): ?string; public function getCommentAvatarUrl(): ?string;
} }

View File

@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class CommentCreated implements ShouldBroadcast class CommentCreated implements ShouldBroadcast
{ {
@@ -27,7 +27,7 @@ class CommentCreated implements ShouldBroadcast
/** @return array<int, PrivateChannel> */ /** @return array<int, PrivateChannel> */
public function broadcastOn(): array public function broadcastOn(): array
{ {
$prefix = Config::getBroadcastChannelPrefix(); $prefix = CommentsConfig::getBroadcastChannelPrefix();
return [ return [
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"), new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
@@ -36,7 +36,7 @@ class CommentCreated implements ShouldBroadcast
public function broadcastWhen(): bool public function broadcastWhen(): bool
{ {
return Config::isBroadcastingEnabled(); return CommentsConfig::isBroadcastingEnabled();
} }
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */ /** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */

View File

@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class CommentDeleted implements ShouldBroadcast class CommentDeleted implements ShouldBroadcast
{ {
@@ -27,7 +27,7 @@ class CommentDeleted implements ShouldBroadcast
/** @return array<int, PrivateChannel> */ /** @return array<int, PrivateChannel> */
public function broadcastOn(): array public function broadcastOn(): array
{ {
$prefix = Config::getBroadcastChannelPrefix(); $prefix = CommentsConfig::getBroadcastChannelPrefix();
return [ return [
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"), new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
@@ -36,7 +36,7 @@ class CommentDeleted implements ShouldBroadcast
public function broadcastWhen(): bool public function broadcastWhen(): bool
{ {
return Config::isBroadcastingEnabled(); return CommentsConfig::isBroadcastingEnabled();
} }
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */ /** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */

View File

@@ -7,8 +7,8 @@ use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class CommentReacted implements ShouldBroadcast class CommentReacted implements ShouldBroadcast
{ {
@@ -26,7 +26,7 @@ class CommentReacted implements ShouldBroadcast
/** @return array<int, PrivateChannel> */ /** @return array<int, PrivateChannel> */
public function broadcastOn(): array public function broadcastOn(): array
{ {
$prefix = Config::getBroadcastChannelPrefix(); $prefix = CommentsConfig::getBroadcastChannelPrefix();
return [ return [
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"), new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
@@ -35,7 +35,7 @@ class CommentReacted implements ShouldBroadcast
public function broadcastWhen(): bool public function broadcastWhen(): bool
{ {
return Config::isBroadcastingEnabled(); return CommentsConfig::isBroadcastingEnabled();
} }
/** @return array{comment_id: int|string, reaction: string, action: string} */ /** @return array{comment_id: int|string, reaction: string, action: string} */

View File

@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class CommentUpdated implements ShouldBroadcast class CommentUpdated implements ShouldBroadcast
{ {
@@ -27,7 +27,7 @@ class CommentUpdated implements ShouldBroadcast
/** @return array<int, PrivateChannel> */ /** @return array<int, PrivateChannel> */
public function broadcastOn(): array public function broadcastOn(): array
{ {
$prefix = Config::getBroadcastChannelPrefix(); $prefix = CommentsConfig::getBroadcastChannelPrefix();
return [ return [
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"), new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
@@ -36,7 +36,7 @@ class CommentUpdated implements ShouldBroadcast
public function broadcastWhen(): bool public function broadcastWhen(): bool
{ {
return Config::isBroadcastingEnabled(); return CommentsConfig::isBroadcastingEnabled();
} }
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */ /** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */

View File

@@ -5,7 +5,7 @@ namespace Relaticle\Comments\Events;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels; use Illuminate\Queue\SerializesModels;
use Relaticle\Comments\Comment; use Relaticle\Comments\Models\Comment;
class UserMentioned class UserMentioned
{ {

View File

@@ -3,35 +3,35 @@
namespace Relaticle\Comments\Listeners; namespace Relaticle\Comments\Listeners;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Relaticle\Comments\CommentSubscription; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Notifications\CommentRepliedNotification; use Relaticle\Comments\Notifications\CommentRepliedNotification;
class SendCommentRepliedNotification class SendCommentRepliedNotification
{ {
public function handle(CommentCreated $event): void public function handle(CommentCreated $event): void
{ {
if (! Config::areNotificationsEnabled()) { if (! CommentsConfig::areNotificationsEnabled()) {
return; return;
} }
$comment = $event->comment; $comment = $event->comment;
$commentable = $event->commentable; $commentable = $event->commentable;
if (Config::shouldAutoSubscribe()) { if (CommentsConfig::shouldAutoSubscribe()) {
CommentSubscription::subscribe($commentable, $comment->user); Subscription::subscribe($commentable, $comment->commenter);
} }
if (! $comment->isReply()) { if (! $comment->isReply()) {
return; return;
} }
$subscribers = CommentSubscription::subscribersFor($commentable); $subscribers = Subscription::subscribersFor($commentable);
$recipients = $subscribers->filter(function ($user) use ($comment) { $recipients = $subscribers->filter(function ($user) use ($comment) {
return ! ($user->getMorphClass() === $comment->user->getMorphClass() return ! ($user->getMorphClass() === $comment->commenter->getMorphClass()
&& $user->getKey() === $comment->user->getKey()); && $user->getKey() === $comment->commenter->getKey());
}); });
if ($recipients->isEmpty()) { if ($recipients->isEmpty()) {

View File

@@ -2,33 +2,33 @@
namespace Relaticle\Comments\Listeners; namespace Relaticle\Comments\Listeners;
use Relaticle\Comments\CommentSubscription; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Notifications\UserMentionedNotification; use Relaticle\Comments\Notifications\UserMentionedNotification;
class SendUserMentionedNotification class SendUserMentionedNotification
{ {
public function handle(UserMentioned $event): void public function handle(UserMentioned $event): void
{ {
if (! Config::areNotificationsEnabled()) { if (! CommentsConfig::areNotificationsEnabled()) {
return; return;
} }
$comment = $event->comment; $comment = $event->comment;
$mentionedUser = $event->mentionedUser; $mentionedUser = $event->mentionedUser;
if (Config::shouldAutoSubscribe()) { if (CommentsConfig::shouldAutoSubscribe()) {
CommentSubscription::subscribe($comment->commentable, $mentionedUser); Subscription::subscribe($comment->commentable, $mentionedUser);
} }
$isSelf = $mentionedUser->getMorphClass() === $comment->user->getMorphClass() $isSelf = $mentionedUser->getMorphClass() === $comment->commenter->getMorphClass()
&& $mentionedUser->getKey() === $comment->user->getKey(); && $mentionedUser->getKey() === $comment->commenter->getKey();
if ($isSelf) { if ($isSelf) {
return; return;
} }
$mentionedUser->notify(new UserMentionedNotification($comment, $comment->user)); $mentionedUser->notify(new UserMentionedNotification($comment, $comment->commenter));
} }
} }

View File

@@ -6,13 +6,13 @@ use Illuminate\Contracts\View\View;
use Livewire\Component; use Livewire\Component;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
use Livewire\WithFileUploads; use Livewire\WithFileUploads;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Contracts\MentionResolver; use Relaticle\Comments\Contracts\MentionResolver;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Events\CommentDeleted; use Relaticle\Comments\Events\CommentDeleted;
use Relaticle\Comments\Events\CommentUpdated; use Relaticle\Comments\Events\CommentUpdated;
use Relaticle\Comments\Mentions\MentionParser; use Relaticle\Comments\Mentions\MentionParser;
use Relaticle\Comments\Models\Comment;
class CommentItem extends Component class CommentItem extends Component
{ {
@@ -106,25 +106,25 @@ class CommentItem extends Component
$rules = ['replyBody' => ['required', 'string', 'min:1']]; $rules = ['replyBody' => ['required', 'string', 'min:1']];
if (Config::areAttachmentsEnabled()) { if (CommentsConfig::areAttachmentsEnabled()) {
$maxSize = Config::getAttachmentMaxSize(); $maxSize = CommentsConfig::getAttachmentMaxSize();
$allowedTypes = implode(',', Config::getAttachmentAllowedTypes()); $allowedTypes = implode(',', CommentsConfig::getAttachmentAllowedTypes());
$rules['replyAttachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"]; $rules['replyAttachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"];
} }
$this->validate($rules); $this->validate($rules);
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
$reply = $this->comment->commentable->comments()->create([ $reply = $this->comment->commentable->comments()->create([
'body' => $this->replyBody, 'body' => $this->replyBody,
'parent_id' => $this->comment->id, 'parent_id' => $this->comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
if (Config::areAttachmentsEnabled() && ! empty($this->replyAttachments)) { if (CommentsConfig::areAttachmentsEnabled() && ! empty($this->replyAttachments)) {
$disk = Config::getAttachmentDisk(); $disk = CommentsConfig::getAttachmentDisk();
foreach ($this->replyAttachments as $file) { foreach ($this->replyAttachments as $file) {
$path = $file->store("comments/attachments/{$reply->id}", $disk); $path = $file->store("comments/attachments/{$reply->id}", $disk);
@@ -169,7 +169,7 @@ class CommentItem extends Component
return $resolver->search($query) return $resolver->search($query)
->map(fn ($user) => [ ->map(fn ($user) => [
'id' => $user->getKey(), 'id' => $user->getKey(),
'name' => $user->getCommentName(), 'name' => $user->getCommentDisplayName(),
'avatar_url' => $user->getCommentAvatarUrl(), 'avatar_url' => $user->getCommentAvatarUrl(),
]) ])
->values() ->values()

View File

@@ -9,12 +9,12 @@ use Livewire\Attributes\Computed;
use Livewire\Component; use Livewire\Component;
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
use Livewire\WithFileUploads; use Livewire\WithFileUploads;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\CommentSubscription;
use Relaticle\Comments\Config;
use Relaticle\Comments\Contracts\MentionResolver; use Relaticle\Comments\Contracts\MentionResolver;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Mentions\MentionParser; use Relaticle\Comments\Mentions\MentionParser;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Models\Subscription;
class Comments extends Component class Comments extends Component
{ {
@@ -36,7 +36,7 @@ class Comments extends Component
public function mount(Model $model): void public function mount(Model $model): void
{ {
$this->model = $model; $this->model = $model;
$this->perPage = Config::getPerPage(); $this->perPage = CommentsConfig::getPerPage();
$this->loadedCount = $this->perPage; $this->loadedCount = $this->perPage;
} }
@@ -46,7 +46,7 @@ class Comments extends Component
{ {
return $this->model return $this->model
->topLevelComments() ->topLevelComments()
->with(['user', 'mentions', 'attachments', 'reactions.user', 'replies.user', 'replies.mentions', 'replies.attachments', 'replies.reactions.user']) ->with(['commenter', 'mentions', 'attachments', 'reactions.commenter', 'replies.commenter', 'replies.mentions', 'replies.attachments', 'replies.reactions.commenter'])
->orderBy('created_at', $this->sortDirection) ->orderBy('created_at', $this->sortDirection)
->take($this->loadedCount) ->take($this->loadedCount)
->get(); ->get();
@@ -67,27 +67,27 @@ class Comments extends Component
#[Computed] #[Computed]
public function isSubscribed(): bool public function isSubscribed(): bool
{ {
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
if (! $user) { if (! $user) {
return false; return false;
} }
return CommentSubscription::isSubscribed($this->model, $user); return Subscription::isSubscribed($this->model, $user);
} }
public function toggleSubscription(): void public function toggleSubscription(): void
{ {
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
if (! $user) { if (! $user) {
return; return;
} }
if ($this->isSubscribed) { if ($this->isSubscribed) {
CommentSubscription::unsubscribe($this->model, $user); Subscription::unsubscribe($this->model, $user);
} else { } else {
CommentSubscription::subscribe($this->model, $user); Subscription::subscribe($this->model, $user);
} }
unset($this->isSubscribed); unset($this->isSubscribed);
@@ -97,26 +97,26 @@ class Comments extends Component
{ {
$rules = ['newComment' => ['required', 'string', 'min:1']]; $rules = ['newComment' => ['required', 'string', 'min:1']];
if (Config::areAttachmentsEnabled()) { if (CommentsConfig::areAttachmentsEnabled()) {
$maxSize = Config::getAttachmentMaxSize(); $maxSize = CommentsConfig::getAttachmentMaxSize();
$allowedTypes = implode(',', Config::getAttachmentAllowedTypes()); $allowedTypes = implode(',', CommentsConfig::getAttachmentAllowedTypes());
$rules['attachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"]; $rules['attachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"];
} }
$this->validate($rules); $this->validate($rules);
$this->authorize('create', Config::getCommentModel()); $this->authorize('create', CommentsConfig::getCommentModel());
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
$comment = $this->model->comments()->create([ $comment = $this->model->comments()->create([
'body' => $this->newComment, 'body' => $this->newComment,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
if (Config::areAttachmentsEnabled() && ! empty($this->attachments)) { if (CommentsConfig::areAttachmentsEnabled() && ! empty($this->attachments)) {
$disk = Config::getAttachmentDisk(); $disk = CommentsConfig::getAttachmentDisk();
foreach ($this->attachments as $file) { foreach ($this->attachments as $file) {
$path = $file->store("comments/attachments/{$comment->id}", $disk); $path = $file->store("comments/attachments/{$comment->id}", $disk);
@@ -163,8 +163,8 @@ class Comments extends Component
'commentUpdated' => 'refreshComments', 'commentUpdated' => 'refreshComments',
]; ];
if (Config::isBroadcastingEnabled()) { if (CommentsConfig::isBroadcastingEnabled()) {
$prefix = Config::getBroadcastChannelPrefix(); $prefix = CommentsConfig::getBroadcastChannelPrefix();
$type = $this->model->getMorphClass(); $type = $this->model->getMorphClass();
$id = $this->model->getKey(); $id = $this->model->getKey();
$channel = "echo-private:{$prefix}.{$type}.{$id}"; $channel = "echo-private:{$prefix}.{$type}.{$id}";
@@ -195,7 +195,7 @@ class Comments extends Component
return $resolver->search($query) return $resolver->search($query)
->map(fn ($user) => [ ->map(fn ($user) => [
'id' => $user->getKey(), 'id' => $user->getKey(),
'name' => $user->getCommentName(), 'name' => $user->getCommentDisplayName(),
'avatar_url' => $user->getCommentAvatarUrl(), 'avatar_url' => $user->getCommentAvatarUrl(),
]) ])
->values() ->values()

View File

@@ -5,9 +5,9 @@ namespace Relaticle\Comments\Livewire;
use Illuminate\Contracts\View\View; use Illuminate\Contracts\View\View;
use Livewire\Attributes\Computed; use Livewire\Attributes\Computed;
use Livewire\Component; use Livewire\Component;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Events\CommentReacted; use Relaticle\Comments\Events\CommentReacted;
use Relaticle\Comments\Models\Comment;
class Reactions extends Component class Reactions extends Component
{ {
@@ -22,19 +22,19 @@ class Reactions extends Component
public function toggleReaction(string $reaction): void public function toggleReaction(string $reaction): void
{ {
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
if (! $user) { if (! $user) {
return; return;
} }
if (! in_array($reaction, Config::getAllowedReactions())) { if (! in_array($reaction, CommentsConfig::getAllowedReactions())) {
return; return;
} }
$existing = $this->comment->reactions() $existing = $this->comment->reactions()
->where('user_id', $user->getKey()) ->where('commenter_id', $user->getKey())
->where('user_type', $user->getMorphClass()) ->where('commenter_type', $user->getMorphClass())
->where('reaction', $reaction) ->where('reaction', $reaction)
->first(); ->first();
@@ -44,8 +44,8 @@ class Reactions extends Component
event(new CommentReacted($this->comment, $user, $reaction, 'removed')); event(new CommentReacted($this->comment, $user, $reaction, 'removed'));
} else { } else {
$this->comment->reactions()->create([ $this->comment->reactions()->create([
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => $reaction, 'reaction' => $reaction,
]); ]);
@@ -66,13 +66,13 @@ class Reactions extends Component
#[Computed] #[Computed]
public function reactionSummary(): array public function reactionSummary(): array
{ {
$user = Config::resolveAuthenticatedUser(); $user = CommentsConfig::resolveAuthenticatedUser();
$userId = $user?->getKey(); $userId = $user?->getKey();
$userType = $user?->getMorphClass(); $userType = $user?->getMorphClass();
$reactions = $this->comment->reactions()->with('user')->get(); $reactions = $this->comment->reactions()->with('commenter')->get();
$emojiSet = Config::getReactionEmojiSet(); $emojiSet = CommentsConfig::getReactionEmojiSet();
return $reactions return $reactions
->groupBy('reaction') ->groupBy('reaction')
@@ -81,10 +81,10 @@ class Reactions extends Component
'reaction' => $key, 'reaction' => $key,
'emoji' => $emojiSet[$key] ?? $key, 'emoji' => $emojiSet[$key] ?? $key,
'count' => $group->count(), 'count' => $group->count(),
'names' => $group->pluck('user.name')->filter()->take(3)->values()->all(), 'names' => $group->pluck('commenter.name')->filter()->take(3)->values()->all(),
'total_reactors' => $group->count(), 'total_reactors' => $group->count(),
'reacted_by_user' => $group->contains( 'reacted_by_user' => $group->contains(
fn ($r) => $r->user_id == $userId && $r->user_type === $userType fn ($r) => $r->commenter_id == $userId && $r->commenter_type === $userType
), ),
]; ];
}) })

View File

@@ -4,7 +4,7 @@ namespace Relaticle\Comments\Mentions;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Relaticle\Comments\Config; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Contracts\MentionResolver; use Relaticle\Comments\Contracts\MentionResolver;
class DefaultMentionResolver implements MentionResolver class DefaultMentionResolver implements MentionResolver
@@ -12,18 +12,18 @@ class DefaultMentionResolver implements MentionResolver
/** @return Collection<int, Model> */ /** @return Collection<int, Model> */
public function search(string $query): Collection public function search(string $query): Collection
{ {
$model = Config::getCommenterModel(); $model = CommentsConfig::getCommenterModel();
return $model::query() return $model::query()
->where('name', 'like', "{$query}%") ->where('name', 'like', "{$query}%")
->limit(Config::getMentionMaxResults()) ->limit(CommentsConfig::getMentionMaxResults())
->get(); ->get();
} }
/** @return Collection<int, Model> */ /** @return Collection<int, Model> */
public function resolveByNames(array $names): Collection public function resolveByNames(array $names): Collection
{ {
$model = Config::getCommenterModel(); $model = CommentsConfig::getCommenterModel();
return $model::query() return $model::query()
->whereIn('name', $names) ->whereIn('name', $names)

View File

@@ -3,10 +3,10 @@
namespace Relaticle\Comments\Mentions; namespace Relaticle\Comments\Mentions;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Contracts\MentionResolver; use Relaticle\Comments\Contracts\MentionResolver;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Models\Comment;
class MentionParser class MentionParser
{ {
@@ -33,13 +33,13 @@ class MentionParser
public function syncMentions(Comment $comment): void public function syncMentions(Comment $comment): void
{ {
$newMentionIds = $this->parse($comment->body); $newMentionIds = $this->parse($comment->body);
$existingMentionIds = $comment->mentions()->pluck('comment_mentions.user_id'); $existingMentionIds = $comment->mentions()->pluck('comment_mentions.commenter_id');
$addedIds = $newMentionIds->diff($existingMentionIds); $addedIds = $newMentionIds->diff($existingMentionIds);
$comment->mentions()->sync($newMentionIds->all()); $comment->mentions()->sync($newMentionIds->all());
$commenterModel = Config::getCommenterModel(); $commenterModel = CommentsConfig::getCommenterModel();
$addedIds->each(function ($userId) use ($comment, $commenterModel) { $addedIds->each(function ($userId) use ($comment, $commenterModel) {
$mentionedUser = $commenterModel::find($userId); $mentionedUser = $commenterModel::find($userId);

View File

@@ -1,13 +1,14 @@
<?php <?php
namespace Relaticle\Comments; namespace Relaticle\Comments\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Number; use Illuminate\Support\Number;
use Relaticle\Comments\CommentsConfig;
class CommentAttachment extends Model class Attachment extends Model
{ {
protected $fillable = [ protected $fillable = [
'comment_id', 'comment_id',
@@ -20,12 +21,12 @@ class CommentAttachment extends Model
public function getTable(): string public function getTable(): string
{ {
return 'comment_attachments'; return CommentsConfig::getTableName('attachments');
} }
public function comment(): BelongsTo public function comment(): BelongsTo
{ {
return $this->belongsTo(Config::getCommentModel()); return $this->belongsTo(CommentsConfig::getCommentModel());
} }
public function isImage(): bool public function isImage(): bool

View File

@@ -1,6 +1,6 @@
<?php <?php
namespace Relaticle\Comments; namespace Relaticle\Comments\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Database\Factories\CommentFactory; use Relaticle\Comments\Database\Factories\CommentFactory;
class Comment extends Model class Comment extends Model
@@ -35,14 +36,14 @@ class Comment extends Model
protected $fillable = [ protected $fillable = [
'body', 'body',
'parent_id', 'parent_id',
'user_id', 'commenter_id',
'user_type', 'commenter_type',
'edited_at', 'edited_at',
]; ];
public function getTable(): string public function getTable(): string
{ {
return Config::getCommentTable(); return CommentsConfig::getCommentTable();
} }
/** @return array<string, string> */ /** @return array<string, string> */
@@ -63,39 +64,39 @@ class Comment extends Model
return $this->morphTo(); return $this->morphTo();
} }
public function user(): MorphTo public function commenter(): MorphTo
{ {
return $this->morphTo(); return $this->morphTo();
} }
public function parent(): BelongsTo public function parent(): BelongsTo
{ {
return $this->belongsTo(Config::getCommentModel(), 'parent_id'); return $this->belongsTo(CommentsConfig::getCommentModel(), 'parent_id');
} }
public function replies(): HasMany public function replies(): HasMany
{ {
return $this->hasMany(Config::getCommentModel(), 'parent_id'); return $this->hasMany(CommentsConfig::getCommentModel(), 'parent_id');
} }
public function reactions(): HasMany public function reactions(): HasMany
{ {
return $this->hasMany(CommentReaction::class); return $this->hasMany(Reaction::class);
} }
public function attachments(): HasMany public function attachments(): HasMany
{ {
return $this->hasMany(CommentAttachment::class); return $this->hasMany(Attachment::class);
} }
public function mentions(): MorphToMany public function mentions(): MorphToMany
{ {
return $this->morphedByMany( return $this->morphedByMany(
Config::getCommenterModel(), CommentsConfig::getCommenterModel(),
'user', 'commenter',
'comment_mentions', CommentsConfig::getTableName('mentions'),
'comment_id', 'comment_id',
'user_id', 'commenter_id',
); );
} }
@@ -121,7 +122,7 @@ class Comment extends Model
public function canReply(): bool public function canReply(): bool
{ {
return $this->depth() < Config::getMaxDepth(); return $this->depth() < CommentsConfig::getMaxDepth();
} }
public function depth(): int public function depth(): int
@@ -133,8 +134,8 @@ class Comment extends Model
$comment = $comment->parent; $comment = $comment->parent;
$depth++; $depth++;
if ($depth >= Config::getMaxDepth()) { if ($depth >= CommentsConfig::getMaxDepth()) {
return Config::getMaxDepth(); return CommentsConfig::getMaxDepth();
} }
} }

View File

@@ -1,31 +1,32 @@
<?php <?php
namespace Relaticle\Comments; namespace Relaticle\Comments\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Relaticle\Comments\CommentsConfig;
class CommentReaction extends Model class Reaction extends Model
{ {
protected $fillable = [ protected $fillable = [
'comment_id', 'comment_id',
'user_id', 'commenter_id',
'user_type', 'commenter_type',
'reaction', 'reaction',
]; ];
public function getTable(): string public function getTable(): string
{ {
return 'comment_reactions'; return CommentsConfig::getTableName('reactions');
} }
public function comment(): BelongsTo public function comment(): BelongsTo
{ {
return $this->belongsTo(Config::getCommentModel()); return $this->belongsTo(CommentsConfig::getCommentModel());
} }
public function user(): MorphTo public function commenter(): MorphTo
{ {
return $this->morphTo(); return $this->morphTo();
} }

View File

@@ -1,25 +1,26 @@
<?php <?php
namespace Relaticle\Comments; namespace Relaticle\Comments\Models;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Relaticle\Comments\CommentsConfig;
class CommentSubscription extends Model class Subscription extends Model
{ {
public const UPDATED_AT = null; public const UPDATED_AT = null;
protected $fillable = [ protected $fillable = [
'commentable_type', 'commentable_type',
'commentable_id', 'commentable_id',
'user_type', 'commenter_type',
'user_id', 'commenter_id',
]; ];
public function getTable(): string public function getTable(): string
{ {
return 'comment_subscriptions'; return CommentsConfig::getTableName('subscriptions');
} }
public function commentable(): MorphTo public function commentable(): MorphTo
@@ -27,7 +28,7 @@ class CommentSubscription extends Model
return $this->morphTo(); return $this->morphTo();
} }
public function user(): MorphTo public function commenter(): MorphTo
{ {
return $this->morphTo(); return $this->morphTo();
} }
@@ -37,8 +38,8 @@ class CommentSubscription extends Model
return static::where([ return static::where([
'commentable_type' => $commentable->getMorphClass(), 'commentable_type' => $commentable->getMorphClass(),
'commentable_id' => $commentable->getKey(), 'commentable_id' => $commentable->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
])->exists(); ])->exists();
} }
@@ -47,8 +48,8 @@ class CommentSubscription extends Model
static::firstOrCreate([ static::firstOrCreate([
'commentable_type' => $commentable->getMorphClass(), 'commentable_type' => $commentable->getMorphClass(),
'commentable_id' => $commentable->getKey(), 'commentable_id' => $commentable->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
]); ]);
} }
@@ -57,8 +58,8 @@ class CommentSubscription extends Model
static::where([ static::where([
'commentable_type' => $commentable->getMorphClass(), 'commentable_type' => $commentable->getMorphClass(),
'commentable_id' => $commentable->getKey(), 'commentable_id' => $commentable->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
])->delete(); ])->delete();
} }
@@ -68,6 +69,6 @@ class CommentSubscription extends Model
return static::where([ return static::where([
'commentable_type' => $commentable->getMorphClass(), 'commentable_type' => $commentable->getMorphClass(),
'commentable_id' => $commentable->getKey(), 'commentable_id' => $commentable->getKey(),
])->with('user')->get()->pluck('user')->filter()->values(); ])->with('commenter')->get()->pluck('commenter')->filter()->values();
} }
} }

View File

@@ -5,8 +5,8 @@ namespace Relaticle\Comments\Notifications;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class CommentRepliedNotification extends Notification class CommentRepliedNotification extends Notification
{ {
@@ -15,7 +15,7 @@ class CommentRepliedNotification extends Notification
/** @return array<int, string> */ /** @return array<int, string> */
public function via(mixed $notifiable): array public function via(mixed $notifiable): array
{ {
return Config::getNotificationChannels(); return CommentsConfig::getNotificationChannels();
} }
/** @return array<string, mixed> */ /** @return array<string, mixed> */
@@ -25,14 +25,14 @@ class CommentRepliedNotification extends Notification
'comment_id' => $this->comment->id, 'comment_id' => $this->comment->id,
'commentable_type' => $this->comment->commentable_type, 'commentable_type' => $this->comment->commentable_type,
'commentable_id' => $this->comment->commentable_id, 'commentable_id' => $this->comment->commentable_id,
'commenter_name' => $this->comment->user->getCommentName(), 'commenter_name' => $this->comment->commenter->getCommentDisplayName(),
'body' => Str::limit(strip_tags($this->comment->body), 100), 'body' => Str::limit(strip_tags($this->comment->body), 100),
]; ];
} }
public function toMail(mixed $notifiable): MailMessage public function toMail(mixed $notifiable): MailMessage
{ {
$commenterName = $this->comment->user->getCommentName(); $commenterName = $this->comment->commenter->getCommentDisplayName();
return (new MailMessage) return (new MailMessage)
->subject('New reply to your comment') ->subject('New reply to your comment')

View File

@@ -6,8 +6,8 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification; use Illuminate\Notifications\Notification;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
class UserMentionedNotification extends Notification class UserMentionedNotification extends Notification
{ {
@@ -19,7 +19,7 @@ class UserMentionedNotification extends Notification
/** @return array<int, string> */ /** @return array<int, string> */
public function via(mixed $notifiable): array public function via(mixed $notifiable): array
{ {
return Config::getNotificationChannels(); return CommentsConfig::getNotificationChannels();
} }
/** @return array<string, mixed> */ /** @return array<string, mixed> */
@@ -29,14 +29,14 @@ class UserMentionedNotification extends Notification
'comment_id' => $this->comment->id, 'comment_id' => $this->comment->id,
'commentable_type' => $this->comment->commentable_type, 'commentable_type' => $this->comment->commentable_type,
'commentable_id' => $this->comment->commentable_id, 'commentable_id' => $this->comment->commentable_id,
'mentioner_name' => $this->mentionedBy->getCommentName(), 'mentioner_name' => $this->mentionedBy->getCommentDisplayName(),
'body' => Str::limit(strip_tags($this->comment->body), 100), 'body' => Str::limit(strip_tags($this->comment->body), 100),
]; ];
} }
public function toMail(mixed $notifiable): MailMessage public function toMail(mixed $notifiable): MailMessage
{ {
$mentionerName = $this->mentionedBy->getCommentName(); $mentionerName = $this->mentionedBy->getCommentDisplayName();
return (new MailMessage) return (new MailMessage)
->subject('You were mentioned in a comment') ->subject('You were mentioned in a comment')

View File

@@ -3,7 +3,7 @@
namespace Relaticle\Comments\Policies; namespace Relaticle\Comments\Policies;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Relaticle\Comments\Comment; use Relaticle\Comments\Models\Comment;
class CommentPolicy class CommentPolicy
{ {
@@ -19,14 +19,14 @@ class CommentPolicy
public function update(Authenticatable $user, Comment $comment): bool public function update(Authenticatable $user, Comment $comment): bool
{ {
return $user->getKey() === $comment->user_id return $user->getKey() === $comment->commenter_id
&& $user->getMorphClass() === $comment->user_type; && $user->getMorphClass() === $comment->commenter_type;
} }
public function delete(Authenticatable $user, Comment $comment): bool public function delete(Authenticatable $user, Comment $comment): bool
{ {
return $user->getKey() === $comment->user_id return $user->getKey() === $comment->commenter_id
&& $user->getMorphClass() === $comment->user_type; && $user->getMorphClass() === $comment->commenter_type;
} }
public function reply(Authenticatable $user, Comment $comment): bool public function reply(Authenticatable $user, Comment $comment): bool

View File

@@ -3,11 +3,11 @@
use Illuminate\Http\UploadedFile; use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\CommentAttachment;
use Relaticle\Comments\Config;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Attachment;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -29,7 +29,7 @@ it('creates comment with file attachment via Livewire component', function () {
->assertSet('attachments', []); ->assertSet('attachments', []);
expect(Comment::count())->toBe(1); expect(Comment::count())->toBe(1);
expect(CommentAttachment::count())->toBe(1); expect(Attachment::count())->toBe(1);
}); });
it('stores attachment with correct metadata', function () { it('stores attachment with correct metadata', function () {
@@ -47,7 +47,7 @@ it('stores attachment with correct metadata', function () {
->set('attachments', [$file]) ->set('attachments', [$file])
->call('addComment'); ->call('addComment');
$attachment = CommentAttachment::first(); $attachment = Attachment::first();
$comment = Comment::first(); $comment = Comment::first();
expect($attachment->original_name)->toBe('vacation.jpg') expect($attachment->original_name)->toBe('vacation.jpg')
@@ -73,7 +73,7 @@ it('stores file on configured disk at comments/attachments/{comment_id}/ path',
->set('attachments', [$file]) ->set('attachments', [$file])
->call('addComment'); ->call('addComment');
$attachment = CommentAttachment::first(); $attachment = Attachment::first();
Storage::disk('public')->assertExists($attachment->file_path); Storage::disk('public')->assertExists($attachment->file_path);
expect($attachment->file_path)->toContain("comments/attachments/{$attachment->comment_id}/"); expect($attachment->file_path)->toContain("comments/attachments/{$attachment->comment_id}/");
@@ -88,15 +88,15 @@ it('displays image attachment thumbnail in comment item view', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Image comment</p>', 'body' => '<p>Image comment</p>',
]); ]);
$file = UploadedFile::fake()->image('photo.jpg', 100, 100); $file = UploadedFile::fake()->image('photo.jpg', 100, 100);
$path = $file->store("comments/attachments/{$comment->id}", 'public'); $path = $file->store("comments/attachments/{$comment->id}", 'public');
CommentAttachment::create([ Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => $path, 'file_path' => $path,
'original_name' => 'photo.jpg', 'original_name' => 'photo.jpg',
@@ -123,15 +123,15 @@ it('displays non-image attachment as download link', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>PDF comment</p>', 'body' => '<p>PDF comment</p>',
]); ]);
$file = UploadedFile::fake()->create('document.pdf', 2048, 'application/pdf'); $file = UploadedFile::fake()->create('document.pdf', 2048, 'application/pdf');
$path = $file->store("comments/attachments/{$comment->id}", 'public'); $path = $file->store("comments/attachments/{$comment->id}", 'public');
CommentAttachment::create([ Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => $path, 'file_path' => $path,
'original_name' => 'document.pdf', 'original_name' => 'document.pdf',
@@ -157,7 +157,7 @@ it('rejects file exceeding max size', function () {
$this->actingAs($user); $this->actingAs($user);
$oversizedFile = UploadedFile::fake()->create('big.pdf', Config::getAttachmentMaxSize() + 1, 'application/pdf'); $oversizedFile = UploadedFile::fake()->create('big.pdf', CommentsConfig::getAttachmentMaxSize() + 1, 'application/pdf');
Livewire::test(Comments::class, ['model' => $post]) Livewire::test(Comments::class, ['model' => $post])
->set('newComment', '<p>Oversized file</p>') ->set('newComment', '<p>Oversized file</p>')
@@ -166,7 +166,7 @@ it('rejects file exceeding max size', function () {
->assertHasErrors('attachments.0'); ->assertHasErrors('attachments.0');
expect(Comment::count())->toBe(0); expect(Comment::count())->toBe(0);
expect(CommentAttachment::count())->toBe(0); expect(Attachment::count())->toBe(0);
}); });
it('rejects disallowed file type', function () { it('rejects disallowed file type', function () {
@@ -186,7 +186,7 @@ it('rejects disallowed file type', function () {
->assertHasErrors('attachments.0'); ->assertHasErrors('attachments.0');
expect(Comment::count())->toBe(0); expect(Comment::count())->toBe(0);
expect(CommentAttachment::count())->toBe(0); expect(Attachment::count())->toBe(0);
}); });
it('accepts allowed file types', function () { it('accepts allowed file types', function () {
@@ -206,7 +206,7 @@ it('accepts allowed file types', function () {
->assertHasNoErrors('attachments.0'); ->assertHasNoErrors('attachments.0');
expect(Comment::count())->toBe(1); expect(Comment::count())->toBe(1);
expect(CommentAttachment::count())->toBe(1); expect(Attachment::count())->toBe(1);
}); });
it('hides upload UI when attachments disabled', function () { it('hides upload UI when attachments disabled', function () {
@@ -248,9 +248,9 @@ it('creates comment with multiple file attachments', function () {
->call('addComment'); ->call('addComment');
expect(Comment::count())->toBe(1); expect(Comment::count())->toBe(1);
expect(CommentAttachment::count())->toBe(2); expect(Attachment::count())->toBe(2);
$attachments = CommentAttachment::all(); $attachments = Attachment::all();
expect($attachments->pluck('original_name')->toArray()) expect($attachments->pluck('original_name')->toArray())
->toContain('photo1.jpg') ->toContain('photo1.jpg')
->toContain('notes.pdf'); ->toContain('notes.pdf');
@@ -265,8 +265,8 @@ it('creates reply with file attachment via CommentItem component', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Parent comment</p>', 'body' => '<p>Parent comment</p>',
]); ]);

View File

@@ -2,11 +2,11 @@
use Illuminate\Broadcasting\PrivateChannel; use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Events\CommentDeleted; use Relaticle\Comments\Events\CommentDeleted;
use Relaticle\Comments\Events\CommentReacted; use Relaticle\Comments\Events\CommentReacted;
use Relaticle\Comments\Events\CommentUpdated; use Relaticle\Comments\Events\CommentUpdated;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -17,8 +17,8 @@ it('CommentCreated event implements ShouldBroadcast', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);
@@ -33,8 +33,8 @@ it('CommentUpdated event implements ShouldBroadcast', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentUpdated($comment); $event = new CommentUpdated($comment);
@@ -49,8 +49,8 @@ it('CommentDeleted event implements ShouldBroadcast', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentDeleted($comment); $event = new CommentDeleted($comment);
@@ -65,8 +65,8 @@ it('CommentReacted event implements ShouldBroadcast', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentReacted($comment, $user, 'thumbs_up', 'added'); $event = new CommentReacted($comment, $user, 'thumbs_up', 'added');
@@ -81,8 +81,8 @@ it('broadcastOn returns PrivateChannel with correct channel name', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);
@@ -100,8 +100,8 @@ it('broadcastWhen returns false when broadcasting is disabled', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);
@@ -118,8 +118,8 @@ it('broadcastWhen returns true when broadcasting is enabled', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);
@@ -134,8 +134,8 @@ it('broadcastWith returns array with comment_id for CommentCreated', function ()
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);
@@ -154,8 +154,8 @@ it('broadcastWith returns array with comment_id, reaction, and action for Commen
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentReacted($comment, $user, 'thumbs_up', 'added'); $event = new CommentReacted($comment, $user, 'thumbs_up', 'added');
@@ -176,8 +176,8 @@ it('uses custom channel prefix from config in broadcastOn', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$event = new CommentCreated($comment); $event = new CommentCreated($comment);

View File

@@ -1,8 +1,8 @@
<?php <?php
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\CommentAttachment; use Relaticle\Comments\Models\Attachment;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -13,12 +13,12 @@ it('creates a comment attachment with all metadata fields', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test comment</p>', 'body' => '<p>Test comment</p>',
]); ]);
$attachment = CommentAttachment::create([ $attachment = Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/photo.jpg', 'file_path' => 'comments/attachments/1/photo.jpg',
'original_name' => 'photo.jpg', 'original_name' => 'photo.jpg',
@@ -27,7 +27,7 @@ it('creates a comment attachment with all metadata fields', function () {
'disk' => 'public', 'disk' => 'public',
]); ]);
expect($attachment)->toBeInstanceOf(CommentAttachment::class) expect($attachment)->toBeInstanceOf(Attachment::class)
->and($attachment->file_path)->toBe('comments/attachments/1/photo.jpg') ->and($attachment->file_path)->toBe('comments/attachments/1/photo.jpg')
->and($attachment->original_name)->toBe('photo.jpg') ->and($attachment->original_name)->toBe('photo.jpg')
->and($attachment->mime_type)->toBe('image/jpeg') ->and($attachment->mime_type)->toBe('image/jpeg')
@@ -42,12 +42,12 @@ it('belongs to a comment via comment() relationship', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
$attachment = CommentAttachment::create([ $attachment = Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/test.png', 'file_path' => 'comments/attachments/1/test.png',
'original_name' => 'test.png', 'original_name' => 'test.png',
@@ -67,12 +67,12 @@ it('has attachments() hasMany relationship on Comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
CommentAttachment::create([ Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/file1.png', 'file_path' => 'comments/attachments/1/file1.png',
'original_name' => 'file1.png', 'original_name' => 'file1.png',
@@ -81,7 +81,7 @@ it('has attachments() hasMany relationship on Comment', function () {
'disk' => 'public', 'disk' => 'public',
]); ]);
CommentAttachment::create([ Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/file2.pdf', 'file_path' => 'comments/attachments/1/file2.pdf',
'original_name' => 'file2.pdf', 'original_name' => 'file2.pdf',
@@ -91,7 +91,7 @@ it('has attachments() hasMany relationship on Comment', function () {
]); ]);
expect($comment->attachments)->toHaveCount(2) expect($comment->attachments)->toHaveCount(2)
->and($comment->attachments->first())->toBeInstanceOf(CommentAttachment::class); ->and($comment->attachments->first())->toBeInstanceOf(Attachment::class);
}); });
it('cascade deletes attachments when comment is force deleted', function () { it('cascade deletes attachments when comment is force deleted', function () {
@@ -101,12 +101,12 @@ it('cascade deletes attachments when comment is force deleted', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
CommentAttachment::create([ Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/photo.jpg', 'file_path' => 'comments/attachments/1/photo.jpg',
'original_name' => 'photo.jpg', 'original_name' => 'photo.jpg',
@@ -115,15 +115,15 @@ it('cascade deletes attachments when comment is force deleted', function () {
'disk' => 'public', 'disk' => 'public',
]); ]);
expect(CommentAttachment::where('comment_id', $comment->id)->count())->toBe(1); expect(Attachment::where('comment_id', $comment->id)->count())->toBe(1);
$comment->forceDelete(); $comment->forceDelete();
expect(CommentAttachment::where('comment_id', $comment->id)->count())->toBe(0); expect(Attachment::where('comment_id', $comment->id)->count())->toBe(0);
}); });
it('correctly identifies image and non-image mime types via isImage()', function (string $mimeType, bool $expected) { it('correctly identifies image and non-image mime types via isImage()', function (string $mimeType, bool $expected) {
$attachment = new CommentAttachment(['mime_type' => $mimeType]); $attachment = new Attachment(['mime_type' => $mimeType]);
expect($attachment->isImage())->toBe($expected); expect($attachment->isImage())->toBe($expected);
})->with([ })->with([
@@ -142,12 +142,12 @@ it('formats bytes into human-readable size via formattedSize()', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
$attachment = CommentAttachment::create([ $attachment = Attachment::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'file_path' => 'comments/attachments/1/file.pdf', 'file_path' => 'comments/attachments/1/file.pdf',
'original_name' => 'file.pdf', 'original_name' => 'file.pdf',
@@ -160,15 +160,15 @@ it('formats bytes into human-readable size via formattedSize()', function () {
}); });
it('returns default attachment disk as public', function () { it('returns default attachment disk as public', function () {
expect(Config::getAttachmentDisk())->toBe('public'); expect(CommentsConfig::getAttachmentDisk())->toBe('public');
}); });
it('returns default attachment max size as 10240', function () { it('returns default attachment max size as 10240', function () {
expect(Config::getAttachmentMaxSize())->toBe(10240); expect(CommentsConfig::getAttachmentMaxSize())->toBe(10240);
}); });
it('returns default allowed attachment types', function () { it('returns default allowed attachment types', function () {
$allowedTypes = Config::getAttachmentAllowedTypes(); $allowedTypes = CommentsConfig::getAttachmentAllowedTypes();
expect($allowedTypes)->toBeArray() expect($allowedTypes)->toBeArray()
->toContain('image/jpeg') ->toContain('image/jpeg')
@@ -181,17 +181,17 @@ it('respects custom config overrides for attachment settings', function () {
config(['comments.attachments.max_size' => 5120]); config(['comments.attachments.max_size' => 5120]);
config(['comments.attachments.allowed_types' => ['image/png']]); config(['comments.attachments.allowed_types' => ['image/png']]);
expect(Config::getAttachmentDisk())->toBe('s3') expect(CommentsConfig::getAttachmentDisk())->toBe('s3')
->and(Config::getAttachmentMaxSize())->toBe(5120) ->and(CommentsConfig::getAttachmentMaxSize())->toBe(5120)
->and(Config::getAttachmentAllowedTypes())->toBe(['image/png']); ->and(CommentsConfig::getAttachmentAllowedTypes())->toBe(['image/png']);
}); });
it('reports attachments as enabled by default', function () { it('reports attachments as enabled by default', function () {
expect(Config::areAttachmentsEnabled())->toBeTrue(); expect(CommentsConfig::areAttachmentsEnabled())->toBeTrue();
}); });
it('respects disabled attachments config', function () { it('respects disabled attachments config', function () {
config(['comments.attachments.enabled' => false]); config(['comments.attachments.enabled' => false]);
expect(Config::areAttachmentsEnabled())->toBeFalse(); expect(CommentsConfig::areAttachmentsEnabled())->toBeFalse();
}); });

View File

@@ -2,12 +2,12 @@
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Events\CommentDeleted; use Relaticle\Comments\Events\CommentDeleted;
use Relaticle\Comments\Events\CommentUpdated; use Relaticle\Comments\Events\CommentUpdated;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -38,8 +38,8 @@ it('fires CommentUpdated event when editing a comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
@@ -64,8 +64,8 @@ it('fires CommentDeleted event when deleting a comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -87,8 +87,8 @@ it('fires CommentCreated event when adding a reply', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -119,6 +119,6 @@ it('carries correct comment and commentable in event payload', function () {
Event::assertDispatched(CommentCreated::class, function (CommentCreated $event) use ($post, $user) { Event::assertDispatched(CommentCreated::class, function (CommentCreated $event) use ($post, $user) {
return $event->comment instanceof Comment return $event->comment instanceof Comment
&& $event->commentable->id === $post->id && $event->commentable->id === $post->id
&& $event->comment->user_id === $user->id; && $event->comment->commenter_id === $user->id;
}); });
}); });

View File

@@ -1,8 +1,8 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -13,8 +13,8 @@ it('allows author to start and save edit on their comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Original body</p>', 'body' => '<p>Original body</p>',
]); ]);
@@ -42,8 +42,8 @@ it('marks edited comment with edited indicator', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
@@ -70,8 +70,8 @@ it('prevents non-author from editing a comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Author comment</p>', 'body' => '<p>Author comment</p>',
]); ]);
@@ -89,8 +89,8 @@ it('allows author to delete their own comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -108,8 +108,8 @@ it('preserves replies when parent comment is deleted', function () {
$attrs = [ $attrs = [
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]; ];
$parent = Comment::factory()->create($attrs); $parent = Comment::factory()->create($attrs);
@@ -133,8 +133,8 @@ it('prevents non-author from deleting a comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
]); ]);
$this->actingAs($otherUser); $this->actingAs($otherUser);
@@ -151,8 +151,8 @@ it('allows user to reply to a comment', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -169,7 +169,7 @@ it('allows user to reply to a comment', function () {
expect($reply)->not->toBeNull(); expect($reply)->not->toBeNull();
expect($reply->body)->toBe('<p>My reply</p>'); expect($reply->body)->toBe('<p>My reply</p>');
expect($reply->user_id)->toBe($user->id); expect($reply->commenter_id)->toBe($user->id);
expect($reply->commentable_id)->toBe($post->id); expect($reply->commentable_id)->toBe($post->id);
}); });
@@ -179,8 +179,8 @@ it('respects max depth for replies', function () {
$attrs = [ $attrs = [
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]; ];
config(['comments.threading.max_depth' => 1]); config(['comments.threading.max_depth' => 1]);
@@ -202,8 +202,8 @@ it('resets state when cancelling edit', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Some body</p>', 'body' => '<p>Some body</p>',
]); ]);
@@ -224,8 +224,8 @@ it('resets state when cancelling reply', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -245,14 +245,14 @@ it('loads all replies within a thread eagerly', function () {
$attrs = [ $attrs = [
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]; ];
$parent = Comment::factory()->create($attrs); $parent = Comment::factory()->create($attrs);
Comment::factory()->count(3)->withParent($parent)->create($attrs); Comment::factory()->count(3)->withParent($parent)->create($attrs);
$parentWithReplies = Comment::with('replies.user')->find($parent->id); $parentWithReplies = Comment::with('replies.commenter')->find($parent->id);
$this->actingAs($user); $this->actingAs($user);

View File

@@ -1,9 +1,9 @@
<?php <?php
use Illuminate\Database\QueryException; use Illuminate\Database\QueryException;
use Relaticle\Comments\Comment;
use Relaticle\Comments\CommentReaction;
use Relaticle\Comments\Events\CommentReacted; use Relaticle\Comments\Events\CommentReacted;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Models\Reaction;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -14,15 +14,15 @@ it('belongs to a comment via comment() relationship', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
$reaction = CommentReaction::create([ $reaction = Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
@@ -30,27 +30,27 @@ it('belongs to a comment via comment() relationship', function () {
->and($reaction->comment->id)->toBe($comment->id); ->and($reaction->comment->id)->toBe($comment->id);
}); });
it('belongs to a user via polymorphic user() relationship', function () { it('belongs to a commenter via polymorphic commenter() relationship', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
$reaction = CommentReaction::create([ $reaction = Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'heart', 'reaction' => 'heart',
]); ]);
expect($reaction->user)->toBeInstanceOf(User::class) expect($reaction->commenter)->toBeInstanceOf(User::class)
->and($reaction->user->id)->toBe($user->id); ->and($reaction->commenter->id)->toBe($user->id);
}); });
it('prevents duplicate reactions with unique constraint', function () { it('prevents duplicate reactions with unique constraint', function () {
@@ -60,22 +60,22 @@ it('prevents duplicate reactions with unique constraint', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
expect(fn () => CommentReaction::create([ expect(fn () => Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]))->toThrow(QueryException::class); ]))->toThrow(QueryException::class);
}); });
@@ -87,8 +87,8 @@ it('carries comment, user, reaction key, and action in CommentReacted event', fu
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Test</p>', 'body' => '<p>Test</p>',
]); ]);

View File

@@ -1,8 +1,7 @@
<?php <?php
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\CommentSubscription; use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Config;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -10,98 +9,98 @@ it('has commentable morphTo relationship', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
$subscription = CommentSubscription::create([ $subscription = Subscription::create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
expect($subscription->commentable)->toBeInstanceOf(Post::class) expect($subscription->commentable)->toBeInstanceOf(Post::class)
->and($subscription->commentable->id)->toBe($post->id); ->and($subscription->commentable->id)->toBe($post->id);
}); });
it('has user morphTo relationship', function () { it('has commenter morphTo relationship', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
$subscription = CommentSubscription::create([ $subscription = Subscription::create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
expect($subscription->user)->toBeInstanceOf(User::class) expect($subscription->commenter)->toBeInstanceOf(User::class)
->and($subscription->user->id)->toBe($user->id); ->and($subscription->commenter->id)->toBe($user->id);
}); });
it('returns database as default notification channel', function () { it('returns database as default notification channel', function () {
expect(Config::getNotificationChannels())->toBe(['database']); expect(CommentsConfig::getNotificationChannels())->toBe(['database']);
}); });
it('returns custom channels when configured', function () { it('returns custom channels when configured', function () {
config()->set('comments.notifications.channels', ['database', 'mail']); config()->set('comments.notifications.channels', ['database', 'mail']);
expect(Config::getNotificationChannels())->toBe(['database', 'mail']); expect(CommentsConfig::getNotificationChannels())->toBe(['database', 'mail']);
}); });
it('returns true for shouldAutoSubscribe by default', function () { it('returns true for shouldAutoSubscribe by default', function () {
expect(Config::shouldAutoSubscribe())->toBeTrue(); expect(CommentsConfig::shouldAutoSubscribe())->toBeTrue();
}); });
it('returns false for shouldAutoSubscribe when configured', function () { it('returns false for shouldAutoSubscribe when configured', function () {
config()->set('comments.subscriptions.auto_subscribe', false); config()->set('comments.subscriptions.auto_subscribe', false);
expect(Config::shouldAutoSubscribe())->toBeFalse(); expect(CommentsConfig::shouldAutoSubscribe())->toBeFalse();
}); });
it('checks if user is subscribed to a commentable via isSubscribed()', function () { it('checks if user is subscribed to a commentable via isSubscribed()', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse(); expect(Subscription::isSubscribed($post, $user))->toBeFalse();
CommentSubscription::create([ Subscription::create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue(); expect(Subscription::isSubscribed($post, $user))->toBeTrue();
}); });
it('creates subscription via subscribe() static method', function () { it('creates subscription via subscribe() static method', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue(); expect(Subscription::isSubscribed($post, $user))->toBeTrue();
}); });
it('removes subscription via unsubscribe() static method', function () { it('removes subscription via unsubscribe() static method', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
CommentSubscription::unsubscribe($post, $user); Subscription::unsubscribe($post, $user);
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse(); expect(Subscription::isSubscribed($post, $user))->toBeFalse();
}); });
it('is idempotent when subscribing twice', function () { it('is idempotent when subscribing twice', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
expect(CommentSubscription::where([ expect(Subscription::where([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
])->count())->toBe(1); ])->count())->toBe(1);
}); });

View File

@@ -1,7 +1,7 @@
<?php <?php
use Carbon\Carbon; use Carbon\Carbon;
use Relaticle\Comments\Comment; use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -12,14 +12,14 @@ it('can be created with factory', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($comment)->toBeInstanceOf(Comment::class); expect($comment)->toBeInstanceOf(Comment::class);
expect($comment->body)->toBeString(); expect($comment->body)->toBeString();
expect($comment->commentable_id)->toBe($post->id); expect($comment->commentable_id)->toBe($post->id);
expect($comment->user_id)->toBe($user->id); expect($comment->commenter_id)->toBe($user->id);
}); });
it('belongs to a commentable model via morphTo', function () { it('belongs to a commentable model via morphTo', function () {
@@ -29,27 +29,27 @@ it('belongs to a commentable model via morphTo', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($comment->commentable)->toBeInstanceOf(Post::class); expect($comment->commentable)->toBeInstanceOf(Post::class);
expect($comment->commentable->id)->toBe($post->id); expect($comment->commentable->id)->toBe($post->id);
}); });
it('belongs to a user via morphTo', function () { it('belongs to a commenter via morphTo', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($comment->user)->toBeInstanceOf(User::class); expect($comment->commenter)->toBeInstanceOf(User::class);
expect($comment->user->id)->toBe($user->id); expect($comment->commenter->id)->toBe($user->id);
}); });
it('supports threading with parent and replies', function () { it('supports threading with parent and replies', function () {
@@ -59,15 +59,15 @@ it('supports threading with parent and replies', function () {
$parent = Comment::factory()->create([ $parent = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
$reply = Comment::factory()->withParent($parent)->create([ $reply = Comment::factory()->withParent($parent)->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($reply->parent->id)->toBe($parent->id); expect($reply->parent->id)->toBe($parent->id);
@@ -82,15 +82,15 @@ it('identifies top-level vs reply comments', function () {
$topLevel = Comment::factory()->create([ $topLevel = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
$reply = Comment::factory()->withParent($topLevel)->create([ $reply = Comment::factory()->withParent($topLevel)->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($topLevel->isTopLevel())->toBeTrue(); expect($topLevel->isTopLevel())->toBeTrue();
@@ -105,8 +105,8 @@ it('calculates depth correctly', function () {
$attrs = [ $attrs = [
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]; ];
$level0 = Comment::factory()->create($attrs); $level0 = Comment::factory()->create($attrs);
@@ -124,8 +124,8 @@ it('checks canReply based on max depth', function () {
$attrs = [ $attrs = [
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]; ];
$level0 = Comment::factory()->create($attrs); $level0 = Comment::factory()->create($attrs);
@@ -144,8 +144,8 @@ it('supports soft deletes', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
$comment->delete(); $comment->delete();
@@ -162,8 +162,8 @@ it('tracks edited state', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($comment->isEdited())->toBeFalse(); expect($comment->isEdited())->toBeFalse();
@@ -171,8 +171,8 @@ it('tracks edited state', function () {
$edited = Comment::factory()->edited()->create([ $edited = Comment::factory()->edited()->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($edited->isEdited())->toBeTrue(); expect($edited->isEdited())->toBeTrue();
@@ -185,8 +185,8 @@ it('detects when it has replies', function () {
$attrs = [ $attrs = [
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]; ];
$parent = Comment::factory()->create($attrs); $parent = Comment::factory()->create($attrs);

View File

@@ -1,7 +1,7 @@
<?php <?php
use Relaticle\Comments\Comment;
use Relaticle\Comments\Filament\Actions\CommentsAction; use Relaticle\Comments\Filament\Actions\CommentsAction;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -42,8 +42,8 @@ it('shows badge with comment count when comments exist', function () {
Comment::factory()->count(3)->create([ Comment::factory()->count(3)->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$action = CommentsAction::make('comments'); $action = CommentsAction::make('comments');

View File

@@ -1,8 +1,8 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -33,8 +33,8 @@ it('associates new comment with the authenticated user', function () {
$comment = Comment::first(); $comment = Comment::first();
expect($comment->user_id)->toBe($user->id); expect($comment->commenter_id)->toBe($user->id);
expect($comment->user_type)->toBe($user->getMorphClass()); expect($comment->commenter_type)->toBe($user->getMorphClass());
expect($comment->commentable_id)->toBe($post->id); expect($comment->commentable_id)->toBe($post->id);
expect($comment->commentable_type)->toBe($post->getMorphClass()); expect($comment->commentable_type)->toBe($post->getMorphClass());
}); });
@@ -71,8 +71,8 @@ it('paginates top-level comments with load more', function () {
Comment::factory()->count(12)->create([ Comment::factory()->count(12)->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -99,8 +99,8 @@ it('hides load more button when all comments are loaded', function () {
Comment::factory()->count(5)->create([ Comment::factory()->count(5)->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -130,8 +130,8 @@ it('returns comments in correct sort order via computed property', function () {
$older = Comment::factory()->create([ $older = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Older comment</p>', 'body' => '<p>Older comment</p>',
'created_at' => now()->subHour(), 'created_at' => now()->subHour(),
]); ]);
@@ -139,8 +139,8 @@ it('returns comments in correct sort order via computed property', function () {
$newer = Comment::factory()->create([ $newer = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Newer comment</p>', 'body' => '<p>Newer comment</p>',
'created_at' => now(), 'created_at' => now(),
]); ]);
@@ -167,8 +167,8 @@ it('displays total comment count', function () {
Comment::factory()->count(3)->create([ Comment::factory()->count(3)->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);

View File

@@ -1,7 +1,7 @@
<?php <?php
use Relaticle\Comments\Comment;
use Relaticle\Comments\Filament\Actions\CommentsTableAction; use Relaticle\Comments\Filament\Actions\CommentsTableAction;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -30,8 +30,8 @@ it('shows badge with comment count for the record', function () {
Comment::factory()->count(5)->create([ Comment::factory()->count(5)->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$action = CommentsTableAction::make('comments'); $action = CommentsTableAction::make('comments');

View File

@@ -1,8 +1,8 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -13,8 +13,8 @@ it('strips script tags from comment body on create', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello</p><script>alert(1)</script>', 'body' => '<p>Hello</p><script>alert(1)</script>',
]); ]);
@@ -29,8 +29,8 @@ it('strips event handler attributes from comment body', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<img onerror="alert(1)" src="x">', 'body' => '<img onerror="alert(1)" src="x">',
]); ]);
@@ -45,8 +45,8 @@ it('strips style tags from comment body', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hi</p><style>body{display:none}</style>', 'body' => '<p>Hi</p><style>body{display:none}</style>',
]); ]);
@@ -61,8 +61,8 @@ it('strips iframe tags from comment body', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hi</p><iframe src="evil.com"></iframe>', 'body' => '<p>Hi</p><iframe src="evil.com"></iframe>',
]); ]);
@@ -85,8 +85,8 @@ it('preserves safe HTML formatting through sanitization', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => $safeHtml, 'body' => $safeHtml,
]); ]);
@@ -108,8 +108,8 @@ it('sanitizes comment body on update', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Clean content</p>', 'body' => '<p>Clean content</p>',
]); ]);
@@ -130,8 +130,8 @@ it('strips javascript protocol from link href', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<a href="javascript:alert(1)">click me</a>', 'body' => '<a href="javascript:alert(1)">click me</a>',
]); ]);
@@ -146,8 +146,8 @@ it('strips onclick handler from elements', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<div onclick="alert(1)">click me</div>', 'body' => '<div onclick="alert(1)">click me</div>',
]); ]);

View File

@@ -1,6 +1,6 @@
<?php <?php
use Relaticle\Comments\Comment; use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -11,8 +11,8 @@ it('provides comments relationship on commentable model', function () {
Comment::factory()->count(3)->create([ Comment::factory()->count(3)->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($post->comments)->toHaveCount(3); expect($post->comments)->toHaveCount(3);
@@ -25,8 +25,8 @@ it('provides topLevelComments excluding replies', function () {
$attrs = [ $attrs = [
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]; ];
$topLevel = Comment::factory()->create($attrs); $topLevel = Comment::factory()->create($attrs);
@@ -46,8 +46,8 @@ it('provides comment count', function () {
Comment::factory()->count(5)->create([ Comment::factory()->count(5)->create([
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($post->commentCount())->toBe(5); expect($post->commentCount())->toBe(5);
@@ -61,15 +61,15 @@ it('scopes comments to the specific commentable', function () {
Comment::factory()->count(3)->create([ Comment::factory()->count(3)->create([
'commentable_type' => $post1->getMorphClass(), 'commentable_type' => $post1->getMorphClass(),
'commentable_id' => $post1->id, 'commentable_id' => $post1->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
Comment::factory()->count(2)->create([ Comment::factory()->count(2)->create([
'commentable_type' => $post2->getMorphClass(), 'commentable_type' => $post2->getMorphClass(),
'commentable_id' => $post2->id, 'commentable_id' => $post2->id,
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'user_id' => $user->id, 'commenter_id' => $user->id,
]); ]);
expect($post1->commentCount())->toBe(3); expect($post1->commentCount())->toBe(3);

View File

@@ -1,8 +1,8 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -14,12 +14,12 @@ it('renders mention with styled span', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>@Alice said hi</p>', 'body' => '<p>@Alice said hi</p>',
]); ]);
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]); $comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
$rendered = $comment->renderBodyWithMentions(); $rendered = $comment->renderBodyWithMentions();
@@ -36,13 +36,13 @@ it('renders multiple mentions with styled spans', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>@Alice and @Bob</p>', 'body' => '<p>@Alice and @Bob</p>',
]); ]);
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]); $comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
$comment->mentions()->attach($bob->id, ['user_type' => $bob->getMorphClass()]); $comment->mentions()->attach($bob->id, ['commenter_type' => $bob->getMorphClass()]);
$rendered = $comment->renderBodyWithMentions(); $rendered = $comment->renderBodyWithMentions();
@@ -58,8 +58,8 @@ it('does not style non-mentioned @text', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>@ghost is not here</p>', 'body' => '<p>@ghost is not here</p>',
]); ]);
@@ -76,12 +76,12 @@ it('renders comment-mention class in Livewire component', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello @Alice</p>', 'body' => '<p>Hello @Alice</p>',
]); ]);
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]); $comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
$this->actingAs($user); $this->actingAs($user);

View File

@@ -2,11 +2,11 @@
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Contracts\MentionResolver; use Relaticle\Comments\Contracts\MentionResolver;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Mentions\DefaultMentionResolver; use Relaticle\Comments\Mentions\DefaultMentionResolver;
use Relaticle\Comments\Mentions\MentionParser; use Relaticle\Comments\Mentions\MentionParser;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -62,8 +62,8 @@ it('stores mentions in comment_mentions table on create', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello @john and @jane</p>', 'body' => '<p>Hello @john and @jane</p>',
]); ]);
@@ -85,8 +85,8 @@ it('dispatches UserMentioned event for each mentioned user', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello @john and @jane</p>', 'body' => '<p>Hello @john and @jane</p>',
]); ]);
@@ -116,8 +116,8 @@ it('only dispatches UserMentioned for newly added mentions on update', function
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello @john</p>', 'body' => '<p>Hello @john</p>',
]); ]);
@@ -146,8 +146,8 @@ it('removes mentions from pivot when user removed from body', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello @john and @jane</p>', 'body' => '<p>Hello @john and @jane</p>',
]); ]);

View File

@@ -1,9 +1,9 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -92,8 +92,8 @@ it('stores mentions when editing comment with @mention', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Original comment</p>', 'body' => '<p>Original comment</p>',
]); ]);

View File

@@ -1,12 +1,12 @@
<?php <?php
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Relaticle\Comments\Comment;
use Relaticle\Comments\CommentSubscription;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Listeners\SendCommentRepliedNotification; use Relaticle\Comments\Listeners\SendCommentRepliedNotification;
use Relaticle\Comments\Listeners\SendUserMentionedNotification; use Relaticle\Comments\Listeners\SendUserMentionedNotification;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Notifications\CommentRepliedNotification; use Relaticle\Comments\Notifications\CommentRepliedNotification;
use Relaticle\Comments\Notifications\UserMentionedNotification; use Relaticle\Comments\Notifications\UserMentionedNotification;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
@@ -19,21 +19,21 @@ it('sends CommentRepliedNotification to parent comment author when reply is crea
$replyAuthor = User::factory()->create(); $replyAuthor = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $parentAuthor); Subscription::subscribe($post, $parentAuthor);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $parentAuthor->getKey(), 'commenter_id' => $parentAuthor->getKey(),
'user_type' => $parentAuthor->getMorphClass(), 'commenter_type' => $parentAuthor->getMorphClass(),
'body' => '<p>Parent comment</p>', 'body' => '<p>Parent comment</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $replyAuthor->getKey(), 'commenter_id' => $replyAuthor->getKey(),
'user_type' => $replyAuthor->getMorphClass(), 'commenter_type' => $replyAuthor->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>A reply</p>', 'body' => '<p>A reply</p>',
]); ]);
@@ -51,13 +51,13 @@ it('does NOT send reply notification for top-level comments', function () {
$subscriber = User::factory()->create(); $subscriber = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Top-level comment</p>', 'body' => '<p>Top-level comment</p>',
]); ]);
@@ -73,21 +73,21 @@ it('does NOT send reply notification to the reply author', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>My comment</p>', 'body' => '<p>My comment</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>My own reply</p>', 'body' => '<p>My own reply</p>',
]); ]);
@@ -108,8 +108,8 @@ it('sends UserMentionedNotification when a user is mentioned', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Hey @someone</p>', 'body' => '<p>Hey @someone</p>',
]); ]);
@@ -128,8 +128,8 @@ it('does NOT send mention notification to the comment author', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Hey @myself</p>', 'body' => '<p>Hey @myself</p>',
]); ]);
@@ -146,22 +146,22 @@ it('does NOT send reply notification to unsubscribed user', function () {
$unsubscribedUser = User::factory()->create(); $unsubscribedUser = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $unsubscribedUser); Subscription::subscribe($post, $unsubscribedUser);
CommentSubscription::unsubscribe($post, $unsubscribedUser); Subscription::unsubscribe($post, $unsubscribedUser);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $unsubscribedUser->getKey(), 'commenter_id' => $unsubscribedUser->getKey(),
'user_type' => $unsubscribedUser->getMorphClass(), 'commenter_type' => $unsubscribedUser->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>Reply</p>', 'body' => '<p>Reply</p>',
]); ]);
@@ -178,20 +178,20 @@ it('auto-subscribes the comment author when creating a comment', function () {
$author = User::factory()->create(); $author = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
expect(CommentSubscription::isSubscribed($post, $author))->toBeFalse(); expect(Subscription::isSubscribed($post, $author))->toBeFalse();
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>My comment</p>', 'body' => '<p>My comment</p>',
]); ]);
$listener = new SendCommentRepliedNotification; $listener = new SendCommentRepliedNotification;
$listener->handle(new CommentCreated($comment)); $listener->handle(new CommentCreated($comment));
expect(CommentSubscription::isSubscribed($post, $author))->toBeTrue(); expect(Subscription::isSubscribed($post, $author))->toBeTrue();
}); });
it('suppresses all notifications when notifications are disabled via config', function () { it('suppresses all notifications when notifications are disabled via config', function () {
@@ -203,21 +203,21 @@ it('suppresses all notifications when notifications are disabled via config', fu
$mentioned = User::factory()->create(); $mentioned = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $subscriber->getKey(), 'commenter_id' => $subscriber->getKey(),
'user_type' => $subscriber->getMorphClass(), 'commenter_type' => $subscriber->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>Reply</p>', 'body' => '<p>Reply</p>',
]); ]);

View File

@@ -1,13 +1,12 @@
<?php <?php
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Relaticle\Comments\Comment;
use Relaticle\Comments\CommentSubscription;
use Relaticle\Comments\Config;
use Relaticle\Comments\Events\CommentCreated; use Relaticle\Comments\Events\CommentCreated;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Listeners\SendCommentRepliedNotification; use Relaticle\Comments\Listeners\SendCommentRepliedNotification;
use Relaticle\Comments\Listeners\SendUserMentionedNotification; use Relaticle\Comments\Listeners\SendUserMentionedNotification;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Notifications\CommentRepliedNotification; use Relaticle\Comments\Notifications\CommentRepliedNotification;
use Relaticle\Comments\Notifications\UserMentionedNotification; use Relaticle\Comments\Notifications\UserMentionedNotification;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
@@ -22,8 +21,8 @@ it('returns correct via channels from config for CommentRepliedNotification', fu
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Hello</p>', 'body' => '<p>Hello</p>',
]); ]);
@@ -39,8 +38,8 @@ it('returns toDatabase array with comment data for CommentRepliedNotification',
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>This is a reply body</p>', 'body' => '<p>This is a reply body</p>',
]); ]);
@@ -51,7 +50,7 @@ it('returns toDatabase array with comment data for CommentRepliedNotification',
->and($data['comment_id'])->toBe($comment->id) ->and($data['comment_id'])->toBe($comment->id)
->and($data['commentable_type'])->toBe($post->getMorphClass()) ->and($data['commentable_type'])->toBe($post->getMorphClass())
->and($data['commentable_id'])->toBe($post->id) ->and($data['commentable_id'])->toBe($post->id)
->and($data['commenter_name'])->toBe($user->getCommentName()); ->and($data['commenter_name'])->toBe($user->getCommentDisplayName());
}); });
it('returns correct via channels from config for UserMentionedNotification', function () { it('returns correct via channels from config for UserMentionedNotification', function () {
@@ -64,8 +63,8 @@ it('returns correct via channels from config for UserMentionedNotification', fun
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $mentionedBy->getKey(), 'commenter_id' => $mentionedBy->getKey(),
'user_type' => $mentionedBy->getMorphClass(), 'commenter_type' => $mentionedBy->getMorphClass(),
'body' => '<p>Hey @someone</p>', 'body' => '<p>Hey @someone</p>',
]); ]);
@@ -82,8 +81,8 @@ it('returns toDatabase array with mention data for UserMentionedNotification', f
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $mentioner->getKey(), 'commenter_id' => $mentioner->getKey(),
'user_type' => $mentioner->getMorphClass(), 'commenter_type' => $mentioner->getMorphClass(),
'body' => '<p>Hey @mentioned</p>', 'body' => '<p>Hey @mentioned</p>',
]); ]);
@@ -92,7 +91,7 @@ it('returns toDatabase array with mention data for UserMentionedNotification', f
expect($data)->toHaveKeys(['comment_id', 'commentable_type', 'commentable_id', 'mentioner_name', 'body']) expect($data)->toHaveKeys(['comment_id', 'commentable_type', 'commentable_id', 'mentioner_name', 'body'])
->and($data['comment_id'])->toBe($comment->id) ->and($data['comment_id'])->toBe($comment->id)
->and($data['mentioner_name'])->toBe($mentioner->getCommentName()); ->and($data['mentioner_name'])->toBe($mentioner->getCommentDisplayName());
}); });
it('sends notification to subscribers when reply comment is created', function () { it('sends notification to subscribers when reply comment is created', function () {
@@ -102,21 +101,21 @@ it('sends notification to subscribers when reply comment is created', function (
$subscriber = User::factory()->create(); $subscriber = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $subscriber->getKey(), 'commenter_id' => $subscriber->getKey(),
'user_type' => $subscriber->getMorphClass(), 'commenter_type' => $subscriber->getMorphClass(),
'body' => '<p>Original comment</p>', 'body' => '<p>Original comment</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>Reply to original</p>', 'body' => '<p>Reply to original</p>',
]); ]);
@@ -134,13 +133,13 @@ it('does NOT send notification for top-level comments', function () {
$subscriber = User::factory()->create(); $subscriber = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Top-level comment</p>', 'body' => '<p>Top-level comment</p>',
]); ]);
@@ -156,21 +155,21 @@ it('does NOT notify the reply author themselves', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>My comment</p>', 'body' => '<p>My comment</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>My own reply</p>', 'body' => '<p>My own reply</p>',
]); ]);
@@ -187,20 +186,20 @@ it('auto-subscribes comment author to the thread', function () {
$author = User::factory()->create(); $author = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
expect(CommentSubscription::isSubscribed($post, $author))->toBeFalse(); expect(Subscription::isSubscribed($post, $author))->toBeFalse();
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Comment</p>', 'body' => '<p>Comment</p>',
]); ]);
$listener = new SendCommentRepliedNotification; $listener = new SendCommentRepliedNotification;
$listener->handle(new CommentCreated($comment)); $listener->handle(new CommentCreated($comment));
expect(CommentSubscription::isSubscribed($post, $author))->toBeTrue(); expect(Subscription::isSubscribed($post, $author))->toBeTrue();
}); });
it('only notifies subscribed users for reply notifications', function () { it('only notifies subscribed users for reply notifications', function () {
@@ -211,21 +210,21 @@ it('only notifies subscribed users for reply notifications', function () {
$nonSubscriber = User::factory()->create(); $nonSubscriber = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $subscriber->getKey(), 'commenter_id' => $subscriber->getKey(),
'user_type' => $subscriber->getMorphClass(), 'commenter_type' => $subscriber->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>Reply</p>', 'body' => '<p>Reply</p>',
]); ]);
@@ -247,8 +246,8 @@ it('sends mention notification to mentioned user', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Hey @mentioned</p>', 'body' => '<p>Hey @mentioned</p>',
]); ]);
@@ -268,8 +267,8 @@ it('does NOT send mention notification to the comment author', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Hey @myself</p>', 'body' => '<p>Hey @myself</p>',
]); ]);
@@ -287,13 +286,13 @@ it('auto-subscribes mentioned user to the thread', function () {
$mentioned = User::factory()->create(); $mentioned = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
expect(CommentSubscription::isSubscribed($post, $mentioned))->toBeFalse(); expect(Subscription::isSubscribed($post, $mentioned))->toBeFalse();
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'body' => '<p>Hey @mentioned</p>', 'body' => '<p>Hey @mentioned</p>',
]); ]);
@@ -301,7 +300,7 @@ it('auto-subscribes mentioned user to the thread', function () {
$listener = new SendUserMentionedNotification; $listener = new SendUserMentionedNotification;
$listener->handle($event); $listener->handle($event);
expect(CommentSubscription::isSubscribed($post, $mentioned))->toBeTrue(); expect(Subscription::isSubscribed($post, $mentioned))->toBeTrue();
}); });
it('does not send notifications when notifications are disabled', function () { it('does not send notifications when notifications are disabled', function () {
@@ -313,21 +312,21 @@ it('does not send notifications when notifications are disabled', function () {
$mentioned = User::factory()->create(); $mentioned = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $subscriber); Subscription::subscribe($post, $subscriber);
$parentComment = Comment::factory()->create([ $parentComment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $subscriber->getKey(), 'commenter_id' => $subscriber->getKey(),
'user_type' => $subscriber->getMorphClass(), 'commenter_type' => $subscriber->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
$reply = Comment::factory()->create([ $reply = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $author->getKey(), 'commenter_id' => $author->getKey(),
'user_type' => $author->getMorphClass(), 'commenter_type' => $author->getMorphClass(),
'parent_id' => $parentComment->id, 'parent_id' => $parentComment->id,
'body' => '<p>Reply</p>', 'body' => '<p>Reply</p>',
]); ]);

View File

@@ -1,33 +1,33 @@
<?php <?php
use Relaticle\Comments\Config; use Relaticle\Comments\CommentsConfig;
it('returns false for isBroadcastingEnabled by default', function () { it('returns false for isBroadcastingEnabled by default', function () {
expect(Config::isBroadcastingEnabled())->toBeFalse(); expect(CommentsConfig::isBroadcastingEnabled())->toBeFalse();
}); });
it('returns true for isBroadcastingEnabled when config overridden', function () { it('returns true for isBroadcastingEnabled when config overridden', function () {
config()->set('comments.broadcasting.enabled', true); config()->set('comments.broadcasting.enabled', true);
expect(Config::isBroadcastingEnabled())->toBeTrue(); expect(CommentsConfig::isBroadcastingEnabled())->toBeTrue();
}); });
it('returns comments as default broadcast channel prefix', function () { it('returns comments as default broadcast channel prefix', function () {
expect(Config::getBroadcastChannelPrefix())->toBe('comments'); expect(CommentsConfig::getBroadcastChannelPrefix())->toBe('comments');
}); });
it('returns custom broadcast channel prefix when overridden', function () { it('returns custom broadcast channel prefix when overridden', function () {
config()->set('comments.broadcasting.channel_prefix', 'my-app-comments'); config()->set('comments.broadcasting.channel_prefix', 'my-app-comments');
expect(Config::getBroadcastChannelPrefix())->toBe('my-app-comments'); expect(CommentsConfig::getBroadcastChannelPrefix())->toBe('my-app-comments');
}); });
it('returns 10s as default polling interval', function () { it('returns 10s as default polling interval', function () {
expect(Config::getPollingInterval())->toBe('10s'); expect(CommentsConfig::getPollingInterval())->toBe('10s');
}); });
it('returns custom polling interval when overridden', function () { it('returns custom polling interval when overridden', function () {
config()->set('comments.polling.interval', '30s'); config()->set('comments.polling.interval', '30s');
expect(Config::getPollingInterval())->toBe('30s'); expect(CommentsConfig::getPollingInterval())->toBe('30s');
}); });

View File

@@ -2,11 +2,11 @@
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\CommentReaction;
use Relaticle\Comments\Config;
use Relaticle\Comments\Events\CommentReacted; use Relaticle\Comments\Events\CommentReacted;
use Relaticle\Comments\Livewire\Reactions; use Relaticle\Comments\Livewire\Reactions;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Models\Reaction;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -17,8 +17,8 @@ it('adds a reaction when user clicks an emoji', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -26,10 +26,10 @@ it('adds a reaction when user clicks an emoji', function () {
Livewire::test(Reactions::class, ['comment' => $comment]) Livewire::test(Reactions::class, ['comment' => $comment])
->call('toggleReaction', 'thumbs_up'); ->call('toggleReaction', 'thumbs_up');
expect(CommentReaction::where([ expect(Reaction::where([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
])->exists())->toBeTrue(); ])->exists())->toBeTrue();
}); });
@@ -41,14 +41,14 @@ it('removes a reaction when toggling same emoji', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
@@ -57,10 +57,10 @@ it('removes a reaction when toggling same emoji', function () {
Livewire::test(Reactions::class, ['comment' => $comment]) Livewire::test(Reactions::class, ['comment' => $comment])
->call('toggleReaction', 'thumbs_up'); ->call('toggleReaction', 'thumbs_up');
expect(CommentReaction::where([ expect(Reaction::where([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
])->exists())->toBeFalse(); ])->exists())->toBeFalse();
}); });
@@ -74,8 +74,8 @@ it('fires CommentReacted event with added action', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -98,14 +98,14 @@ it('fires CommentReacted event with removed action', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'heart', 'reaction' => 'heart',
]); ]);
@@ -133,35 +133,35 @@ it('returns correct reaction summary with counts', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user1->getKey(), 'commenter_id' => $user1->getKey(),
'user_type' => $user1->getMorphClass(), 'commenter_type' => $user1->getMorphClass(),
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user1->getKey(), 'commenter_id' => $user1->getKey(),
'user_type' => $user1->getMorphClass(), 'commenter_type' => $user1->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user2->getKey(), 'commenter_id' => $user2->getKey(),
'user_type' => $user2->getMorphClass(), 'commenter_type' => $user2->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user3->getKey(), 'commenter_id' => $user3->getKey(),
'user_type' => $user3->getMorphClass(), 'commenter_type' => $user3->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user1->getKey(), 'commenter_id' => $user1->getKey(),
'user_type' => $user1->getMorphClass(), 'commenter_type' => $user1->getMorphClass(),
'reaction' => 'heart', 'reaction' => 'heart',
]); ]);
@@ -187,14 +187,14 @@ it('requires authentication to react', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
Livewire::test(Reactions::class, ['comment' => $comment]) Livewire::test(Reactions::class, ['comment' => $comment])
->call('toggleReaction', 'thumbs_up'); ->call('toggleReaction', 'thumbs_up');
expect(CommentReaction::count())->toBe(0); expect(Reaction::count())->toBe(0);
}); });
it('allows multiple reaction types from same user', function () { it('allows multiple reaction types from same user', function () {
@@ -204,8 +204,8 @@ it('allows multiple reaction types from same user', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -215,17 +215,17 @@ it('allows multiple reaction types from same user', function () {
$component->call('toggleReaction', 'thumbs_up'); $component->call('toggleReaction', 'thumbs_up');
$component->call('toggleReaction', 'heart'); $component->call('toggleReaction', 'heart');
expect(CommentReaction::where([ expect(Reaction::where([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
])->exists())->toBeTrue(); ])->exists())->toBeTrue();
expect(CommentReaction::where([ expect(Reaction::where([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'reaction' => 'heart', 'reaction' => 'heart',
])->exists())->toBeTrue(); ])->exists())->toBeTrue();
}); });
@@ -238,8 +238,8 @@ it('allows same reaction from multiple users', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user1->getKey(), 'commenter_id' => $user1->getKey(),
'user_type' => $user1->getMorphClass(), 'commenter_type' => $user1->getMorphClass(),
]); ]);
$this->actingAs($user1); $this->actingAs($user1);
@@ -250,7 +250,7 @@ it('allows same reaction from multiple users', function () {
Livewire::test(Reactions::class, ['comment' => $comment]) Livewire::test(Reactions::class, ['comment' => $comment])
->call('toggleReaction', 'thumbs_up'); ->call('toggleReaction', 'thumbs_up');
expect(CommentReaction::where([ expect(Reaction::where([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
])->count())->toBe(2); ])->count())->toBe(2);
@@ -263,8 +263,8 @@ it('rejects invalid reaction keys', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -272,7 +272,7 @@ it('rejects invalid reaction keys', function () {
Livewire::test(Reactions::class, ['comment' => $comment]) Livewire::test(Reactions::class, ['comment' => $comment])
->call('toggleReaction', 'invalid_emoji'); ->call('toggleReaction', 'invalid_emoji');
expect(CommentReaction::count())->toBe(0); expect(Reaction::count())->toBe(0);
}); });
it('marks reacted_by_user correctly in summary', function () { it('marks reacted_by_user correctly in summary', function () {
@@ -283,14 +283,14 @@ it('marks reacted_by_user correctly in summary', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $userA->getKey(), 'commenter_id' => $userA->getKey(),
'user_type' => $userA->getMorphClass(), 'commenter_type' => $userA->getMorphClass(),
]); ]);
CommentReaction::create([ Reaction::create([
'comment_id' => $comment->id, 'comment_id' => $comment->id,
'user_id' => $userA->getKey(), 'commenter_id' => $userA->getKey(),
'user_type' => $userA->getMorphClass(), 'commenter_type' => $userA->getMorphClass(),
'reaction' => 'thumbs_up', 'reaction' => 'thumbs_up',
]); ]);
@@ -310,7 +310,7 @@ it('marks reacted_by_user correctly in summary', function () {
}); });
it('returns configured emoji set from config', function () { it('returns configured emoji set from config', function () {
$emojiSet = Config::getReactionEmojiSet(); $emojiSet = CommentsConfig::getReactionEmojiSet();
expect($emojiSet)->toBeArray(); expect($emojiSet)->toBeArray();
expect($emojiSet)->toHaveKey('thumbs_up'); expect($emojiSet)->toHaveKey('thumbs_up');
@@ -322,7 +322,7 @@ it('returns configured emoji set from config', function () {
}); });
it('returns allowed reaction keys from config', function () { it('returns allowed reaction keys from config', function () {
$allowed = Config::getAllowedReactions(); $allowed = CommentsConfig::getAllowedReactions();
expect($allowed)->toBeArray(); expect($allowed)->toBeArray();
expect($allowed)->toContain('thumbs_up'); expect($allowed)->toContain('thumbs_up');

View File

@@ -1,10 +1,10 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config;
use Relaticle\Comments\Livewire\CommentItem; use Relaticle\Comments\Livewire\CommentItem;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -35,8 +35,8 @@ it('pre-fills editBody with existing comment HTML when starting edit', function
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => $originalHtml, 'body' => $originalHtml,
]); ]);
@@ -54,8 +54,8 @@ it('saves edited HTML content through edit form', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Original</p>', 'body' => '<p>Original</p>',
]); ]);
@@ -81,8 +81,8 @@ it('creates reply with rich HTML content', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
]); ]);
$this->actingAs($user); $this->actingAs($user);
@@ -108,8 +108,8 @@ it('renders comment body with fi-prose class', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>Styled comment</p>', 'body' => '<p>Styled comment</p>',
]); ]);
@@ -120,7 +120,7 @@ it('renders comment body with fi-prose class', function () {
}); });
it('returns editor toolbar configuration as nested array', function () { it('returns editor toolbar configuration as nested array', function () {
$toolbar = Config::getEditorToolbar(); $toolbar = CommentsConfig::getEditorToolbar();
expect($toolbar)->toBeArray(); expect($toolbar)->toBeArray();
expect($toolbar)->not->toBeEmpty(); expect($toolbar)->not->toBeEmpty();
@@ -134,7 +134,7 @@ it('uses custom toolbar config when overridden', function () {
['bold', 'italic'], ['bold', 'italic'],
]]); ]]);
$toolbar = Config::getEditorToolbar(); $toolbar = CommentsConfig::getEditorToolbar();
expect($toolbar)->toHaveCount(1); expect($toolbar)->toHaveCount(1);
expect($toolbar[0])->toBe(['bold', 'italic']); expect($toolbar[0])->toBe(['bold', 'italic']);

View File

@@ -1,8 +1,8 @@
<?php <?php
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Relaticle\Comments\Comment; use Relaticle\Comments\CommentsConfig;
use Relaticle\Comments\Config; use Relaticle\Comments\Models\Comment;
it('registers the config file', function () { it('registers the config file', function () {
expect(config('comments'))->toBeArray(); expect(config('comments'))->toBeArray();
@@ -11,15 +11,15 @@ it('registers the config file', function () {
}); });
it('resolves the comment model from config', function () { it('resolves the comment model from config', function () {
expect(Config::getCommentModel())->toBe(Comment::class); expect(CommentsConfig::getCommentModel())->toBe(Comment::class);
}); });
it('resolves the comment table from config', function () { it('resolves the comment table from config', function () {
expect(Config::getCommentTable())->toBe('comments'); expect(CommentsConfig::getCommentTable())->toBe('comments');
}); });
it('resolves max depth from config', function () { it('resolves max depth from config', function () {
expect(Config::getMaxDepth())->toBe(2); expect(CommentsConfig::getMaxDepth())->toBe(2);
}); });
it('registers the morph map for comment', function () { it('registers the morph map for comment', function () {
@@ -32,7 +32,7 @@ it('creates the comments table via migration', function () {
expect(Schema::hasTable('comments'))->toBeTrue(); expect(Schema::hasTable('comments'))->toBeTrue();
expect(Schema::hasColumns('comments', [ expect(Schema::hasColumns('comments', [
'id', 'commentable_type', 'commentable_id', 'id', 'commentable_type', 'commentable_id',
'user_type', 'user_id', 'parent_id', 'body', 'commenter_type', 'commenter_id', 'parent_id', 'body',
'edited_at', 'deleted_at', 'created_at', 'updated_at', 'edited_at', 'deleted_at', 'created_at', 'updated_at',
]))->toBeTrue(); ]))->toBeTrue();
}); });

View File

@@ -1,8 +1,8 @@
<?php <?php
use Livewire\Livewire; use Livewire\Livewire;
use Relaticle\Comments\CommentSubscription;
use Relaticle\Comments\Livewire\Comments; use Relaticle\Comments\Livewire\Comments;
use Relaticle\Comments\Models\Subscription;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -12,35 +12,35 @@ it('subscribes user when toggling from unsubscribed state', function () {
$this->actingAs($user); $this->actingAs($user);
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse(); expect(Subscription::isSubscribed($post, $user))->toBeFalse();
Livewire::test(Comments::class, ['model' => $post]) Livewire::test(Comments::class, ['model' => $post])
->call('toggleSubscription'); ->call('toggleSubscription');
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue(); expect(Subscription::isSubscribed($post, $user))->toBeTrue();
}); });
it('unsubscribes user when toggling from subscribed state', function () { it('unsubscribes user when toggling from subscribed state', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
$this->actingAs($user); $this->actingAs($user);
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue(); expect(Subscription::isSubscribed($post, $user))->toBeTrue();
Livewire::test(Comments::class, ['model' => $post]) Livewire::test(Comments::class, ['model' => $post])
->call('toggleSubscription'); ->call('toggleSubscription');
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse(); expect(Subscription::isSubscribed($post, $user))->toBeFalse();
}); });
it('returns true for isSubscribed computed when user is subscribed', function () { it('returns true for isSubscribed computed when user is subscribed', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
$this->actingAs($user); $this->actingAs($user);
@@ -64,7 +64,7 @@ it('renders Subscribed text for subscribed user', function () {
$user = User::factory()->create(); $user = User::factory()->create();
$post = Post::factory()->create(); $post = Post::factory()->create();
CommentSubscription::subscribe($post, $user); Subscription::subscribe($post, $user);
$this->actingAs($user); $this->actingAs($user);

View File

@@ -1,7 +1,7 @@
<?php <?php
use Relaticle\Comments\Comment;
use Relaticle\Comments\Events\UserMentioned; use Relaticle\Comments\Events\UserMentioned;
use Relaticle\Comments\Models\Comment;
use Relaticle\Comments\Tests\Models\Post; use Relaticle\Comments\Tests\Models\Post;
use Relaticle\Comments\Tests\Models\User; use Relaticle\Comments\Tests\Models\User;
@@ -13,8 +13,8 @@ it('carries correct comment and mentioned user in payload', function () {
$comment = Comment::factory()->create([ $comment = Comment::factory()->create([
'commentable_id' => $post->id, 'commentable_id' => $post->id,
'commentable_type' => $post->getMorphClass(), 'commentable_type' => $post->getMorphClass(),
'user_id' => $user->getKey(), 'commenter_id' => $user->getKey(),
'user_type' => $user->getMorphClass(), 'commenter_type' => $user->getMorphClass(),
'body' => '<p>@john</p>', 'body' => '<p>@john</p>',
]); ]);

View File

@@ -5,14 +5,14 @@ namespace Relaticle\Comments\Tests\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Relaticle\Comments\Concerns\IsCommenter; use Relaticle\Comments\Concerns\CanComment;
use Relaticle\Comments\Contracts\Commenter; use Relaticle\Comments\Contracts\Commentator;
use Relaticle\Comments\Tests\Database\Factories\UserFactory; use Relaticle\Comments\Tests\Database\Factories\UserFactory;
class User extends Authenticatable implements Commenter class User extends Authenticatable implements Commentator
{ {
use CanComment;
use HasFactory; use HasFactory;
use IsCommenter;
use Notifiable; use Notifiable;
protected $table = 'users'; protected $table = 'users';

View File

@@ -48,13 +48,13 @@ abstract class TestCase extends Orchestra
$table->timestamps(); $table->timestamps();
}); });
Schema::create(config('comments.tables.comments', 'comments'), function (Blueprint $table) { Schema::create(config('comments.table_names.comments', 'comments'), function (Blueprint $table) {
$table->id(); $table->id();
$table->morphs('commentable'); $table->morphs('commentable');
$table->morphs('user'); $table->morphs('commenter');
$table->foreignId('parent_id') $table->foreignId('parent_id')
->nullable() ->nullable()
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->text('body'); $table->text('body');
$table->timestamp('edited_at')->nullable(); $table->timestamp('edited_at')->nullable();
@@ -67,30 +67,30 @@ abstract class TestCase extends Orchestra
Schema::create('comment_mentions', function (Blueprint $table) { Schema::create('comment_mentions', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->morphs('user'); $table->morphs('commenter');
$table->timestamps(); $table->timestamps();
$table->unique(['comment_id', 'user_id', 'user_type']); $table->unique(['comment_id', 'commenter_id', 'commenter_type']);
}); });
Schema::create('comment_reactions', function (Blueprint $table) { Schema::create('comment_reactions', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->morphs('user'); $table->morphs('commenter');
$table->string('reaction'); $table->string('reaction');
$table->timestamps(); $table->timestamps();
$table->unique(['comment_id', 'user_id', 'user_type', 'reaction']); $table->unique(['comment_id', 'commenter_id', 'commenter_type', 'reaction']);
}); });
Schema::create('comment_attachments', function (Blueprint $table) { Schema::create('comment_attachments', function (Blueprint $table) {
$table->id(); $table->id();
$table->foreignId('comment_id') $table->foreignId('comment_id')
->constrained(config('comments.tables.comments', 'comments')) ->constrained(config('comments.table_names.comments', 'comments'))
->cascadeOnDelete(); ->cascadeOnDelete();
$table->string('file_path'); $table->string('file_path');
$table->string('original_name'); $table->string('original_name');
@@ -112,10 +112,10 @@ abstract class TestCase extends Orchestra
Schema::create('comment_subscriptions', function (Blueprint $table) { Schema::create('comment_subscriptions', function (Blueprint $table) {
$table->id(); $table->id();
$table->morphs('commentable'); $table->morphs('commentable');
$table->morphs('user'); $table->morphs('commenter');
$table->timestamp('created_at')->nullable(); $table->timestamp('created_at')->nullable();
$table->unique(['commentable_type', 'commentable_id', 'user_type', 'user_id'], 'comment_subscriptions_unique'); $table->unique(['commentable_type', 'commentable_id', 'commenter_type', 'commenter_id'], 'comment_subscriptions_unique');
}); });
} }