previewRepository->deleteByLayoutAndAdmin($templateId, $layoutName, $adminId); return $this->previewRepository->create([ 'token' => (string) Str::uuid(), 'template_id' => $templateId, 'layout_name' => $layoutName, 'content' => $content, 'admin_id' => $adminId, 'expires_at' => now()->addMinutes(self::DEFAULT_TTL_MINUTES), ]); } /** * 토큰으로 미리보기 레이아웃을 조회하고 병합합니다. * * 편집 중인 content에 extends가 있으면 부모 레이아웃과 병합하고, * 모듈/플러그인 extension도 적용합니다. * * @param string $token 미리보기 토큰 * @return array|null 병합된 레이아웃 데이터 또는 null */ public function getPreviewLayout(string $token): ?array { $preview = $this->previewRepository->findByToken($token); if (! $preview) { return null; } $layoutData = $preview->content; $templateId = $preview->template_id; // extends 상속 병합 if (isset($layoutData['extends'])) { $parentLayoutName = $layoutData['extends']; $parentLayout = $this->layoutService->loadAndMergeLayout($templateId, $parentLayoutName); $layoutData = $this->layoutService->mergeLayouts($parentLayout, $layoutData); } // 모듈/플러그인 Extension 적용 $layoutData = $this->layoutExtensionService->applyExtensions($layoutData, $templateId); return $layoutData; } /** * 토큰으로 미리보기의 템플릿 식별자를 조회합니다. * * @param string $token 미리보기 토큰 * @return string|null 템플릿 식별자 또는 null */ public function getTemplateIdentifierByToken(string $token): ?string { $preview = $this->previewRepository->findByToken($token); if (! $preview) { return null; } $template = $this->templateRepository->findById($preview->template_id); return $template?->identifier; } /** * 만료된 미리보기를 삭제합니다. * * @return int 삭제된 행 수 */ public function cleanupExpired(): int { return $this->previewRepository->deleteExpired(); } /** * 특정 관리자의 특정 레이아웃 미리보기를 삭제합니다. * * @param int $templateId 템플릿 ID * @param string $layoutName 레이아웃 이름 * @param int $adminId 관리자 ID * @return int 삭제된 행 수 */ public function deleteByLayoutAndAdmin(int $templateId, string $layoutName, int $adminId): int { return $this->previewRepository->deleteByLayoutAndAdmin($templateId, $layoutName, $adminId); } }