Laravel 系列 - 2: 與資料庫的互動

更新於 發佈於 閱讀時間約 1 分鐘

這篇文章將帶你了解 Laravel 與資料庫之間的互動方式。

Model 的建立與操作

在許多專案中,當資料庫需要與應用程式互動時,我們通常會建立資料庫模型(Model),以方便後續的操作。透過模型,我們可以輕鬆進行資料庫的 CRUD 操作,並進一步設置環境配置、添加額外註解,以及提高專案的可維護性,讓後來的開發者能夠更順利地接手。

那麼,該如何建立模型呢?其實過程相當簡單。我們可以透過 artisan 指令來生成模型的基礎框架,然後再根據需求手動調整細節。具體步驟如下:

  1. .env 設定檔中,配置好資料庫的連線參數。
  2. 將資料表的 DDL 資訊複製到 schema 檔案中。[ 註1 ]
  3. 執行指令 php artisan make:model 表位置 --migration 來生成模型及對應的遷移檔案。
  4. 手動調整生成的檔案內容,特別是設定表格名稱、主鍵及其他必要的資料庫設定。[ 註2 ]
  5. 執行指令 php artisan ide-helper:models "App\\Models\\剛剛的表位置",生成 IDE 的輔助提示。
  6. 最後,為模型的屬性加上註解,這樣就大功完成啦!

需要注意的是,Laravel 預設不支援複合主鍵的使用,因此建議可以選擇使用 UUID 來替代複合主鍵的需求。

Model 間的關聯

簡單的關聯 —— 使用 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,並使用條件過濾結果。

複雜的關聯 —— 使用 Eloquent 來處理 X 對 X 關係

當資料庫中有一些經常使用的表格關聯,每次撰寫查詢時都需要重複建立連結,這樣的做法容易變得繁瑣。此時,我們可以選擇在模型層定義好這些關聯,這樣就不需要每次都寫 join,只需在查詢時直接調用關聯即可。

在前面提到的模型建立過程中,我們可以在模型內定義各種資料表之間的關聯,比如一對一、一對多、多對一以及多對多等關係。這些關聯定義好後,當我們在取得資料時,就可以自動獲取與這筆資料相關聯的其他資料。舉例來說:

// 模型定義
class A extends Model
{
// 假設 A 對 B 是一對多關係
public function children(): HasMany
{
return $this->hasMany(B::class);
}
}

// 別處使用時
$myChildren = A::find(1)->children; //這樣就能取得 A 模型關聯的 B 模型資料

使用 Eloquent 關聯的注意事項

這種方式非常方便,但需要注意的是,關聯式資料表適合每個資料表各司其職且資料不冗餘,並且關聯資料通常是每次查詢時都會需要的。如果不是這樣的情況,過多的關聯可能會讓查詢變得過於複雜且性能不佳。

因此,若是某些關聯並非每次都需要,建議還是使用 join 的方式來動態建立查詢,這樣可以避免不必要的資料提取,保持查詢效率。

N + 1 問題

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 問題,我們可以採取以下兩種主要的解決方案:

  1. 使用 join 查詢
    我們可以使用 join 查詢來一次性取得顧客和其消費紀錄的資料,這樣可以減少資料庫的查詢次數,從而提高效能。例如:
    $users = User::select('users.*')
    ->leftJoin('buy_logs', 'users.id', '=', 'buy_logs.user_id')
    ->get();
    這樣,資料庫只會發送一次查詢來獲取顧客及其相關的消費紀錄,避免了重複的查詢。
  2. 使用 eager loading(預載入)
    Laravel 提供了 with() 方法來進行預載入(eager loading),可以一次性載入所有關聯資料,從而避免發生 N + 1 問題。使用方法如下:
    $users = User::with('buyLogs')->get();
    這樣,Laravel 會使用兩次查詢來加載所有顧客及其消費紀錄:一次查詢顧客資料,另一次查詢所有消費紀錄。這樣可以顯著減少查詢次數,避免每次顧客資料載入時再去查詢相關的消費紀錄。
  3. 使用緩存
    除了改善查詢邏輯,另一種解決方法是將不常變動的資料緩存起來。對於那些查詢頻繁但變動不大的資料,我們可以使用 Laravel 的緩存機制,將資料儲存在緩存中,避免重複查詢資料庫。例如,可以使用 cache 函數來緩存資料:
    $users = Cache::remember('users_with_buy_logs', 60, function () {
    return User::with('buyLogs')->get();
    });
    這樣查詢結果會被緩存 60 分鐘,在此期間不會再次查詢資料庫。這對於讀取頻繁且變動不大的資料來說,是一種非常有效的優化方式喔!

[ 註1 ] 複製 DDL 資訊後,記得檢查最上方的 CREATE DATABASE 語句,並將其調整為 CREATE DATABASE IF NOT EXISTS '表名稱'。這樣可以避免每次啟動應用程式時都重新建立同名資料表。

[ 註2 ] 如果需要,這時候你也可以在模型中加入軟刪除(Soft Deletes)的設定。詳細資訊請參見 Laravel 官方文檔

