這篇文章將帶你了解 Laravel 與資料庫之間的互動方式。
在許多專案中,當資料庫需要與應用程式互動時,我們通常會建立資料庫模型(Model),以方便後續的操作。透過模型,我們可以輕鬆進行資料庫的 CRUD 操作,並進一步設置環境配置、添加額外註解,以及提高專案的可維護性,讓後來的開發者能夠更順利地接手。
那麼,該如何建立模型呢?其實過程相當簡單。我們可以透過 artisan
指令來生成模型的基礎框架,然後再根據需求手動調整細節。具體步驟如下:
.env
設定檔中,配置好資料庫的連線參數。schema
檔案中。[ 註1 ]php artisan make:model 表位置 --migration
來生成模型及對應的遷移檔案。php artisan ide-helper:models "App\\Models\\剛剛的表位置"
,生成 IDE 的輔助提示。需要注意的是,Laravel 預設不支援複合主鍵的使用,因此建議可以選擇使用 UUID 來替代複合主鍵的需求。
join
在使用 SQL 查詢時,我們通常不會只查詢一張表格,而是會有多張表格需要聯結(join),以便整合出我們需要的資料。在 Laravel 中,這樣的查詢方式也同樣簡單,我們只需要使用 join
指令。
假設我們在組成查詢的 query
時,需要將一張表格左連結(left join)到另一張表格,我們可以直接使用 ->leftJoin()
來實現,並在後面加上相關的條件篩選。舉個例子:
$query->leftJoin('other_table', 'a.id', '=', 'other_table.aid')
->whereIn('other_table.status', $myArray);
這樣就能在查詢中左連結 other_table
,並使用條件過濾結果。
當資料庫中有一些經常使用的表格關聯,每次撰寫查詢時都需要重複建立連結,這樣的做法容易變得繁瑣。此時,我們可以選擇在模型層定義好這些關聯,這樣就不需要每次都寫 join
,只需在查詢時直接調用關聯即可。
在前面提到的模型建立過程中,我們可以在模型內定義各種資料表之間的關聯,比如一對一、一對多、多對一以及多對多等關係。這些關聯定義好後,當我們在取得資料時,就可以自動獲取與這筆資料相關聯的其他資料。舉例來說:
// 模型定義
class A extends Model
{
// 假設 A 對 B 是一對多關係
public function children(): HasMany
{
return $this->hasMany(B::class);
}
}
// 別處使用時
$myChildren = A::find(1)->children; //這樣就能取得 A 模型關聯的 B 模型資料
這種方式非常方便,但需要注意的是,關聯式資料表適合每個資料表各司其職且資料不冗餘,並且關聯資料通常是每次查詢時都會需要的。如果不是這樣的情況,過多的關聯可能會讓查詢變得過於複雜且性能不佳。
因此,若是某些關聯並非每次都需要,建議還是使用 join
的方式來動態建立查詢,這樣可以避免不必要的資料提取,保持查詢效率。
N + 1 問題是 ORM 中常見的一個性能問題,當資料庫查詢不當時,可能會導致系統效能顯著下降。
簡單來說,N + 1 問題會發生在當模型之間存在一對多關係,並且系統的架構設計不夠完善時,例如資料表中的欄位設計不夠精簡、關聯不夠嚴謹等情形。具體來說,每當你查詢一筆資料時,系統會額外去查詢 N 筆關聯資料,這樣會導致資料庫的負擔大幅增加,從而影響效能。
假設有一個顧客(User)模型,並且顧客擁有多筆消費紀錄(BuyLog)。首先查詢顧客資料(1 筆),然後對每一筆顧客資料都再發送一次查詢來獲取對應的消費紀錄(N 筆)。結果會導致總共執行 1 + N 次查詢,從而加重資料庫的負擔,進而影響效能。
// 顧客模型
class User extends Model
{
public function buyLogs()
{
return $this->hasMany(BuyLog::class);
}
}
//當我們使用以下程式碼查詢所有顧客及其消費紀錄時,可能會遇到 N + 1 問題:
$users = User::all();
foreach ($users as $user) {
echo $user->buyLogs;
}
為了避免 N + 1 問題,我們可以採取以下兩種主要的解決方案:
join
查詢來一次性取得顧客和其消費紀錄的資料,這樣可以減少資料庫的查詢次數,從而提高效能。例如:這樣,資料庫只會發送一次查詢來獲取顧客及其相關的消費紀錄,避免了重複的查詢。$users = User::select('users.*')
->leftJoin('buy_logs', 'users.id', '=', 'buy_logs.user_id')
->get();
with()
方法來進行預載入(eager loading),可以一次性載入所有關聯資料,從而避免發生 N + 1 問題。使用方法如下:這樣,Laravel 會使用兩次查詢來加載所有顧客及其消費紀錄:一次查詢顧客資料,另一次查詢所有消費紀錄。這樣可以顯著減少查詢次數,避免每次顧客資料載入時再去查詢相關的消費紀錄。$users = User::with('buyLogs')->get();
cache
函數來緩存資料:這樣查詢結果會被緩存 60 分鐘,在此期間不會再次查詢資料庫。這對於讀取頻繁且變動不大的資料來說,是一種非常有效的優化方式喔!$users = Cache::remember('users_with_buy_logs', 60, function () {
return User::with('buyLogs')->get();
});
[ 註1 ] 複製 DDL 資訊後,記得檢查最上方的 CREATE DATABASE
語句,並將其調整為 CREATE DATABASE IF NOT EXISTS '表名稱'
。這樣可以避免每次啟動應用程式時都重新建立同名資料表。
[ 註2 ] 如果需要,這時候你也可以在模型中加入軟刪除(Soft Deletes)的設定。詳細資訊請參見 Laravel 官方文檔。