Files
Gnuboard7/app/Repositories/LayoutVersionRepository.php
T
2026-04-01 10:30:52 +09:00

193 lines
5.7 KiB
PHP

<?php
namespace App\Repositories;
use App\Contracts\Repositories\LayoutVersionRepositoryInterface;
use App\Models\TemplateLayout;
use App\Models\TemplateLayoutVersion;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\DB;
class LayoutVersionRepository implements LayoutVersionRepositoryInterface
{
public function __construct(
private TemplateLayoutVersion $model
) {}
/**
* 버전 저장 (자동 증가)
*
* @param int $layoutId 레이아웃 ID
* @param array $oldContent 이전 콘텐츠
* @param array|null $newContent 새 콘텐츠 (null이면 현재 레이아웃에서 가져옴)
* @return TemplateLayoutVersion 생성된 버전
*/
public function saveVersion(int $layoutId, array $oldContent, ?array $newContent = null): TemplateLayoutVersion
{
$nextVersion = $this->getNextVersion($layoutId);
// newContent가 제공되지 않으면 현재 레이아웃 content를 사용
if ($newContent === null) {
$layout = TemplateLayout::findOrFail($layoutId);
$newContent = $layout->content;
}
// changes_summary 계산
$changesSummary = $this->calculateChanges($oldContent, $newContent);
return $this->model->create([
'layout_id' => $layoutId,
'version' => $nextVersion,
'content' => $oldContent,
'changes_summary' => $changesSummary,
]);
}
/**
* 특정 레이아웃의 모든 버전 조회 (최신순)
*/
public function getVersions(int $layoutId): Collection
{
return $this->model
->where('layout_id', $layoutId)
->orderBy('version', 'desc')
->get();
}
/**
* 특정 버전 조회
*/
public function getVersion(int $versionId): ?TemplateLayoutVersion
{
return $this->model->find($versionId);
}
/**
* 다음 버전 번호 계산
*/
public function getNextVersion(int $layoutId): int
{
$maxVersion = $this->model
->where('layout_id', $layoutId)
->max('version');
return $maxVersion ? $maxVersion + 1 : 1;
}
/**
* JSON content 변경사항 계산
*/
public function calculateChanges(array $oldContent, array $newContent): array
{
$added = [];
$removed = [];
$modified = [];
// 재귀적으로 비교
$this->diffRecursive($oldContent, $newContent, '', $added, $removed, $modified);
// 문자 수 변화 계산
$oldChars = strlen(json_encode($oldContent, JSON_UNESCAPED_UNICODE));
$newChars = strlen(json_encode($newContent, JSON_UNESCAPED_UNICODE));
$charDiff = $newChars - $oldChars;
return [
'added' => $added,
'removed' => $removed,
'modified' => $modified,
'char_diff' => $charDiff,
];
}
/**
* 재귀적 JSON diff 알고리즘
*/
private function diffRecursive(
array $old,
array $new,
string $prefix,
array &$added,
array &$removed,
array &$modified
): void {
// 삭제된 키 찾기
foreach ($old as $key => $value) {
$path = $prefix ? "{$prefix}.{$key}" : $key;
if (!array_key_exists($key, $new)) {
$removed[] = $path;
continue;
}
// 값 비교
$oldValue = $old[$key];
$newValue = $new[$key];
// 둘 다 배열이면 재귀 호출
if (is_array($oldValue) && is_array($newValue)) {
$this->diffRecursive($oldValue, $newValue, $path, $added, $removed, $modified);
} elseif ($oldValue !== $newValue) {
// 값이 변경됨
$modified[] = $path;
}
}
// 추가된 키 찾기
foreach ($new as $key => $value) {
if (!array_key_exists($key, $old)) {
$path = $prefix ? "{$prefix}.{$key}" : $key;
$added[] = $path;
// 추가된 항목의 하위 구조도 added로 포함
if (is_array($value)) {
$this->collectAddedPaths($value, $path, $added);
}
}
}
}
/**
* 추가된 경로의 모든 하위 경로 수집
*/
private function collectAddedPaths(array $data, string $prefix, array &$added): void
{
foreach ($data as $key => $value) {
$path = "{$prefix}.{$key}";
$added[] = $path;
if (is_array($value)) {
$this->collectAddedPaths($value, $path, $added);
}
}
}
/**
* 버전 복원
*
* @throws ModelNotFoundException 버전을 찾을 수 없는 경우
*/
public function restoreVersion(int $layoutId, int $versionId): TemplateLayoutVersion
{
return DB::transaction(function () use ($layoutId, $versionId) {
// 1. 복원할 버전 조회
$versionToRestore = $this->model
->where('id', $versionId)
->where('layout_id', $layoutId)
->firstOrFail();
// 2. 레이아웃 모델 조회 및 현재 content 저장
$layout = TemplateLayout::findOrFail($layoutId);
$currentContent = $layout->content;
// 3. 레이아웃을 복원할 content로 업데이트
$layout->update([
'content' => $versionToRestore->content,
]);
// 4. 복원 전 content를 새 버전으로 저장 (이력 보존)
return $this->saveVersion($layoutId, $currentContent, $versionToRestore->content);
});
}
}