- File 모델 추가 (Polymorphic 관계) - Post 모델에 files() MorphMany 관계 추가 - PostService 파일 업로드/삭제/다운로드 메서드 추가 - PostController 파일 관련 액션 추가 - 게시글 작성/수정 폼에 드래그앤드롭 파일 업로드 UI - 게시글 상세에 첨부파일 목록 표시 - tenant 디스크 설정 (공유 스토리지) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
212 lines
4.9 KiB
PHP
212 lines
4.9 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Boards;
|
|
|
|
use App\Models\Tenants\Tenant;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
/**
|
|
* 게시글 모델
|
|
*
|
|
* @property int $id
|
|
* @property int|null $tenant_id
|
|
* @property int $board_id
|
|
* @property int $user_id
|
|
* @property string $title
|
|
* @property string|null $content
|
|
* @property string $editor_type
|
|
* @property string|null $ip_address
|
|
* @property bool $is_notice
|
|
* @property bool $is_secret
|
|
* @property int $views
|
|
* @property string $status
|
|
*/
|
|
class Post extends Model
|
|
{
|
|
use SoftDeletes;
|
|
|
|
protected $table = 'posts';
|
|
|
|
protected $fillable = [
|
|
'tenant_id',
|
|
'board_id',
|
|
'user_id',
|
|
'title',
|
|
'content',
|
|
'editor_type',
|
|
'ip_address',
|
|
'is_notice',
|
|
'is_secret',
|
|
'views',
|
|
'status',
|
|
'created_by',
|
|
'updated_by',
|
|
'deleted_by',
|
|
];
|
|
|
|
protected $casts = [
|
|
'is_notice' => 'boolean',
|
|
'is_secret' => 'boolean',
|
|
'views' => 'integer',
|
|
];
|
|
|
|
protected $attributes = [
|
|
'is_notice' => false,
|
|
'is_secret' => false,
|
|
'views' => 0,
|
|
'status' => 'published',
|
|
'editor_type' => 'wysiwyg',
|
|
];
|
|
|
|
// =========================================================================
|
|
// Scopes
|
|
// =========================================================================
|
|
|
|
/**
|
|
* 특정 게시판의 글
|
|
*/
|
|
public function scopeOfBoard(Builder $query, int $boardId): Builder
|
|
{
|
|
return $query->where('board_id', $boardId);
|
|
}
|
|
|
|
/**
|
|
* 공지사항만
|
|
*/
|
|
public function scopeNotices(Builder $query): Builder
|
|
{
|
|
return $query->where('is_notice', true);
|
|
}
|
|
|
|
/**
|
|
* 일반 글만 (공지 제외)
|
|
*/
|
|
public function scopeRegular(Builder $query): Builder
|
|
{
|
|
return $query->where('is_notice', false);
|
|
}
|
|
|
|
/**
|
|
* 게시된 글만
|
|
*/
|
|
public function scopePublished(Builder $query): Builder
|
|
{
|
|
return $query->where('status', 'published');
|
|
}
|
|
|
|
/**
|
|
* 비밀글이 아닌 것만
|
|
*/
|
|
public function scopePublic(Builder $query): Builder
|
|
{
|
|
return $query->where('is_secret', false);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Relationships
|
|
// =========================================================================
|
|
|
|
public function board(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Board::class, 'board_id');
|
|
}
|
|
|
|
public function tenant(): BelongsTo
|
|
{
|
|
return $this->belongsTo(Tenant::class, 'tenant_id');
|
|
}
|
|
|
|
public function author(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'user_id');
|
|
}
|
|
|
|
public function creator(): BelongsTo
|
|
{
|
|
return $this->belongsTo(User::class, 'created_by');
|
|
}
|
|
|
|
public function customFieldValues(): HasMany
|
|
{
|
|
return $this->hasMany(PostCustomFieldValue::class, 'post_id');
|
|
}
|
|
|
|
/**
|
|
* 첨부파일 (Polymorphic)
|
|
*/
|
|
public function files(): MorphMany
|
|
{
|
|
return $this->morphMany(File::class, 'fileable');
|
|
}
|
|
|
|
// =========================================================================
|
|
// Helper Methods
|
|
// =========================================================================
|
|
|
|
/**
|
|
* 조회수 증가
|
|
*/
|
|
public function incrementViews(): void
|
|
{
|
|
$this->increment('views');
|
|
}
|
|
|
|
/**
|
|
* 커스텀 필드 값 가져오기
|
|
*/
|
|
public function getCustomFieldValue(string $fieldKey): ?string
|
|
{
|
|
$fieldValue = $this->customFieldValues()
|
|
->whereHas('field', fn ($q) => $q->where('field_key', $fieldKey))
|
|
->first();
|
|
|
|
return $fieldValue?->value;
|
|
}
|
|
|
|
/**
|
|
* 커스텀 필드 값들을 배열로 가져오기
|
|
*/
|
|
public function getCustomFieldsArray(): array
|
|
{
|
|
return $this->customFieldValues()
|
|
->with('field')
|
|
->get()
|
|
->mapWithKeys(fn ($v) => [$v->field->field_key => $v->value])
|
|
->toArray();
|
|
}
|
|
|
|
/**
|
|
* 비밀글 접근 가능 여부
|
|
*/
|
|
public function canAccess(?User $user): bool
|
|
{
|
|
// 비밀글이 아니면 모두 접근 가능
|
|
if (! $this->is_secret) {
|
|
return true;
|
|
}
|
|
|
|
// 비로그인이면 접근 불가
|
|
if (! $user) {
|
|
return false;
|
|
}
|
|
|
|
// 작성자 본인이면 접근 가능
|
|
if ($this->user_id === $user->id) {
|
|
return true;
|
|
}
|
|
|
|
// 관리자면 접근 가능
|
|
if ($user->hasRole(['admin', 'super-admin'])) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|