Files
Gnuboard7/app/Search/SearchCategoryPayload.php
T
HeuJung 7f457a05b3 fix(core,board,page,ecommerce,basic,pay): FULLTEXT 게이트·검색 실패 표면화와 페이지네이션/접두사 결함 정비
- 공개 : MATCH 는 커버 인덱스가 있을 때만 조립 — 부재 시 LIKE 폴백 + 1회 경고,
 카테고리 검색 예외를 categories_failed/search_failed 로 표면화하고 basic 템플릿이 오류 안내 렌더
- 공개 동근원: paginate page 명시 전달 (언어팩 check-updates, 상품 문의 목록)
- 공개 동근원: raw SQL 접두사/별칭 하드코딩 정리 (board 시더, 7.0.6 업그레이드 스텝,
 결제 3플러그인 컨트롤러 51지점 모델 파생 전환)
- audit 룰 3종 신설 + repository-raw-hardcoded-table 컨트롤러 확대, 확장 TestCase
 오토로더 중복 선언 가드 16지점, ja 언어팩 동기
2026-08-17 03:02:26 +09:00

128 lines
5.4 KiB
PHP

<?php
namespace App\Search;
use App\Enums\TotalRelation;
use App\Support\Query\BoundedCount;
use App\Support\Query\BoundedPage;
use App\Support\Query\KeysetPaginator;
use Illuminate\Pagination\CursorPaginator;
/**
* 검색 카테고리 응답 페이로드를 만드는 단일 지점
*
* 검색 결과는 컬렉션 리소스를 거치지 않고 리스너가 배열을 만들어 넘긴다. 그 조립을
* 도메인마다 손으로 하면 응답 형태가 갈라지고, 나중에 필드가 하나 늘 때 어떤 화면은
* 받고 어떤 화면은 못 받는다. 그 형태 확장이 조용히 깨지는 것을 막기 위해 키 구성을
* 여기 한 곳에 둔다.
*
* offset 응답과 커서 응답은 채워지는 값만 다르고 **키 집합은 같다**. 화면이 두 형태를
* 분기 없이 그릴 수 있어야 하기 때문이다.
*/
final class SearchCategoryPayload
{
/**
* offset(페이지 번호) 방식 결과로 페이로드를 만듭니다.
*
* @param BoundedPage $page 상한 총 건수를 가진 페이지 결과
* @param array<int, mixed> $items 화면에 실을 항목 (가공 완료 상태)
* @param array<string, mixed> $extra 도메인 고유 필드 (available_boards 등)
* @return array<string, mixed> 카테고리 페이로드
*/
public static function fromBounded(BoundedPage $page, array $items, array $extra = []): array
{
return array_merge([
'total' => $page->total(),
'total_relation' => $page->totalRelation()->value,
'total_is_exact' => $page->totalRelation()->isExact(),
'result_cap' => $page->resultCap(),
'last_page' => $page->lastPage(),
'has_more_pages' => $page->hasMorePages(),
// offset 응답에는 커서가 없다. 키 자체는 남겨 화면이 분기 없이 읽게 한다.
'next_cursor' => null,
'prev_cursor' => null,
'items' => $items,
], $extra);
}
/**
* 커서(키셋) 방식 결과로 페이로드를 만듭니다.
*
* 커서 응답에는 총 건수가 없으므로 건수는 별도 집계로 받습니다. 마지막 페이지 번호는
* 커서 방식에 존재하지 않는 개념이라 언제나 null 이며, 화면은 그때 마지막 페이지
* 점프만 감추고 "다음" 이동은 그대로 유지합니다.
*
* @param CursorPaginator $page 커서 페이지 결과
* @param BoundedCount|null $count 총 건수 집계 (배지를 그리지 않으면 null)
* @param array<int, mixed> $items 화면에 실을 항목 (가공 완료 상태)
* @param array<string, mixed> $extra 도메인 고유 필드
* @return array<string, mixed> 카테고리 페이로드
*/
public static function fromCursor(
CursorPaginator $page,
?BoundedCount $count,
array $items,
array $extra = []
): array {
$relation = $count?->totalRelation() ?? TotalRelation::AtLeast;
return array_merge([
'total' => $count?->total() ?? count($items),
'total_relation' => $relation->value,
'total_is_exact' => $count !== null && $relation->isExact(),
'result_cap' => $count?->resultCap(),
// 커서 방식에는 마지막 페이지 번호가 없다 (총 건수를 알아도 계산하지 않는다).
'last_page' => null,
'has_more_pages' => $page->hasMorePages(),
'next_cursor' => KeysetPaginator::nextCursor($page),
'prev_cursor' => KeysetPaginator::previousCursor($page),
'items' => $items,
], $extra);
}
/**
* 카테고리 검색이 예외로 실패했을 때의 페이로드를 만듭니다.
*
* 실패한 0건을 "정확한 0건" 으로 말하지 않는다 — `total_is_exact=false` 로 내보내
* 배지가 정확한 값처럼 그려지는 것을 막고, `failed` 플래그로 화면이 "결과 없음" 과
* 구분되는 오류 안내를 그릴 수 있게 한다. 키 집합은 다른 팩토리와 동일하게 유지해
* 화면이 분기 없이 읽게 한다.
*
* @param array<string, mixed> $extra 도메인 고유 필드 (available_boards 등)
* @return array<string, mixed> 카테고리 페이로드
*/
public static function failed(array $extra = []): array
{
return array_merge([
'failed' => true,
'total' => 0,
'total_relation' => TotalRelation::AtLeast->value,
'total_is_exact' => false,
'result_cap' => null,
'last_page' => null,
'has_more_pages' => false,
'next_cursor' => null,
'prev_cursor' => null,
'items' => [],
], $extra);
}
/**
* 목록 없이 건수만 필요한 자리(비활성 탭 배지)의 페이로드를 만듭니다.
*
* @param BoundedCount $count 총 건수 집계
* @param array<string, mixed> $extra 도메인 고유 필드
* @return array<string, mixed> 카테고리 페이로드
*/
public static function fromCountOnly(BoundedCount $count, array $extra = []): array
{
return array_merge($count->toArray(), [
'last_page' => null,
'has_more_pages' => false,
'next_cursor' => null,
'prev_cursor' => null,
'items' => [],
], $extra);
}
}