feat: [boards] 게시판 API 시스템 구현
- BoardController, PostController 추가 - Board, BoardSetting 모델 수정 - BoardService 추가 - FormRequest 클래스 추가 - Swagger 문서 추가 (BoardApi, PostApi) - 게시판 시스템 필드 마이그레이션 추가
This commit is contained in:
327
app/Services/Boards/PostService.php
Normal file
327
app/Services/Boards/PostService.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Boards;
|
||||
|
||||
use App\Models\Boards\Board;
|
||||
use App\Models\Boards\BoardComment;
|
||||
use App\Models\Boards\Post;
|
||||
use App\Models\Boards\PostCustomFieldValue;
|
||||
use App\Services\Service;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class PostService extends Service
|
||||
{
|
||||
// =========================================================================
|
||||
// 게시글 조회 메서드
|
||||
// =========================================================================
|
||||
|
||||
/**
|
||||
* 게시글 목록 조회 (페이징)
|
||||
*/
|
||||
public function getPostsByBoard(int $boardId, array $filters = [], int $perPage = 15): LengthAwarePaginator
|
||||
{
|
||||
return Post::where('board_id', $boardId)
|
||||
->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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user