AI 正在初次分析文章並整理建議,請稍候…
拒絕手寫 SQL 地獄!2026 Laravel Eloquent ORM 終極指南:從 N+1 問題到高效能查詢的優雅煉金術
嗨,我是 Eric,浪花科技的資深工程師。如果你在 2026 年還在 PHP 裡面瘋狂手寫 SELECT * FROM,然後用字串串接來處理關聯資料,那你的鍵盤可能已經在哭泣了。Laravel 的 Eloquent ORM 不僅僅是一個資料庫抽象層,它是讓你的程式碼從「義大利麵」進化成「米其林擺盤」的關鍵。今天這篇不講廢話,直接帶你深入 Eloquent 的核心,從基礎用法到資深工程師才會注意的效能陷阱,一次講清楚。
什麼是 Eloquent ORM?為什麼 2026 年我們還在用?
Eloquent 是 Laravel 內建的 ORM (Object-Relational Mapper),它實作了 Active Record 模式。簡單來說,它讓資料庫中的每一個「資料表 (Table)」都有一個對應的「模型 (Model)」類別。你不用寫 SQL 指令,而是像操作物件一樣操作資料庫。
雖然到了 2026 年,甚至出現了 Google Antigravity 這種 AI 輔助開發工具,但理解底層邏輯依然重要。AI 可以幫你寫 Code,但它不一定知道你的查詢會不會把伺服器記憶體吃光。Eloquent 的語法糖 (Syntactic Sugar) 非常甜,但如果吃太多(例如濫用查詢),也是會得糖尿病(效能低落)的。
1. 定義模型與基礎 CRUD:優雅的起手式
在 Laravel 12 或更高版本中,定義一個模型依然直觀。假設我們有一個 posts 資料表,對應的 Model 如下:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
// 指定資料表名稱 (如果是慣例命名可省略)
protected $table = 'posts';
// 允許被批量賦值的欄位 (Mass Assignment)
protected $fillable = ['title', 'content', 'user_id', 'is_published'];
// 預設轉換型別 (Casting),2026 年必備的型別安全
protected $casts = [
'is_published' => 'boolean',
'published_at' => 'datetime',
];
}
有了這個 Model,你的 CRUD (新增、讀取、更新、刪除) 操作就會變得異常優雅:
- 建立:
Post::create(['title' => 'Laravel ORM 教學', ...]); - 讀取:
$posts = Post::where('is_published', true)->orderBy('created_at', 'desc')->get(); - 更新:
$post->update(['title' => '新標題']); - 刪除:
$post->delete();
這裡有個工程師的小囉嗦:永遠要設定 $fillable 或 $guarded。在 2026 年,資安意識已經是基本功,Mass Assignment 漏洞如果還出現,真的會被笑掉大牙。
2. 關聯關係 (Relationships):ORM 的靈魂
Eloquent 最強大的地方在於處理資料表之間的關聯。別再手寫 JOIN 了,除非你在做極端效能優化的報表。
一對多 (One To Many)
一個使用者有多篇文章:
// User Model
public function posts()
{
return $this->hasMany(Post::class);
}
// Post Model
public function user()
{
return $this->belongsTo(User::class);
}
多對多 (Many To Many)
文章與標籤 (Tags) 的關係:
// Post Model
public function tags()
{
return $this->belongsToMany(Tag::class);
}
使用起來就像存取屬性一樣自然:$user->posts 會回傳該使用者的所有文章集合 (Collection)。
3. 效能殺手:N+1 問題與 Eager Loading
這是面試資深工程師的必考題,也是導致網站變慢的元兇之一。N+1 問題發生在你迴圈遍歷模型時,對每個模型都執行一次關聯查詢。
❌ 錯誤示範 (Lazy Loading):
$users = User::all(); // 1 次查詢
foreach ($users as $user) {
echo $user->posts->count(); // 執行 N 次查詢 (每個 User 查一次)
}
// 總共查詢次數:1 + N
如果這頁有 100 個使用者,你就對資料庫發送了 101 次查詢。在流量高峰時,資料庫會直接掛給你看。
✅ 正確示範 (Eager Loading):
使用 with() 方法預先載入關聯資料。
$users = User::with('posts')->get(); // 總共只執行 2 次查詢!
foreach ($users as $user) {
echo $user->posts->count(); // 不會再查詢資料庫
}
在 Laravel 10 之後,我們甚至可以在開發環境開啟 Strict Mode,強制禁止 Lazy Loading,這是我強烈建議在 AppServiceProvider 中設定的:
Model::preventLazyLoading(!app()->isProduction());
4. 進階技巧:Scopes 與 Mutators
為了讓程式碼更乾淨 (Clean Code),不要把複雜的 where 條件散落在 Controller 各處。
Scopes (查詢範圍)
在 Model 中定義常用的查詢邏輯:
// Post Model
public function scopePublished($query)
{
return $query->where('is_published', true)->whereNotNull('published_at');
}
// 使用時
$posts = Post::published()->get();
Accessors & Mutators (存取器與修改器)
在 Laravel 新版本中,我們使用 Attribute 類別來定義,這比舊語法更簡潔:
use Illuminate\Database\Eloquent\Casts\Attribute;
protected function title(): Attribute
{
return Attribute::make(
get: fn (string $value) => ucfirst($value), // 取出時自動大寫首字
set: fn (string $value) => strtolower($value), // 存入時自動轉小寫
);
}
5. 何時該拋棄 Eloquent 回歸 SQL?
雖然 Eloquent 很棒,但在 2026 年的大數據場景下,它也不是萬能的。Eloquent 在處理「大量資料匯入」或「複雜報表統計」時,因為要實例化大量物件,記憶體消耗會很大。
- 大量更新: 使用
Post::where(...)->update([...])直接轉成 SQL 執行,不要撈出來迴圈 update。 - 大數據讀取: 使用
chunk()或cursor()來分批處理,避免一次把 10 萬筆資料塞爆 RAM。 - 複雜報表: 如果涉及多層 Group By 和複雜運算,直接寫 Raw SQL 或使用 Query Builder 效能會好得多。
結論
Laravel Eloquent ORM 是現代 PHP 開發者的神兵利器。它讓我們能專注於業務邏輯,而不是 SQL 語法。但就像所有的強大工具一樣,你需要理解它的運作原理才能駕馭它。記住:善用 Eager Loading、封裝 Scope、並在必要時果斷切換回 Query Builder,這就是資深工程師的生存之道。
推薦閱讀
想更深入了解 Laravel 架構與效能優化嗎?這幾篇是你的必讀清單:
- Eloquent 不只是 CRUD!資深工程師揭秘 Laravel ORM 進階戰術與效能黑魔法
- 肥 Controller 瘦不下來?Laravel 後台架構終極對決:Repository vs. Service vs. Action 模式
- 你的 Laravel 專案是技術債炸彈還是傳世藝術品?Laravel 10 專案架構最佳實務指南
你的專案資料庫查詢慢到懷疑人生嗎?或者你需要客製化開發高流量的 Laravel 系統?歡迎聯繫浪花科技,讓我們幫你把系統體質調整到最佳狀態。
常見問題 (FAQ)
Q1: Eloquent ORM 一定比寫 SQL 慢嗎?
在單純的查詢執行時間上,Eloquent 因為需要將資料轉換成物件 (Hydration),確實比原生 SQL 或 Query Builder 稍慢一點點。但在 95% 的 web 應用場景中,這個差異是微乎其微的(毫秒級別)。通常導致慢的主因是寫法不當(如 N+1 問題)或資料庫索引沒設好,而不是 ORM 本身。
Q2: 什麼時候應該使用 `save()` 什麼時候用 `create()`?
`create()` 是批量賦值的方法,需要設定 `$fillable`,適合直接從表單接收陣列資料建立模型。`save()` 則是用於建立新實例後逐一設定屬性,或者更新現有模型。兩者底層都是執行 INSERT 或 UPDATE,視使用情境而定。
Q3: Laravel 的 `with()` 和 `load()` 有什麼不同?
`with()` 是在查詢建構時使用(Eager Loading),它會立即將關聯資料撈出來。`load()` 則是在模型已經被撈出來之後使用(Lazy Eager Loading),如果你已經有一個 User 物件,後來才發現需要用到他的 Posts,這時就可以用 `$user->load(‘posts’)`。






