where('tenant_id', $this->tenantId()) ->when(isset($filters['search']), function ($q) use ($filters) { $q->where(function ($query) use ($filters) { $query->where('title', 'like', "%{$filters['search']}%") ->orWhere('content', 'like', "%{$filters['search']}%"); }); }) ->when(isset($filters['is_notice']), fn ($q) => $q->where('is_notice', $filters['is_notice'])) ->when(isset($filters['status']), fn ($q) => $q->where('status', $filters['status'])) ->orderByDesc('is_notice') ->orderByDesc('created_at') ->paginate($perPage); } /** * 게시글 목록 조회 (게시판 코드 기반) */ public function getPostsByBoardCode(string $boardCode, array $filters = [], int $perPage = 15): LengthAwarePaginator { $board = Board::accessible($this->tenantId()) ->where('board_code', $boardCode) ->firstOrFail(); return $this->getPostsByBoard($board->id, $filters, $perPage); } /** * 게시글 단건 조회 */ public function getPost(int $postId): ?Post { return Post::with(['files', 'comments.replies']) ->where('tenant_id', $this->tenantId()) ->find($postId); } /** * 게시글 상세 조회 (조회수 증가) */ public function getPostWithViewIncrement(int $postId): ?Post { $post = $this->getPost($postId); if ($post) { $post->increment('views'); } return $post; } /** * 게시판 코드와 게시글 ID로 조회 */ public function getPostByCodeAndId(string $boardCode, int $postId): ?Post { $board = Board::accessible($this->tenantId()) ->where('board_code', $boardCode) ->first(); if (! $board) { return null; } return Post::with(['files', 'comments.replies', 'board']) ->where('board_id', $board->id) ->where('tenant_id', $this->tenantId()) ->find($postId); } // ========================================================================= // 게시글 생성 메서드 // ========================================================================= /** * 게시글 생성 */ public function createPost(int $boardId, array $data): Post { $data['board_id'] = $boardId; $data['tenant_id'] = $this->tenantId(); $data['user_id'] = $this->apiUserId(); $data['ip_address'] = request()->ip(); $data['status'] = $data['status'] ?? 'published'; $data['views'] = 0; $post = Post::create($data); // 커스텀 필드 저장 if (isset($data['custom_fields'])) { $this->saveCustomFields($post->id, $data['custom_fields']); } return $post->fresh(['board', 'files']); } /** * 게시글 생성 (게시판 코드 기반) */ public function createPostByBoardCode(string $boardCode, array $data): Post { $board = Board::accessible($this->tenantId()) ->where('board_code', $boardCode) ->firstOrFail(); return $this->createPost($board->id, $data); } // ========================================================================= // 게시글 수정 메서드 // ========================================================================= /** * 게시글 수정 */ public function updatePost(int $postId, array $data): ?Post { $post = Post::where('tenant_id', $this->tenantId()) ->find($postId); if (! $post) { return null; } $post->update($data); // 커스텀 필드 업데이트 if (isset($data['custom_fields'])) { $this->saveCustomFields($post->id, $data['custom_fields']); } return $post->fresh(['board', 'files']); } /** * 게시글 수정 (게시판 코드 기반) */ public function updatePostByBoardCode(string $boardCode, int $postId, array $data): ?Post { $board = Board::accessible($this->tenantId()) ->where('board_code', $boardCode) ->first(); if (! $board) { return null; } $post = Post::where('board_id', $board->id) ->where('tenant_id', $this->tenantId()) ->find($postId); if (! $post) { return null; } $post->update($data); if (isset($data['custom_fields'])) { $this->saveCustomFields($post->id, $data['custom_fields']); } return $post->fresh(['board', 'files']); } // ========================================================================= // 게시글 삭제 메서드 // ========================================================================= /** * 게시글 삭제 (Soft Delete) */ public function deletePost(int $postId): bool { $post = Post::where('tenant_id', $this->tenantId()) ->find($postId); if (! $post) { return false; } return $post->delete(); } /** * 게시글 삭제 (게시판 코드 기반) */ public function deletePostByBoardCode(string $boardCode, int $postId): bool { $board = Board::accessible($this->tenantId()) ->where('board_code', $boardCode) ->first(); if (! $board) { return false; } $post = Post::where('board_id', $board->id) ->where('tenant_id', $this->tenantId()) ->find($postId); if (! $post) { return false; } return $post->delete(); } // ========================================================================= // 댓글 메서드 // ========================================================================= /** * 댓글 목록 조회 */ public function getComments(int $postId): Collection { return BoardComment::where('post_id', $postId) ->whereNull('parent_id') ->where('status', 'active') ->with('replies') ->orderBy('created_at') ->get(); } /** * 댓글 작성 */ public function createComment(int $postId, array $data): BoardComment { $data['post_id'] = $postId; $data['user_id'] = $this->apiUserId(); $data['ip_address'] = request()->ip(); $data['status'] = 'active'; return BoardComment::create($data); } /** * 댓글 수정 */ public function updateComment(int $commentId, array $data): ?BoardComment { $comment = BoardComment::where('user_id', $this->apiUserId()) ->find($commentId); if (! $comment) { return null; } $comment->update($data); return $comment->fresh(); } /** * 댓글 삭제 */ public function deleteComment(int $commentId): bool { $comment = BoardComment::where('user_id', $this->apiUserId()) ->find($commentId); if (! $comment) { return false; } $comment->status = 'deleted'; $comment->save(); return true; } // ========================================================================= // 커스텀 필드 메서드 // ========================================================================= /** * 커스텀 필드 값 저장 */ protected function saveCustomFields(int $postId, array $customFields): void { foreach ($customFields as $fieldId => $value) { PostCustomFieldValue::updateOrCreate( [ 'post_id' => $postId, 'field_id' => $fieldId, ], [ 'value' => is_array($value) ? json_encode($value) : $value, ] ); } } /** * 커스텀 필드 값 조회 */ public function getCustomFieldValues(int $postId): Collection { return PostCustomFieldValue::where('post_id', $postId) ->with('field') ->get(); } // ========================================================================= // 나의 게시글 메서드 // ========================================================================= /** * 나의 게시글 목록 조회 */ public function getMyPosts(array $filters = [], int $perPage = 15): LengthAwarePaginator { return Post::where('user_id', $this->apiUserId()) ->where('tenant_id', $this->tenantId()) ->with(['board:id,board_code,name']) ->when(isset($filters['board_code']), function ($q) use ($filters) { $q->whereHas('board', fn ($query) => $query->where('board_code', $filters['board_code'])); }) ->when(isset($filters['search']), function ($q) use ($filters) { $q->where(function ($query) use ($filters) { $query->where('title', 'like', "%{$filters['search']}%") ->orWhere('content', 'like', "%{$filters['search']}%"); }); }) ->when(isset($filters['status']), fn ($q) => $q->where('status', $filters['status'])) ->orderByDesc('created_at') ->paginate($perPage); } }