*/ protected $fillable = [ 'title', 'slug', 'content', 'excerpt', 'status', 'post_type', 'published_at', 'date', 'time', 'image_url', 'address', 'description', 'link', 'featured_image', ]; /** * The attributes that should be cast. * * @var array */ protected $casts = [ 'published_at' => 'datetime', 'date' => 'date', 'featured_image' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; /** * Default values for attributes. * * @var array */ protected $attributes = [ 'status' => 'draft', 'featured_image' => false, ]; /** * Get the categories that belong to this post. * * @return BelongsToMany */ public function categories(): BelongsToMany { return $this->belongsToMany(Category::class, 'category_post', 'post_id', 'category_id') ->withTimestamps(); } /** * Scope a query to only include posts of a given type. * * @param \Illuminate\Database\Eloquent\Builder $query * @param string $type * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOfType($query, $type) { return $query->where('post_type', $type); } /** * Scope a query to only include news posts. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeNews($query) { return $query->where('post_type', 'news'); } /** * Scope a query to only include event posts. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeEvent($query) { return $query->where('post_type', 'event'); } /** * Scope a query to only include published posts. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopePublished($query) { return $query->where('status', 'publish'); } /** * Scope a query to only include draft posts. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeDraft($query) { return $query->where('status', 'draft'); } /** * Scope a query to filter posts by category. * * @param \Illuminate\Database\Eloquent\Builder $query * @param int|string $category * @return \Illuminate\Database\Eloquent\Builder */ public function scopeByCategory($query, $category) { return $query->whereHas('categories', function ($q) use ($category) { if (is_numeric($category)) { $q->where('categories.id', $category); } else { $q->where('categories.slug', $category); } }); } /** * Scope a query to order posts by published date (descending). * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOrdered($query) { return $query->orderBy('published_at', 'desc')->orderBy('created_at', 'desc'); } /** * Get the full image URL. * * @return string|null */ public function getImageAttribute() { if (empty($this->image_url)) { return null; } // 如果已經是完整 URL,直接返回 if (filter_var($this->image_url, FILTER_VALIDATE_URL)) { return $this->image_url; } // 否則拼接 upload 目錄路徑 return config('app.url') . '/uploads/' . ltrim($this->image_url, '/'); } /** * Check if post is published. * * @return bool */ public function isPublished() { return $this->status === 'publish'; } /** * Check if post is a news post. * * @return bool */ public function isNews() { return $this->post_type === 'news'; } /** * Check if post is an event post. * * @return bool */ public function isEvent() { return $this->post_type === 'event'; } /** * Check if post belongs to a specific category. * * @param int|string $category * @return bool */ public function hasCategory($category) { if (is_numeric($category)) { return $this->categories()->where('categories.id', $category)->exists(); } return $this->categories()->where('categories.slug', $category)->exists(); } }