Files
Gnuboard7/modules/_bundled/sirsoft-ecommerce/src/Models/ProductImage.php
T
HeuJung 057e002ad1 feat(core,ecommerce,ckeditor5,board,admin_basic): 공개 자산 직접 URL(CDN) 서빙 옵트인
StorageInterface::url 의 public 하드코딩을 완화하고 core.storage.filter_url 훅을
신설해, 완전 공개 자산(상품·카테고리·리뷰·에디터 이미지)의 직접 URL 서빙을 옵트인으로
열었다. 미설정 시 기존 스트리밍 100% 보존.

디스크 결정은 확장 개별 설정 > 코어 전역 > 미설정 순이며, 서빙·삭제·이동은 행에 기록된
disk 를 따라 전환 전후 자산이 혼재해도 각자 실제 저장 위치를 향한다. 고아 disk(공급
플러그인 비활성화)는 스트리밍으로 폴백해 500 이 되지 않는다.

공개 이슈 (@lyg-kaban 건의) 대응.
2026-08-14 08:05:54 +09:00

152 lines
3.9 KiB
PHP

<?php
namespace Modules\Sirsoft\Ecommerce\Models;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\Sirsoft\Ecommerce\Enums\ProductImageCollection;
use Modules\Sirsoft\Ecommerce\Models\Concerns\HasDirectAssetUrl;
/**
* 상품 이미지 모델
*/
class ProductImage extends Model
{
use HasDirectAssetUrl;
use SoftDeletes;
protected $table = 'ecommerce_product_images';
/**
* 모델 부트 메서드
*/
protected static function boot(): void
{
parent::boot();
// 생성 시 hash 자동 생성 (URL용 고유 키)
static::creating(function (self $model) {
if (empty($model->hash)) {
$model->hash = self::generateHash();
}
});
}
/**
* 고유 해시를 생성합니다.
*
* @return string 12자리 고유 해시
*/
public static function generateHash(): string
{
do {
$hash = substr(bin2hex(random_bytes(6)), 0, 12);
} while (self::where('hash', $hash)->exists());
return $hash;
}
protected $fillable = [
'product_id',
'temp_key',
'hash',
'original_filename',
'stored_filename',
'disk',
'path',
'mime_type',
'file_size',
'width',
'height',
'alt_text',
'collection',
'is_thumbnail',
'sort_order',
'created_by',
];
protected $casts = [
'alt_text' => 'array',
'collection' => ProductImageCollection::class,
'is_thumbnail' => 'boolean',
'sort_order' => 'integer',
'width' => 'integer',
'height' => 'integer',
'file_size' => 'integer',
];
/**
* 이 이미지가 속한 상품 관계 (product_id FK).
*
* @return BelongsTo 상품 관계
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class, 'product_id');
}
/**
* 이 이미지를 업로드한 사용자 관계 (created_by FK).
*
* @return BelongsTo 업로더 사용자 관계
*/
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'created_by');
}
/**
* 현재 로케일의 대체 텍스트(alt)를 반환합니다 (다국어 fallback chain 적용).
*
* @param string|null $locale 반환할 로케일. null 이면 현재 앱 로케일 사용
* @return string|null 로케일별 alt 텍스트, alt_text 가 비어있으면 null
*/
public function getLocalizedAltText(?string $locale = null): ?string
{
if (empty($this->alt_text)) {
return null;
}
$locale = $locale ?? app()->getLocale();
$altText = $this->alt_text;
return $altText[$locale] ?? $altText[config('app.fallback_locale', 'ko')] ?? null;
}
/**
* 특정 컬렉션(main/gallery/detail 등) 의 이미지만 필터링하는 쿼리 스코프.
*
* @param Builder $query
* @param ProductImageCollection $collection 필터링할 이미지 컬렉션 enum
* @return Builder
*/
public function scopeByCollection($query, ProductImageCollection $collection)
{
return $query->where('collection', $collection);
}
/**
* 대표 이미지만 조회
*
* @param Builder $query
* @return Builder
*/
public function scopeThumbnails($query)
{
return $query->where('is_thumbnail', true);
}
/**
* hash 기반 상품 이미지 서빙 API URL (직접 URL 불가 시 폴백).
*
* @return string `/api/modules/sirsoft-ecommerce/product-image/{hash}` 형식 URL
*/
protected function apiDownloadUrl(): string
{
return '/api/modules/sirsoft-ecommerce/product-image/'.$this->hash;
}
}