visibleNotice(); } public function paginate(string $q, int $perPage = 15): LengthAwarePaginator { $q = trim($q); $query = $this->baseVisible() ->when($q !== '', function (Builder $query) use ($q) { $query->where(function (Builder $w) use ($q) { $w->where('subject', 'like', "%{$q}%") ->orWhere('content', 'like', "%{$q}%"); }); }); // 기존 정렬 스코프 유지 $query->noticeOrder(); return $query->paginate($perPage); } public function findOrFail(int $seq): GcBoard { return $this->baseVisible() ->where('seq', $seq) ->firstOrFail(); } public function incrementHit(int $seq): void { // 단순 hit 증가 GcBoard::query() ->where('seq', $seq) ->increment('hit'); } /** * 리스트 정렬 기준(공지 우선 + 최신 우선)을 아래처럼 가정/고정: * 1) first_sign DESC * 2) seq DESC * * ⚠️ 기존 noticeOrder()가 regdate를 포함해도 무방하지만, * prev/next는 "가장 가까운 글"을 잡아야 해서 여기서는 명시적으로 처리. */ public function findPrev(GcBoard $cur): ?GcBoard { $curFirst = (int)($cur->first_sign ?? 0); $curSeq = (int)$cur->seq; return $this->baseVisible() ->where(function (Builder $w) use ($curFirst, $curSeq) { // COALESCE(first_sign,0) < curFirst $w->whereRaw('COALESCE(first_sign,0) < ?', [$curFirst]) ->orWhere(function (Builder $w2) use ($curFirst, $curSeq) { // COALESCE(first_sign,0) = curFirst AND seq < curSeq $w2->whereRaw('COALESCE(first_sign,0) = ?', [$curFirst]) ->where('seq', '<', $curSeq); }); }) ->orderByRaw('COALESCE(first_sign,0) desc') ->orderBy('seq', 'desc') ->first(); } public function findNext(GcBoard $cur): ?GcBoard { $curFirst = (int)($cur->first_sign ?? 0); $curSeq = (int)$cur->seq; // "가장 가까운 앞"을 위해 ASC로 뒤집어서 first() return $this->baseVisible() ->where(function (Builder $w) use ($curFirst, $curSeq) { $w->whereRaw('COALESCE(first_sign,0) > ?', [$curFirst]) ->orWhere(function (Builder $w2) use ($curFirst, $curSeq) { $w2->whereRaw('COALESCE(first_sign,0) = ?', [$curFirst]) ->where('seq', '>', $curSeq); }); }) ->orderByRaw('COALESCE(first_sign,0) asc') ->orderBy('seq', 'asc') ->first(); } }