belongsTo(Board::class)->withDefault(); } public function post() { return $this->belongsTo(Post::class)->withDefault(); } public function user() { return $this->belongsTo(User::class)->withDefault(); } public function commentblame() { return $this->hasMany(CommentBlame::Class); } public function commentDeleted() { return $this->hasMany(CommentDeleted::Class); } public function commentHistory() { return $this->hasMany(CommentHistory::Class); } public function commentLike() { return $this->hasMany(CommentLike::Class); } public function commentMeta() { return $this->hasOne(CommentMeta::Class); } public function getParentIdName(): string { return 'parent_id'; } public function getLftName(): string { return 'lft'; } public function getRgtName(): string { return 'rgt'; } /** * 댓글 조회 */ public function data(SearchData $params): object { $query = $this->query(); $query->select( 'tb_comment.*', 'tb_board.name AS boardName', 'tb_board.code', 'tb_post.subject', 'users.id', 'users.sid', 'users.name', 'users.email' ); if($params->keyword) { switch ($params->field) { case 'tb_comment.id' : case 'tb_comment.post_id' : case 'users.id' : case 'users.sid' : $query->where($params->field, '=', $params->keyword); break; case 'tb_comment.content' : case 'users.name' : case 'users.email' : $query->where($params->field, 'LIKE', "%{$params->keyword}%"); break; } } $query->where('tb_comment.is_delete', '=', 0); if($params->startDate || $params->endDate) { if($params->startDate) { $query->where('tb_comment.created_at', '>=', $params->startDate . ' 00:00:00'); } if($params->endDate) { $query->where('tb_comment.created_at', '<=', $params->endDate . ' 23:59:59'); } } if($params->boardID) { $query->where('tb_comment.board_id', '=', $params->boardID); } $query->join('tb_board', 'tb_board.id', '=', 'tb_comment.board_id'); $query->join('tb_post', 'tb_post.id', '=', 'tb_comment.post_id'); $query->leftJoin('users', 'users.id', '=', 'tb_comment.user_id'); $query->orderByDesc('tb_comment.id'); $list = $query->paginate($params->perPage, ['*'], 'page', $params->page); $total = $this->count(); $rows = $list->count(); return (object)[ 'total' => $total, 'rows' => $rows, 'list' => $list ]; } /** * 댓글 전체 개수 */ public function total(int $postID): int { return $this->where('post_id', $postID)->count(); } /** * 댓글 등록 */ public function register(array $params): Comment { return $this->create($params); } /** * 댓글 수정 */ public function updater(int $commentID, array $params): Comment { return $this->updateOrCreate([$this->primaryKey => $commentID], $params); } /** * 답글 등록 */ public function reply(int $commentID, array $params): Comment { $parent = $this->findParent($commentID); $node = $parent->children()->create($params); $node->depth = ($parent->depth + 1); $node->save(); return $node; } /** * 댓글 조회 */ public function get(int $commentID): Comment { return $this->from('tb_comment AS CMT')->select('CMT.*') ->with(['board', 'post', 'user'])->where([ ['CMT.id', $commentID], ['CMT.is_delete', 0] ])->firstOrNew(); } /** * 댓글 조회 */ public function getList(string $code, int $postID, int $page, int $offset, int $perPage, int $sort): object { $orderBy = $this->sortQuery($sort); $sql = " SELECT COUNT(CMT.id) AS total FROM tb_comment CMT JOIN tb_post PST ON PST.id = CMT.post_id JOIN tb_board BRD ON BRD.id = PST.board_id WHERE TRUE AND BRD.code = ? AND PST.id = ? AND CASE WHEN (CMT.is_delete = 1 AND CMT.reply <= 0 AND CMT.depth <= 0) THEN FALSE ELSE TRUE END "; $total = DB::selectOne($sql, [$code, $postID])->total; $sql = " SELECT @replyRows := (SELECT (COUNT(id) - 1) FROM tb_comment WHERE lft BETWEEN CMT.lft AND CMT.rgt) AS replyRows, @depth := (SELECT (COUNT(*) - 1) FROM tb_comment WHERE CMT.rgt BETWEEN lft AND rgt), CMT.*, USR.name, USR.thumb, USR.icon, IFNULL(CLIK.`type`, 0) AS likeType FROM tb_comment CMT JOIN tb_comment PCMT ON CMT.lft BETWEEN PCMT.lft AND PCMT.rgt JOIN tb_post PST ON PST.id = CMT.post_id JOIN tb_board BRD ON BRD.id = PST.board_id LEFT JOIN tb_comment_like CLIK ON CLIK.post_id = CMT.post_id AND CMT.id = CLIK.comment_id LEFT JOIN users USR ON USR.id = CMT.user_id WHERE TRUE AND BRD.code = ? AND PST.id = ? AND CASE WHEN (CMT.is_delete = 1 AND @replyRows <= 0 AND @depth <= 0) THEN FALSE ELSE TRUE END GROUP BY CMT.id, CLIK.`type` ORDER BY {$orderBy} LIMIT ?, ?; "; $list = $this->getPaginator( DB::select($sql, [$code, $postID, $offset, $perPage]), $total, $perPage, $page ); return (object)[ 'total' => $total, 'list' => $list ]; } /** * 회원 댓글 목록 조회 */ public function getUserList(int $userID, SearchData $params): object { $sql = " SELECT COUNT(CMT.id) AS total FROM tb_comment CMT JOIN tb_post PST ON PST.id = CMT.post_id JOIN tb_board BRD ON BRD.id = PST.board_id LEFT JOIN users USR ON USR.id = CMT.user_id WHERE TRUE AND USR.id = ? AND CMT.is_delete = 0; "; $total = DB::selectOne($sql, [$userID])->total; $sql = " SELECT @replyRows := (SELECT (COUNT(id) - 1) FROM tb_comment WHERE lft BETWEEN CMT.lft AND CMT.rgt) AS replyRows, @depth := (SELECT (COUNT(*) - 1) FROM tb_comment WHERE CMT.rgt BETWEEN lft AND rgt), CMT.*, BRD.code, PST.subject, PST.comment_rows, PST.file_rows, PST.image_rows, PST.link_rows, USR.name, USR.thumb, USR.icon FROM tb_comment CMT JOIN tb_comment PCMT ON CMT.lft BETWEEN PCMT.lft AND PCMT.rgt JOIN tb_post PST ON PST.id = CMT.post_id JOIN tb_board BRD ON BRD.id = CMT.board_id LEFT JOIN users USR ON USR.id = CMT.user_id WHERE TRUE AND USR.id = ? AND CMT.is_delete = 0 GROUP BY CMT.id ORDER BY CMT.lft ASC, CMT.created_at ASC LIMIT ?, ?; "; $list = $this->getPaginator( DB::select($sql, [$userID, $params->offset, $params->perPage]), $total, $params->perPage, $params->page ); return (object)[ 'total' => $total, 'list' => $list ]; } /** * 정렬 순서 */ private function sortQuery(int $sort): string { switch($sort) { default:case 1: // 등록순 return 'CMT.lft ASC, CMT.created_at ASC'; case 2: // 최신순 return 'CMT.rgt DESC'; case 3: // 공감 순 return ' CASE WHEN (CMT.parent_id IS NULL AND CMT.reply <= 0) THEN CMT.like WHEN TRUE THEN TRUE ELSE NULL END DESC , CMT.lft ASC '; case 4: // 댓글 순 return ' CASE WHEN (CMT.parent_id IS NULL AND CMT.reply <= 0) THEN CMT.like WHEN TRUE THEN TRUE ELSE NULL END DESC , CMT.lft ASC '; } } /** * 댓글 조회 */ public function findParent(int $commentID) { return $this->newQuery()->withDepth()->find($commentID); } /** * 댓글 신고 수 증가 */ public function increaseBlame(int $commentID): int { return $this->where('id', $commentID)->increment('blame'); } /** * 댓글 신고 수 증감 */ public function decreaseBlame(int $commentID): int { return $this->where('id', $commentID)->where('blame', '>', '0')->decrement('blame', 1); } /** * 댓글 좋아요 수 증가 */ public function increaseLike(int $commentID): int { return $this->where('id', $commentID)->increment('like'); } /** * 댓글 좋아요 수 증감 */ public function decreaseLike(int $commentID): int { return $this->where('id', $commentID)->where('like', '>', '0')->decrement('like', 1); } /** * 댓글 싫어요 수 증가 */ public function increaseDisLike(int $commentID): int { return $this->where('id', $commentID)->increment('dislike'); } /** * 댓글 싫어요 수 증감 */ public function decreaseDisLike(int $commentID): int { return $this->where('id', $commentID)->where('dislike', '>', '0')->decrement('dislike'); } /** * 댓글 수 증가 */ public function increaseReply(int $commentID): int { return $this->where('id', $commentID)->increment('reply'); } /** * 댓글 수 증감 */ public function decreaseReply(int $commentID): int { return $this->where('id', $commentID)->where('reply', '>', '0')->decrement('reply'); } /** * 댓글 개수 갱신 */ public function updateCommentRows(int $commentID): int { $sql = " UPDATE tb_comment A, ( SELECT (COUNT(*) - 1) AS total FROM tb_comment CMT JOIN tb_comment PCMT ON CMT.lft BETWEEN PCMT.lft AND PCMT.rgt WHERE PCMT.id = :id ) B SET A.reply = B.total WHERE A.id = :id; "; return DB::update($sql, [ 'id' => $commentID ]); } /** * 댓글의 현재 페이지 번호 */ public function findPageNumber(int $commentID, int $perPage = DEFAULT_LIST_PER_PAGE): int { $sql = " SELECT D.cnt, COALESCE(CEIL((D.cnt - 1) / :perPage), 1) AS page FROM ( SELECT *, @num := (@num + 1) AS cnt FROM tb_comment, (SELECT @num := 0) AS N1, (SELECT @boardID := board_id, @postID := post_id, @commentID := id FROM tb_comment WHERE id = :commentID ) AS N2 WHERE TRUE AND board_id = @boardID AND post_id = @postID ORDER BY lft ) D WHERE D.id = @commentID; "; return DB::selectOne($sql, [ 'commentID' => $commentID, 'perPage' => $perPage ])->page; } /** * 댓글 존재 확인 */ public function isExists(int $commentID): int { $sql = " SELECT IF(COUNT(*) > 0, 1, 0) AS isExists FROM tb_comment CMT JOIN tb_post PST ON PST.id = CMT.post_id WHERE CMT.id = ? AND CMT.is_delete = 0; "; return DB::selectOne($sql, [$commentID])->isExists; } /** * 내가 작성한 댓글인지 확인 */ public function getChildRows(int $commentID): int { $sql = " SELECT (COUNT(CMT.comment_id) - 1) AS `rows` FROM tb_comment CMT JOIN tb_comment PCMT ON CMT.lft BETWEEN PCMT.lft AND PCMT.rgt WHERE PCMT.id = ? "; return DB::selectOne($sql, [$commentID])->rows; } /** * 추가 댓글 번호 */ public function lastedKey(): int { return intval(DB::selectOne( "SELECT AUTO_INCREMENT AS id FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'tb_comment';" )->id); } /** * 댓글 삭제 */ public function remove(Comment $comment, ?User $user): mixed { return DB::transaction(function() use ($comment, $user) { // 댓글 삭제 여부 승인 $this->updater($comment->id, [ 'is_delete' => 1, 'deleted_at' => now() ]); // 댓글 삭제 정보 추가 (new CommentDeleted)->register([ 'board_id' => $comment->board_id, 'post_id' => $comment->post_id, 'comment_id' => $comment->id, 'user_id' => $user?->id, 'ip_address' => IP_ADDRESS, 'user_agent' => USER_AGENT, 'created_at' => now() ]); // 게시판 댓글 수 갱신 (new Board)->updateCommentRows($comment->board_id); // 게시판 댓글 수 갱신 (new Post)->updateCommentRows($comment->post_id); // 부모 댓글 수 갱신 $this->updateCommentRows($comment->id); return true; }); } }