- Morph map에 Post, Department 모델 등록 (ClassMorphViolationException 해결) - 파일 저장 방식을 API 스타일로 변경 (document_id + document_type) - 파일 미리보기 라우트 및 메서드 추가 (previewFile) - 게시글 상세 페이지에서 이미지 첨부파일을 본문 상단에 풀 너비로 표시 - 비이미지 첨부파일은 하단에 다운로드 목록으로 분리 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <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\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');
|
|
}
|
|
|
|
/**
|
|
* 첨부파일 (document_id + document_type 기반)
|
|
*/
|
|
public function files(): HasMany
|
|
{
|
|
return $this->hasMany(File::class, 'document_id')
|
|
->where('document_type', 'post');
|
|
}
|
|
|
|
// =========================================================================
|
|
// 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;
|
|
}
|
|
}
|