DB 스키마 + 백엔드 로직 + API 레이어를 구현.
- board_reaction_types/board_reactions 테이블 신규, boards.use_reaction·
active_reaction_types, board_posts.reaction_counts 컬럼 추가
- ReactionType(다국어 JSON+폴백)/Reaction 모델, Repository 2종(인터페이스 주입)
- ReactionService: 등록/전환/해제 통합, DB::transaction 원자 카운트,
use_reaction·비활성유형·본인글·slug 스코프 차단, before/after_react 훅
- POST boards/{slug}/posts/{postId}/react(회원) + GET admin/reaction-types
- ReactPostRequest + AvailableReactionType Rule, PostResource/BoardResource 확장
- 반응 활동 로그(add/change/remove) + ko/en 다국어 + API 레퍼런스 문서
- 테스트 30건(Unit/Feature, 87 assertions) green, 마이그레이션 왕복 안전성 검증
프론트엔드 화면·ja 언어팩·CHANGELOG·버전 bump 는 후속 단계(4~5)에서 진행.
78 lines
2.8 KiB
PHP
78 lines
2.8 KiB
PHP
<?php
|
|
|
|
namespace Modules\Sirsoft\Board\Http\Controllers\User;
|
|
|
|
use App\Http\Controllers\Api\Base\AuthBaseController;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Modules\Sirsoft\Board\Exceptions\PostNotFoundException;
|
|
use Modules\Sirsoft\Board\Exceptions\ReactionNotAllowedException;
|
|
use Modules\Sirsoft\Board\Http\Requests\User\ReactPostRequest;
|
|
use Modules\Sirsoft\Board\Services\BoardService;
|
|
use Modules\Sirsoft\Board\Services\ReactionService;
|
|
|
|
/**
|
|
* 사용자용 반응 컨트롤러
|
|
*
|
|
* 게시글 반응(추천/비추천) 등록·전환·해제를 하나의 엔드포인트로 처리합니다.
|
|
*/
|
|
class ReactionController extends AuthBaseController
|
|
{
|
|
/**
|
|
* ReactionController 생성자
|
|
*
|
|
* @param ReactionService $reactionService 반응 서비스
|
|
* @param BoardService $boardService 게시판 서비스 (slug → 게시판 해석)
|
|
*/
|
|
public function __construct(
|
|
private ReactionService $reactionService,
|
|
private BoardService $boardService,
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* 게시글에 반응합니다 (등록/전환/해제 통합).
|
|
*
|
|
* @param ReactPostRequest $request 반응 요청 (reaction_type_id 검증)
|
|
* @param string $slug 게시판 slug
|
|
* @param int $postId 게시글 ID
|
|
* @return JsonResponse 반응 결과 응답
|
|
*/
|
|
public function react(ReactPostRequest $request, string $slug, int $postId): JsonResponse
|
|
{
|
|
try {
|
|
$board = $this->boardService->getBoardBySlug($slug, checkScope: false);
|
|
|
|
if (! $board) {
|
|
return $this->notFound('sirsoft-board::messages.boards.error_404');
|
|
}
|
|
|
|
$result = $this->reactionService->react(
|
|
(int) Auth::id(),
|
|
$board,
|
|
$postId,
|
|
(int) $request->validated('reaction_type_id'),
|
|
);
|
|
|
|
$messageKey = match ($result['action']) {
|
|
'change' => 'sirsoft-board::messages.reaction.change_success',
|
|
'remove' => 'sirsoft-board::messages.reaction.remove_success',
|
|
default => 'sirsoft-board::messages.reaction.add_success',
|
|
};
|
|
|
|
return $this->success($messageKey, [
|
|
'action' => $result['action'],
|
|
'my_reaction_type_id' => $result['reaction_type_id'],
|
|
'reaction_counts' => $result['reaction_counts'],
|
|
]);
|
|
} catch (PostNotFoundException $e) {
|
|
return $this->notFound('sirsoft-board::messages.post.not_found');
|
|
} catch (ReactionNotAllowedException $e) {
|
|
return $this->error($e->getMessage(), 422, ['code' => 'reaction_not_allowed']);
|
|
} catch (\Exception $e) {
|
|
return $this->error('sirsoft-board::messages.reaction.failed', 500, $e->getMessage());
|
|
}
|
|
}
|
|
}
|