參考資料

  1. https://tools.wingzero.tw/article/sn/527
  2. https://inbound.technology/lavarel中如何建立資料庫/
  3. https://ithelp.ithome.com.tw/articles/10243718
  4. https://laravel.com/api/8.x/Illuminate/Database/Eloquent/SoftDeletes.html
  5. https://laravel.com/docs/10.x/eloquent-relationships#introduction
avatar-img
2會員
34內容數
test
留言
avatar-img
留言分享你的想法!

































































林柏宇的沙龍 的其他內容
本篇文章介紹了Laravel這個開源PHP框架,重點解析其MVC架構及相關功能,包括中介層、服務層、數據傳輸對象(DTO)與值對象(VO)。Laravel的穩定性與擴展性使其成為臺灣許多企業的首選框架,並提供升級與編碼風格的資源參考,適合所有PHP開發者瞭解和掌握。
本文探討控制反轉(IoC)和依賴注入的設計原則,解釋如何利用這些理念降低程式碼的耦合度,提高可擴展性和測試性。通過案例說明,控制反轉改變了程式控制的方式,並介紹依賴注入的三種注入方式及其在 Laravel 框架中的應用。讀者將能夠理解這些設計原則在實際專案中的重要性。
本文章深入探討物件導向程式設計的三大核心特性,包括封裝、多型和繼承。這些特性對於建立模組化、可擴充以及易於維護的程式架構至關重要。封裝確保資料的隱私和完整性,多型實現靈活的系統設計,繼承則促進程式碼的重用。瞭解這些概念將幫助開發者設計更高效的程式系統。
本文探討物件導向編程及其在面試中常見的必備知識,特別是物件導向與程序導向的基本概念與特性分析。此外,介紹了物件導向的五大原則——SOLID,分別為單一功能原則、開放封閉原則、里氏替換原則、介面隔離原則及依賴反轉原則,重點在於提高程式的維護性與擴展性。這些原則對於現代軟體設計具有重要的指導意義。
本文章詳細介紹了 PHP 的依賴管理工具 Composer 的運作原理,包括如何使用 Composer 進行下載、更新依賴的流程,並提供了在專案中正確運用這個工具的實用建議。透過本文,讀者將能夠更好地理解 Composer 的重要性及其背後的機制,確保專案運行的穩定性。
本文探討PHP中的類別、介面和抽象類別的基本概念與差異。雖然這些知識可能對初學者幫助不大,但對於資深工程師來說,理解這些概念有助於建立更加優雅的架構和維持代碼的整潔。內容涵蓋了類別的封裝、繼承、多型,及介面和Traits的使用規範,提供程式設計師在日常開發中的參考和指導。
本篇文章介紹了Laravel這個開源PHP框架,重點解析其MVC架構及相關功能,包括中介層、服務層、數據傳輸對象(DTO)與值對象(VO)。Laravel的穩定性與擴展性使其成為臺灣許多企業的首選框架,並提供升級與編碼風格的資源參考,適合所有PHP開發者瞭解和掌握。
本文探討控制反轉(IoC)和依賴注入的設計原則,解釋如何利用這些理念降低程式碼的耦合度,提高可擴展性和測試性。通過案例說明,控制反轉改變了程式控制的方式,並介紹依賴注入的三種注入方式及其在 Laravel 框架中的應用。讀者將能夠理解這些設計原則在實際專案中的重要性。
本文章深入探討物件導向程式設計的三大核心特性,包括封裝、多型和繼承。這些特性對於建立模組化、可擴充以及易於維護的程式架構至關重要。封裝確保資料的隱私和完整性,多型實現靈活的系統設計,繼承則促進程式碼的重用。瞭解這些概念將幫助開發者設計更高效的程式系統。
本文探討物件導向編程及其在面試中常見的必備知識,特別是物件導向與程序導向的基本概念與特性分析。此外,介紹了物件導向的五大原則——SOLID,分別為單一功能原則、開放封閉原則、里氏替換原則、介面隔離原則及依賴反轉原則,重點在於提高程式的維護性與擴展性。這些原則對於現代軟體設計具有重要的指導意義。
本文章詳細介紹了 PHP 的依賴管理工具 Composer 的運作原理,包括如何使用 Composer 進行下載、更新依賴的流程,並提供了在專案中正確運用這個工具的實用建議。透過本文,讀者將能夠更好地理解 Composer 的重要性及其背後的機制,確保專案運行的穩定性。
本文探討PHP中的類別、介面和抽象類別的基本概念與差異。雖然這些知識可能對初學者幫助不大,但對於資深工程師來說,理解這些概念有助於建立更加優雅的架構和維持代碼的整潔。內容涵蓋了類別的封裝、繼承、多型,及介面和Traits的使用規範,提供程式設計師在日常開發中的參考和指導。
你可能也想看
Google News 追蹤
Thumbnail
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
※ ORM 是什麼?ORM 的優缺點是什麼? ORM 是什麼? ORM 專用於關聯式資料庫 (relational database)一種叫「物件映射 (object mapping)」 的技術,主要是用程式語言裡的「物件」來包裝資料庫的 SQL (structured query langua
Thumbnail
有的時候,會希望在物件導向中對原生的Class新增功能的時候,大多我們都會寫一個新的class並繼承。 但是其實Laravel提供了一個不同的方式,讓我們可以在常用的Class上,直接新增想要的function,那就是macro。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
※ 基本操作:SQL 語法,SELECT, WHERE, CREATE, UPDATE, DELETE。 SELECT:從資料庫中或資料表中指定要選擇的欄位中取得資料,稱之為查詢 (query)。 ※ 語法:要由兩部分構成,第一部分是要 "拿什麼" 資料 (若有多項用逗號隔開);第二部分則為
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更
在前一篇文章中,我們探討了多重資料庫連線情境下,Model 及 Database Assertion 的應對方式,不過實際上筆者認為比較有難度的,其實是 Migration 應對方式。 今天就讓我們來探討這部分吧! Migration 應對方式 對於多重資料庫連線這種情境,筆者實務上做過的對應
在實務情境上,常會有在單一專案程式庫中,存取多個不同資料庫的使用情境,在這種情況下,我們通常會設置多個資料庫連線(Database Connection)設定。 在平常開發使用設很方便,但要做測試時就會發現一些問題: 在測試程式碼或 Seeder 中調用 factory() 時,都是在預設連線資
在之前的文章中,我們分別演練了 API 測試與資料庫測試,今天則讓我們停下腳步,來介紹一些 Laravel 提供的,與 自動化測試有關的 Trait 吧! DatabaseMigrations 當我們使用了這個 Trait 後,會在每個測試被執行前,先執行 migrate ,接著在測試被執行後,
前置: Factory & UserRepository 在開始實作資料庫測試之前,先與大家介紹 Factory 這個東西。 Factory 是個 Laravel 的 ORM:Eloquent 提供的功能,它可以讓我們用很簡單的方式,去準備測試資料,在 Laravel 初始化後,預設已經幫我們準
Thumbnail
CodeIgniter 3 和 Laravel 是兩種不同的 PHP 框架,各有其特點和適用場景。CodeIgniter 3 是一個輕量級框架,Laravel 是一個功能強大的現代 PHP 框架,同樣都有Models的它們有什麼樣的差別呢?
※ ORM 是什麼?ORM 的優缺點是什麼? ORM 是什麼? ORM 專用於關聯式資料庫 (relational database)一種叫「物件映射 (object mapping)」 的技術,主要是用程式語言裡的「物件」來包裝資料庫的 SQL (structured query langua
Thumbnail
有的時候,會希望在物件導向中對原生的Class新增功能的時候,大多我們都會寫一個新的class並繼承。 但是其實Laravel提供了一個不同的方式,讓我們可以在常用的Class上,直接新增想要的function,那就是macro。
Thumbnail
有趣的是,Model 其實沒什麼嚴格的定義,所以每個人對 Model 的解讀也不盡相同,有人覺得資料怎麼儲存屬於 Model 的一部份 (受 ORM 工具的影響),有人覺得工作流程 (workflow) 是 Model 的一部份,我個人也有自己的想法,而且隨專案的規模和特性,也不是總是一樣的。
Thumbnail
※ 基本操作:SQL 語法,SELECT, WHERE, CREATE, UPDATE, DELETE。 SELECT:從資料庫中或資料表中指定要選擇的欄位中取得資料,稱之為查詢 (query)。 ※ 語法:要由兩部分構成,第一部分是要 "拿什麼" 資料 (若有多項用逗號隔開);第二部分則為
Migration在 Laravel 中是一種用來管理資料庫結構變更的機制。它的主要目的是使開發者能夠在應用程序的不同環境中保持資料庫結構的一致性,並輕鬆地進行結構變更
在前一篇文章中,我們探討了多重資料庫連線情境下,Model 及 Database Assertion 的應對方式,不過實際上筆者認為比較有難度的,其實是 Migration 應對方式。 今天就讓我們來探討這部分吧! Migration 應對方式 對於多重資料庫連線這種情境,筆者實務上做過的對應
在實務情境上,常會有在單一專案程式庫中,存取多個不同資料庫的使用情境,在這種情況下,我們通常會設置多個資料庫連線(Database Connection)設定。 在平常開發使用設很方便,但要做測試時就會發現一些問題: 在測試程式碼或 Seeder 中調用 factory() 時,都是在預設連線資
在之前的文章中,我們分別演練了 API 測試與資料庫測試,今天則讓我們停下腳步,來介紹一些 Laravel 提供的,與 自動化測試有關的 Trait 吧! DatabaseMigrations 當我們使用了這個 Trait 後,會在每個測試被執行前,先執行 migrate ,接著在測試被執行後,
前置: Factory & UserRepository 在開始實作資料庫測試之前,先與大家介紹 Factory 這個東西。 Factory 是個 Laravel 的 ORM:Eloquent 提供的功能,它可以讓我們用很簡單的方式,去準備測試資料,在 Laravel 初始化後,預設已經幫我們準