假設有一個部落格,想檢索兩個日期之間的所有貼文。聽起來很熟悉?那具體該怎麼做呢?

一般的做法
這看起來確實是一個微不足道的問題,答案也很簡單(但完全錯誤):只需使用 BETWEEN(或在 Laravel 中使用 whereBetween),如下所示:
$startDate = '2025-06-01';
$endDate = '2025-06-30';
$posts = Post::whereBetween( $published_at, [$startDate, $endDate])->get();大部份的人都這樣做過(至少我這樣做過),檢索 6 月發布的所有帖子。這裡的問題是,published_at 列通常是 Datetime 類型,所以它不僅僅是一個簡單的日期,還包含一個時間。這意味著實際上 2025-06-30 日發布的文章不會被檢索到,因為它們的發布日期總是大於 2025-06-30(SQL 會將其理解為“2025-06-30 00:00:00”)。
$startDate = Carbon::createFromFormat('Y-m-d', '2025-06-01');
$endDate = Carbon::createFromFormat('Y-m-d', '2025-06-30');
$posts = Post::whereBetween('published_at', [$startDate, $endDate])->get();這實際上更糟糕,因為它變得完全不可預測。 Carbon 實例代表一個時刻,它也有一個時間,除非不指定時間,否則它會預設為運行時的當前時間。所以,如果在上午 9 點運行時,而文章是在 30 號上午 8 點發布的,你就能檢索到它……但是,如果是在上午 7 點運行完全相同的腳本,將無法再檢索到該文章,因為 $endDate 實際上是“2025-06-30 07:00:00”。
我們可以使用 $endDate->toDateString() 來去除時間,但最終還是會回到上面的情況。
較好的 Carbon 利用方法
一種解決方案是確保在查詢中指定一個時間,並且該時間對於開始日期來說是一天的開始時間(00:00:00),對於結束日期來說是一天的結束時間(23:59:59.999999)。
幸運的是,Carbon 提供了 startOfDay() 和 endOfDay() 方法來實現這一點:
$startDate = Carbon::createFromFormat('Y-m-d', '2025-06-01')->startOfDay();
$endDate = Carbon::createFromFormat('Y-m-d', '2025-06-30')->endOfDay();
$posts = Post::whereBetween('published_at', [$startDate, $endDate])->get();另一種方法: Filter
Laravel 提供很多方法(Available Methods),也可以先將資料以 where 條件篩選出來,透過 get() 取得資料後,再使用 filter() 作細部篩選。
$data = BlogPost::where([
['status', '=', 'published'],
['visible', '=', 'public']
])->get()
->filter(function ($item) {
if (isset($item->published_at) && isset($item->unpublished_at)) {
if ( Carbon::now()->between($item->published_at, $item->unpublished_at) ) {
return $item;
}
} else if (isset($item->published_at) && !isset($item->unpublished_at)) {
if ( Carbon::now() > $item->published_at ) {
return $item;
}
} else {
return;
}
});在程式中,使用 Carbon::now() 取得當時的時間,再與 published_at & unpublished_at 作比較,這也可以得到發佈與下架日期之間的文章。
Eloquent 其它種方法
Eloquent 提供了一個非常有用的 whereDate() 方法,它可以做兩件事:
- 建立 SQL 查詢,使用 DATE() 的 SQL 函數將列內容格式化為 Y-m-d 格式。
- 在比較 Carbon 或 Datetime 物件之前,將其正確地轉換為 Y-m-d 格式。
使用這個方法,我們可以放心地傳遞 Carbon 實例,並且知道其中包含的任何時間都會被丟棄,我們實際上是在兩個日期之間進行搜尋:
$startDate = Carbon::createFromFormat('Y-m-d', '2025-06-01');
$endDate = Carbon::createFromFormat('Y-m-d', '2025-06-30');
$posts = Post::query()
->whereDate('published_at', '>=', $startDate)
->whereDate('published_at', '<=', $endDate)
->get();這將產生以下 SQL 查詢:
SELECT * from "posts"
WHERE DATE("published_at") >= '2025-06-01'
AND DATE("published_at") <= '2025-06-30';
