Files
sam-manage/app/Models/Boards/File.php

239 lines
5.5 KiB
PHP
Raw Normal View History

<?php
namespace App\Models\Boards;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Storage;
/**
* 파일 모델 (Polymorphic)
*
* @property int $id
* @property int|null $tenant_id
* @property int|null $folder_id
* @property bool $is_temp
* @property string $file_path
* @property string|null $display_name
* @property string|null $stored_name
* @property string|null $original_name
* @property int $file_size
* @property string|null $mime_type
* @property string|null $file_type
* @property int|null $fileable_id
* @property string|null $fileable_type
* @property int|null $uploaded_by
*/
class File extends Model
{
use SoftDeletes;
protected $table = 'files';
protected $fillable = [
'tenant_id',
'folder_id',
'is_temp',
'file_path',
'display_name',
'stored_name',
'original_name',
'file_name',
'file_size',
'mime_type',
'file_type',
// New fields (API 방식)
'document_id',
'document_type',
// Legacy fields (하위 호환)
'fileable_id',
'fileable_type',
'description',
'uploaded_by',
'deleted_by',
'created_by',
'updated_by',
// GCS fields
'gcs_object_name',
'gcs_uri',
];
protected $casts = [
'is_temp' => 'boolean',
'file_size' => 'integer',
];
// =========================================================================
// Relationships
// =========================================================================
/**
* Polymorphic 관계 - 연결된 모델 (Post, Product )
*/
public function fileable(): MorphTo
{
return $this->morphTo();
}
/**
* 업로더
*/
public function uploader(): BelongsTo
{
return $this->belongsTo(User::class, 'uploaded_by');
}
/**
* 생성자
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
// =========================================================================
// Helper Methods
// =========================================================================
/**
* 스토리지 전체 경로
*/
public function getStoragePath(): string
{
return Storage::disk('tenant')->path($this->file_path);
}
/**
* 파일 존재 여부
*/
public function existsInStorage(): bool
{
return Storage::disk('tenant')->exists($this->file_path);
}
/**
* 다운로드 응답
*/
public function download()
{
if (! $this->existsInStorage()) {
abort(404, '파일을 찾을 수 없습니다.');
}
$fileName = $this->display_name ?? $this->original_name ?? $this->file_name;
return response()->download($this->getStoragePath(), $fileName);
}
/**
* 파일 URL (public 접근용)
*/
public function getUrl(): ?string
{
if (! $this->existsInStorage()) {
return null;
}
return Storage::disk('tenant')->url($this->file_path);
}
/**
* 파일 크기 포맷팅
*/
public function getFormattedSize(): string
{
$bytes = $this->file_size;
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2).' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2).' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2).' KB';
}
return $bytes.' bytes';
}
/**
* 파일 확장자
*/
public function getExtension(): string
{
$fileName = $this->stored_name ?? $this->file_name ?? $this->original_name;
return pathinfo($fileName, PATHINFO_EXTENSION);
}
/**
* 이미지 여부
*/
public function isImage(): bool
{
return str_starts_with($this->mime_type ?? '', 'image/');
}
/**
* Soft Delete with user tracking
*/
public function softDeleteFile(int $userId): void
{
$this->deleted_by = $userId;
$this->save();
$this->delete();
}
/**
* 완전 삭제 (물리 파일 포함)
*/
public function permanentDelete(): void
{
if ($this->existsInStorage()) {
Storage::disk('tenant')->delete($this->file_path);
}
$this->forceDelete();
}
// =========================================================================
// Scopes
// =========================================================================
/**
* 특정 모델의 파일들
*/
public function scopeForModel($query, string $type, int $id)
{
return $query->where('fileable_type', $type)
->where('fileable_id', $id);
}
/**
* 임시 파일만
*/
public function scopeTemp($query)
{
return $query->where('is_temp', true);
}
/**
* 임시 파일 제외
*/
public function scopeNonTemp($query)
{
return $query->where('is_temp', false);
}
/**
* 특정 문서의 파일들
*/
public function scopeForDocument($query, int $documentId, string $documentType)
{
return $query->where('document_id', $documentId)
->where('document_type', $documentType);
}
}