263 lines
8.4 KiB
PHP
263 lines
8.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Admin;
|
|
|
|
use App\Exceptions\ConcurrentModificationException;
|
|
use App\Http\Controllers\Api\Base\AdminBaseController;
|
|
use App\Http\Requests\Layout\StoreLayoutPreviewRequest;
|
|
use App\Http\Requests\Layout\UpdateLayoutContentRequest;
|
|
use App\Http\Resources\LayoutResource;
|
|
use App\Http\Resources\LayoutVersionResource;
|
|
use App\Services\LayoutPreviewService;
|
|
use App\Services\LayoutService;
|
|
use App\Services\TemplateService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class LayoutController extends AdminBaseController
|
|
{
|
|
public function __construct(
|
|
private LayoutService $layoutService,
|
|
private TemplateService $templateService,
|
|
private LayoutPreviewService $layoutPreviewService
|
|
) {
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* 특정 템플릿의 모든 레이아웃 목록 조회
|
|
*
|
|
* @param string $templateName 템플릿 identifier
|
|
* @return JsonResponse 레이아웃 목록 응답
|
|
*/
|
|
public function index(string $templateName): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$layouts = $this->layoutService->getLayoutsByTemplateId($template->id);
|
|
|
|
// 레이아웃 이름 → 라우트 path 매핑 — 코드 편집기가 파일 선택 시 ?route= URL
|
|
// 동기화 / 위지윅에서 넘어온 ?route= 로 해당 파일 복원에 사용한다.
|
|
$routePathMap = $this->templateService->getLayoutRoutePathMap($templateName);
|
|
|
|
$collection = LayoutResource::collection($layouts);
|
|
$collection->collection->transform(
|
|
fn (LayoutResource $resource) => $resource->withRoutePathMap($routePathMap)
|
|
);
|
|
|
|
return $this->success('common.success', $collection);
|
|
}
|
|
|
|
/**
|
|
* 특정 레이아웃 상세 조회
|
|
*
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @return JsonResponse 레이아웃 상세 응답
|
|
*/
|
|
public function show(string $templateName, string $name): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$layout = $this->layoutService->getLayoutByName($template->id, $name);
|
|
|
|
if (! $layout) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
new LayoutResource($layout)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 레이아웃 수정
|
|
*
|
|
* @param UpdateLayoutContentRequest $request 레이아웃 수정 요청
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @return JsonResponse 수정된 레이아웃 응답
|
|
*/
|
|
public function update(UpdateLayoutContentRequest $request, string $templateName, string $name): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
try {
|
|
DB::beginTransaction();
|
|
|
|
$layout = $this->layoutService->updateLayout($template->id, $name, $request->validated());
|
|
|
|
DB::commit();
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
new LayoutResource($layout)
|
|
);
|
|
} catch (ConcurrentModificationException $e) {
|
|
DB::rollBack();
|
|
|
|
return $this->error(
|
|
'exceptions.concurrent_modification',
|
|
409,
|
|
[
|
|
'error' => 'concurrent_modification',
|
|
'current_version' => $e->currentVersion,
|
|
'your_version' => $e->expectedVersion,
|
|
'resource' => $e->resource,
|
|
],
|
|
['resource' => $e->resource, 'current' => $e->currentVersion, 'expected' => $e->expectedVersion],
|
|
);
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
|
|
return $this->error(
|
|
'common.failed',
|
|
500,
|
|
['error' => $e->getMessage()]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 레이아웃의 모든 버전 목록 조회
|
|
*
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @return JsonResponse 버전 목록 응답
|
|
*/
|
|
public function versions(string $templateName, string $name): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$layout = $this->layoutService->getLayoutByName($template->id, $name);
|
|
|
|
if (! $layout) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$versions = $this->layoutService->getLayoutVersions($template->id, $name);
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
LayoutVersionResource::collection($versions)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 특정 버전의 레이아웃 content 조회
|
|
*
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @param int $version 버전 번호
|
|
* @return JsonResponse 버전 content 응답
|
|
*/
|
|
public function showVersion(string $templateName, string $name, int $version): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$layoutVersion = $this->layoutService->getLayoutVersion($template->id, $name, $version);
|
|
|
|
if (! $layoutVersion) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
// 버전 비교 diff 용 — content 원본 전체(slots/extends 등 포함) 노출.
|
|
(new LayoutVersionResource($layoutVersion))->withFullContent()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 버전 복원
|
|
*
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @param int $versionId 버전 ID
|
|
* @return JsonResponse 복원된 버전 응답
|
|
*/
|
|
public function restoreVersion(string $templateName, string $name, int $versionId): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
$newVersion = $this->layoutService->restoreVersion($template->id, $name, $versionId);
|
|
|
|
if (! $newVersion) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
new LayoutVersionResource($newVersion)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 레이아웃 미리보기 생성
|
|
*
|
|
* 편집 중인 레이아웃 content를 임시 저장하고 미리보기 URL을 반환합니다.
|
|
*
|
|
* @param StoreLayoutPreviewRequest $request 미리보기 생성 요청
|
|
* @param string $templateName 템플릿 identifier
|
|
* @param string $name 레이아웃 이름
|
|
* @return JsonResponse 미리보기 토큰/URL 응답
|
|
*/
|
|
public function storePreview(StoreLayoutPreviewRequest $request, string $templateName, string $name): JsonResponse
|
|
{
|
|
$template = $this->templateService->findByIdentifier($templateName);
|
|
|
|
if (! $template) {
|
|
return $this->notFound('common.not_found');
|
|
}
|
|
|
|
try {
|
|
$preview = $this->layoutPreviewService->createPreview(
|
|
$template->id,
|
|
$name,
|
|
$request->validated('content'),
|
|
$request->user()->id
|
|
);
|
|
|
|
return $this->success(
|
|
'common.success',
|
|
[
|
|
'token' => $preview->token,
|
|
'preview_url' => '/preview/'.$preview->token,
|
|
'expires_at' => $preview->expires_at->toIso8601String(),
|
|
]
|
|
);
|
|
} catch (\Exception $e) {
|
|
return $this->error(
|
|
'common.failed',
|
|
500,
|
|
['error' => $e->getMessage()]
|
|
);
|
|
}
|
|
}
|
|
}
|