182 lines
4.8 KiB
PHP
182 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace Relaticle\Comments\Models;
|
|
|
|
use Filament\Forms\Components\RichEditor\MentionProvider;
|
|
use Filament\Forms\Components\RichEditor\RichContentRenderer;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
|
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Support\Str;
|
|
use Relaticle\Comments\CommentsConfig;
|
|
use Relaticle\Comments\Database\Factories\CommentFactory;
|
|
|
|
class Comment extends Model
|
|
{
|
|
use HasFactory;
|
|
use SoftDeletes;
|
|
|
|
protected static function boot(): void
|
|
{
|
|
parent::boot();
|
|
|
|
static::saving(function (self $comment): void {
|
|
$comment->body = Str::sanitizeHtml($comment->body);
|
|
});
|
|
|
|
static::deleting(function (self $comment): void {
|
|
$comment->replies()->each(fn ($reply) => $reply->delete());
|
|
});
|
|
|
|
static::forceDeleting(function (self $comment): void {
|
|
$comment->attachments()->delete();
|
|
$comment->reactions()->delete();
|
|
$comment->mentions()->detach();
|
|
});
|
|
}
|
|
|
|
protected $fillable = [
|
|
'body',
|
|
'parent_id',
|
|
'commenter_id',
|
|
'commenter_type',
|
|
'edited_at',
|
|
];
|
|
|
|
public function getTable(): string
|
|
{
|
|
return CommentsConfig::getCommentTable();
|
|
}
|
|
|
|
/** @return array<string, string> */
|
|
protected function casts(): array
|
|
{
|
|
return [
|
|
'edited_at' => 'datetime',
|
|
];
|
|
}
|
|
|
|
protected static function newFactory(): CommentFactory
|
|
{
|
|
return CommentFactory::new();
|
|
}
|
|
|
|
public function commentable(): MorphTo
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
|
|
public function commenter(): MorphTo
|
|
{
|
|
return $this->morphTo();
|
|
}
|
|
|
|
public function parent(): BelongsTo
|
|
{
|
|
return $this->belongsTo(CommentsConfig::getCommentModel(), 'parent_id');
|
|
}
|
|
|
|
public function replies(): HasMany
|
|
{
|
|
return $this->hasMany(CommentsConfig::getCommentModel(), 'parent_id');
|
|
}
|
|
|
|
public function reactions(): HasMany
|
|
{
|
|
return $this->hasMany(Reaction::class);
|
|
}
|
|
|
|
public function attachments(): HasMany
|
|
{
|
|
return $this->hasMany(Attachment::class);
|
|
}
|
|
|
|
public function mentions(): MorphToMany
|
|
{
|
|
return $this->morphedByMany(
|
|
CommentsConfig::getCommenterModel(),
|
|
'commenter',
|
|
CommentsConfig::getTableName('mentions'),
|
|
'comment_id',
|
|
'commenter_id',
|
|
);
|
|
}
|
|
|
|
public function isReply(): bool
|
|
{
|
|
return $this->parent_id !== null;
|
|
}
|
|
|
|
public function isTopLevel(): bool
|
|
{
|
|
return $this->parent_id === null;
|
|
}
|
|
|
|
public function hasReplies(): bool
|
|
{
|
|
return $this->replies()->exists();
|
|
}
|
|
|
|
public function isEdited(): bool
|
|
{
|
|
return $this->edited_at !== null;
|
|
}
|
|
|
|
public function canReply(): bool
|
|
{
|
|
return $this->depth() < CommentsConfig::getMaxDepth();
|
|
}
|
|
|
|
public function depth(): int
|
|
{
|
|
$depth = 0;
|
|
$maxDepth = CommentsConfig::getMaxDepth();
|
|
$parentId = $this->parent_id;
|
|
|
|
while ($parentId !== null && $depth < $maxDepth) {
|
|
$depth++;
|
|
$parentId = static::where('id', $parentId)->value('parent_id');
|
|
}
|
|
|
|
return $depth;
|
|
}
|
|
|
|
public function renderBodyWithMentions(): string
|
|
{
|
|
$body = $this->body;
|
|
|
|
if ($this->hasRichEditorMentions($body)) {
|
|
return RichContentRenderer::make($body)
|
|
->mentions([
|
|
MentionProvider::make('@')
|
|
->getLabelsUsing(fn (array $ids): array => CommentsConfig::getCommenterModel()::query()
|
|
->whereIn('id', $ids)
|
|
->pluck('name', 'id')
|
|
->all()),
|
|
])
|
|
->toHtml();
|
|
}
|
|
|
|
$mentionNames = $this->mentions->pluck('name')->filter()->unique();
|
|
|
|
foreach ($mentionNames as $name) {
|
|
$escapedName = e($name);
|
|
$styledSpan = '<span class="comment-mention inline rounded bg-primary-50 px-1 font-medium text-primary-700 dark:bg-primary-900/30 dark:text-primary-300">@'.$escapedName.'</span>';
|
|
|
|
$body = str_replace("@{$name}", $styledSpan, $body);
|
|
$body = str_replace("@{$name}", $styledSpan, $body);
|
|
}
|
|
|
|
return $body;
|
|
}
|
|
|
|
protected function hasRichEditorMentions(string $body): bool
|
|
{
|
|
return str_contains($body, 'data-type="mention"') || str_contains($body, '<p>') || str_contains($body, '<br');
|
|
}
|
|
}
|