레이아웃 코드 편집 화면이 목록을 부를 때 모든 레이아웃의 본문(content)까지 함께 내려받고 있었다. 레이아웃이 많은 템플릿에서는 목록을 여는 것만으로 수십 MB 가 브라우저에 쌓여 화면이 크게 느려졌고, 디버그 모드에서는 탭이 메모리 부족으로 종료됐다. 목록 화면은 본문을 쓰지 않고 선택한 파일 하나만 편집한다. 목록 전용 Resource 를 두어 이름·설명·크기·수정일만 싣고, 본문은 상세 조회에서만 읽는다. 크기는 목록에서 표시하므로 저장소가 길이만 계산해 돌려준다.
114 lines
4.1 KiB
PHP
114 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Resources;
|
|
|
|
use Carbon\Carbon;
|
|
use Illuminate\Http\Request;
|
|
|
|
/**
|
|
* 레이아웃 **목록** 전용 리소스
|
|
*
|
|
* 코드 편집 화면의 파일 목록은 행마다 이름·설명·라우트·크기·수정일·업데이트 표시만 쓴다.
|
|
* 편집 대상 본문은 선택 시 상세 엔드포인트(`current_layout`)가 따로 받아온다.
|
|
*
|
|
* 종전에는 목록·상세가 {@see LayoutResource} 하나를 공용해 목록 응답에도 각 레이아웃의
|
|
* `content`(본문 전체) · `components` · `data_sources` · `metadata` 가 실렸다.
|
|
* 실측(sirsoft-admin_basic): 응답 18.30MB 중 `content` 가 17.41MB(95%). 화면은 102개 중
|
|
* 1개 본문만 필요한데 101개를 더 받아 전역 상태에 상주시켰고, 디버그 모드에서는 그 상태를
|
|
* 추적하다 렌더러가 메모리 한계를 넘었다.
|
|
*
|
|
* 본문에서 파생되는 값(`description` · `size`)은 조회 계층이 미리 계산해 넘긴다 —
|
|
* 이 리소스는 본문을 보지 않는다.
|
|
*
|
|
* @since 7.0.6
|
|
*/
|
|
class LayoutListResource extends BaseApiResource
|
|
{
|
|
/** @var array<string, string|null> 레이아웃 이름 → 라우트 path 매핑 */
|
|
private array $routePathMap = [];
|
|
|
|
/**
|
|
* 라우트 path 매핑을 주입합니다.
|
|
*
|
|
* @param array<string, string|null> $routePathMap 레이아웃 이름 → 라우트 path
|
|
* @return $this
|
|
*/
|
|
public function withRoutePathMap(array $routePathMap): self
|
|
{
|
|
$this->routePathMap = $routePathMap;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 목록 행을 배열로 변환합니다.
|
|
*
|
|
* @param Request $request 요청 객체
|
|
* @return array 목록 행 데이터
|
|
*/
|
|
public function toArray(Request $request): array
|
|
{
|
|
$name = $this->getValue('name');
|
|
$size = (int) $this->getValue('size', 0);
|
|
|
|
return [
|
|
'id' => $this->getValue('id'),
|
|
'template_id' => $this->getValue('template_id'),
|
|
'name' => $name,
|
|
'description' => $this->getValue('description') ?: $name,
|
|
|
|
// 이 레이아웃을 사용하는 라우트의 path (routes.json 기준). 파일 선택 시 ?route=
|
|
// 동기화 / 위지윅에서 넘어온 ?route= 로 해당 파일 복원에 사용.
|
|
'route_path' => $this->routePathMap[$name] ?? null,
|
|
|
|
'size' => $size,
|
|
'size_formatted' => $this->formatFileSize($size),
|
|
'has_update' => false,
|
|
|
|
// 낙관적 잠금 — 편집 진입 후 저장 요청에 expected_lock_version 으로 전달
|
|
'lock_version' => (int) $this->getValue('lock_version', 0),
|
|
|
|
// 상세 응답(LayoutResource)의 formatTimestamps() 와 같은 규약 — 사용자 timezone
|
|
// 기준 문자열이다. 원시 ISO 를 그대로 내보내면 목록의 수정일 표기만 형식이 달라진다.
|
|
'created_at' => $this->formatDateTimeStringForUser($this->toCarbon($this->getValue('created_at'))),
|
|
'updated_at' => $this->formatDateTimeStringForUser($this->toCarbon($this->getValue('updated_at'))),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* 값을 Carbon 인스턴스로 변환합니다.
|
|
*
|
|
* 경량 조회는 배열 행을 넘기므로 값이 Carbon 일 수도, 문자열일 수도, null 일 수도 있다.
|
|
*
|
|
* @param mixed $value 변환할 값
|
|
* @return Carbon|null 변환된 Carbon 또는 null
|
|
*/
|
|
private function toCarbon(mixed $value): ?Carbon
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
return $value instanceof Carbon ? $value : Carbon::parse($value);
|
|
}
|
|
|
|
/**
|
|
* 파일 크기를 사람이 읽는 단위로 변환합니다.
|
|
*
|
|
* @param int $bytes 바이트 수
|
|
* @return string 변환된 크기 문자열
|
|
*/
|
|
private function formatFileSize(int $bytes): string
|
|
{
|
|
if ($bytes < 1024) {
|
|
return $bytes.' B';
|
|
}
|
|
|
|
if ($bytes < 1048576) {
|
|
return round($bytes / 1024, 1).' KB';
|
|
}
|
|
|
|
return round($bytes / 1048576, 1).' MB';
|
|
}
|
|
}
|