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

214 lines
5.1 KiB
PHP

<?php
namespace App\Models;
use App\Enums\AttachmentSourceType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
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;
use Illuminate\Support\Str;
/**
* 첨부파일 모델
*
* 다형성 구조로 여러 엔티티에서 공용으로 사용할 수 있는 첨부파일 모델입니다.
*/
class Attachment extends Model
{
use HasFactory, SoftDeletes;
/**
* 대량 할당 가능한 속성
*
* @var array<int, string>
*/
protected $fillable = [
'attachmentable_type',
'attachmentable_id',
'source_type',
'source_identifier',
'hash',
'original_filename',
'stored_filename',
'disk',
'path',
'mime_type',
'size',
'collection',
'order',
'meta',
'created_by',
];
/**
* 속성 캐스팅
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'meta' => 'array',
'source_type' => AttachmentSourceType::class,
'size' => 'integer',
'order' => 'integer',
'attachmentable_id' => 'integer',
];
}
/**
* 모델 부팅 시 해시 자동 생성
*/
protected static function boot(): void
{
parent::boot();
static::creating(function (Attachment $attachment) {
if (empty($attachment->hash)) {
$attachment->hash = self::generateUniqueHash();
}
});
}
/**
* 고유한 12자 해시 생성
*
* @return string 고유 해시
*/
public static function generateUniqueHash(): string
{
do {
$hash = Str::random(12);
} while (self::where('hash', $hash)->exists());
return $hash;
}
/**
* 다형성 관계 - 첨부 대상
*/
public function attachmentable(): MorphTo
{
return $this->morphTo();
}
/**
* 업로더 관계
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
/**
* 파일의 전체 경로 반환
*
* @return string 전체 파일 경로
*/
public function getFullPathAttribute(): string
{
return Storage::disk($this->disk)->path($this->path);
}
/**
* 다운로드 URL 반환 (해시 기반)
*
* @return string 다운로드 URL
*/
public function getDownloadUrlAttribute(): string
{
return '/api/attachment/'.$this->hash;
}
/**
* 이미지 여부 확인
*
* @return bool 이미지 여부
*/
public function getIsImageAttribute(): bool
{
return str_starts_with($this->mime_type, 'image/');
}
/**
* 파일 크기 포맷팅 (KB, MB 등)
*
* @return string 포맷된 파일 크기
*/
public function getSizeFormattedAttribute(): string
{
$bytes = $this->size;
$units = ['B', 'KB', 'MB', 'GB'];
$i = 0;
while ($bytes >= 1024 && $i < count($units) - 1) {
$bytes /= 1024;
$i++;
}
return round($bytes, 2).' '.$units[$i];
}
/**
* 특정 컬렉션으로 필터링하는 스코프
*
* @param Builder $query 쿼리 빌더
* @param string $collection 컬렉션명
* @return Builder 필터링된 쿼리 빌더
*/
public function scopeCollection(Builder $query, string $collection): Builder
{
return $query->where('collection', $collection);
}
/**
* 특정 소스 타입으로 필터링하는 스코프
*
* @param Builder $query 쿼리 빌더
* @param AttachmentSourceType $sourceType 소스 타입
* @return Builder 필터링된 쿼리 빌더
*/
public function scopeBySourceType(Builder $query, AttachmentSourceType $sourceType): Builder
{
return $query->where('source_type', $sourceType);
}
/**
* 특정 소스 식별자로 필터링하는 스코프
*
* @param Builder $query 쿼리 빌더
* @param string $identifier 소스 식별자
* @return Builder 필터링된 쿼리 빌더
*/
public function scopeBySourceIdentifier(Builder $query, string $identifier): Builder
{
return $query->where('source_identifier', $identifier);
}
/**
* 이미지만 필터링하는 스코프
*
* @param Builder $query 쿼리 빌더
* @return Builder 필터링된 쿼리 빌더
*/
public function scopeImages(Builder $query): Builder
{
return $query->where('mime_type', 'like', 'image/%');
}
/**
* 정렬된 상태로 조회하는 스코프
*
* @param Builder $query 쿼리 빌더
* @return Builder 정렬된 쿼리 빌더
*/
public function scopeOrdered(Builder $query): Builder
{
return $query->orderBy('order')->orderBy('id');
}
}