'string', ]; // ============================================================ // Events / Observers // ============================================================ protected static function booted(): void { static::created(function (Comment $comment) { // Notify if comment is on a Task if ($comment->commentable_type === Task::class) { $task = $comment->commentable; if ($task) { // Notify creator and assignee (except the commenter) $usersToNotify = collect([$task->creator, $task->assignee]) ->unique('id') ->filter(fn ($u) => $u && $u->id !== $comment->user_id); foreach ($usersToNotify as $user) { $user->notify(new TaskCommentNotification($task, $comment)); } } } }); } // ============================================================ // Relationships // ============================================================ public function commentable(): MorphTo { return $this->morphTo(); } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function parent(): BelongsTo { return $this->belongsTo(Comment::class, 'parent_id'); } public function replies(): HasMany { return $this->hasMany(Comment::class, 'parent_id')->orderBy('created_at'); } // ============================================================ // Scopes // ============================================================ public function scopeTopLevel($query) { return $query->whereNull('parent_id'); } public function scopeWithReplies($query) { return $query->with('replies.user'); } // ============================================================ // Accessors // ============================================================ public function getExcerptAttribute(): string { return strlen($this->body) > 150 ? substr($this->body, 0, 150).'...' : $this->body; } }