merge([ 'post_id' => $this->route('postId'), ]); } /** * 요청에 적용할 검증 규칙 * * @return array */ public function rules(): array { $slug = $this->route('slug'); $board = Board::where('slug', $slug)->first(); if (! $board) { return []; } // 비회원 여부 확인 (request()->user()를 사용해야 PermissionMiddleware에서 설정한 사용자를 인식) $isGuest = ! $this->user(); // 스팸/보안 설정에서 쿨다운 값 가져오기 $spamSecurity = g7_module_settings('sirsoft-board', 'spam_security', []); $commentCooldown = (int) ($spamSecurity['comment_cooldown_seconds'] ?? 0); // 금지 키워드 목록 가져오기 (게시글과 동일하게 게시판 설정 기준) $blockedKeywords = $board->blocked_keywords ?? []; $rules = [ 'content' => [ 'required', 'string', 'min:'.($board->min_comment_length ?? 2), 'max:'.($board->max_comment_length ?? 1000), new BlockedKeywordsRule($blockedKeywords), new CooldownRule('comment', $commentCooldown, $slug), ], 'post_id' => ['bail', 'required', 'integer', new CommentValidationRule($slug, 'post')], 'parent_id' => [ 'nullable', 'integer', // 부모 댓글은 요청 경로의 게시글에 속해야 한다 (교차 게시글 부모 차단) new CommentValidationRule($slug, 'parent_comment', (int) $this->route('postId')), ], // depth는 서비스에서 자동 계산되므로 요청 데이터에서 받지 않음 (보안상 이유) 'is_secret' => ['boolean'], 'status' => ['nullable', 'string', Rule::in(PostStatus::values())], // 비회원인 경우 author_name, password 필수 'author_name' => [$isGuest ? 'required' : 'nullable', 'string', 'max:50'], 'password' => [$isGuest ? 'required' : 'nullable', 'string', 'min:4', 'max:20'], ]; // 훅: 모듈/플러그인이 validation rules를 동적으로 추가할 수 있도록 필터 제공 return HookManager::applyFilters('sirsoft-board.comment.store_validation_rules', $rules, $this); } /** * 검증 오류 메시지 커스터마이징 * * @return array */ public function messages(): array { return [ 'content.required' => __('sirsoft-board::validation.comment.content.required'), 'content.min' => __('sirsoft-board::validation.comment.content.min'), 'content.max' => __('sirsoft-board::validation.comment.content.max'), 'parent_id.integer' => __('sirsoft-board::validation.comment.parent_id.integer'), 'author_name.required' => __('sirsoft-board::validation.comment.author_name.required'), 'author_name.max' => __('sirsoft-board::validation.comment.author_name.max'), 'password.required' => __('sirsoft-board::validation.comment.password.required'), 'password.min' => __('sirsoft-board::validation.comment.password.min'), ]; } /** * 검증할 필드의 이름을 커스터마이징 * * @return array */ public function attributes(): array { return [ 'content' => __('sirsoft-board::validation.attributes.comment.content'), 'parent_id' => __('sirsoft-board::validation.attributes.comment.parent_id'), 'is_secret' => __('sirsoft-board::validation.attributes.comment.is_secret'), 'status' => __('sirsoft-board::validation.attributes.comment.status'), 'author_name' => __('sirsoft-board::validation.attributes.comment.author_name'), 'password' => __('sirsoft-board::validation.attributes.comment.password'), ]; } /** * 검증 통과 후 비밀번호를 해싱합니다. * * @param string|null $key 반환할 특정 검증 값의 키 (null 이면 전체) * @param mixed $default 키가 없을 때 반환할 기본값 * @return array|mixed 검증된 데이터 (키 지정 시 해당 값) */ public function validated($key = null, $default = null): mixed { $validated = parent::validated($key, $default); // key가 지정된 경우 특정 값 반환 if ($key !== null) { return $validated; } // 비밀번호가 있으면 해싱 if (! empty($validated['password'])) { $validated['password'] = Hash::make($validated['password']); } return $validated; } }