Eloquent:入門 - Laravel 道場
文章推薦指數: 80 %
所有的Eloquent 模型都繼承 Illuminate\Database\Eloquent\Model 類別。
建立模型實例的最簡單的方法是使用Artisan 指令的 make:model :. php artisan make ...
Documentation
前言
發行說明
升級導引
貢獻導引
API文件
入門
安裝
設定
目錄結構
Homestead
Valet
部署
核心概念
請求的生命週期
服務容器
服務提供者
Facades
Contracts
基礎功能
路由
中介層
CSRF保護
控制器
請求
回應
視圖
產生URL
Session
驗證
錯誤與記錄
前端開發
Blade模板
在地化
前端導引
編譯資源檔
安全性
認證
API認證
授權
加密
雜湊
重置密碼
進階功能
Artisan指令列
廣播
快取
集合
事件
檔案儲存系統
輔助函式
郵件
通知
套件開發
隊列
任務排程
資料庫
入門
查詢建構器
分頁
遷移
資料填充
Redis
EloquentORM
入門
關聯
集合
存取器
API資源
序列化
測試
入門
HTTP測試
瀏覽器測試
資料庫測試
Mocking
官方套件
Cashier
Envoy
Horizon
Passport
Scout
Socialite
展開文件目錄
前言
發行說明
升級導引
貢獻導引
API文件
入門
安裝
設定
目錄結構
Homestead
Valet
部署
核心概念
請求的生命週期
服務容器
服務提供者
Facades
Contracts
基礎功能
路由
中介層
CSRF保護
控制器
請求
回應
視圖
產生URL
Session
驗證
錯誤與記錄
前端開發
Blade模板
在地化
前端導引
編譯資源檔
安全性
認證
API認證
授權
加密
雜湊
重置密碼
進階功能
Artisan指令列
廣播
快取
集合
事件
檔案儲存系統
輔助函式
郵件
通知
套件開發
隊列
任務排程
資料庫
入門
查詢建構器
分頁
遷移
資料填充
Redis
EloquentORM
入門
關聯
集合
存取器
API資源
序列化
測試
入門
HTTP測試
瀏覽器測試
資料庫測試
Mocking
官方套件
Cashier
Envoy
Horizon
Passport
Scout
Socialite
master
5.6
5.5
5.4
5.3
5.2
5.1
5.0
4.2
Eloquent:入門
介紹
定義模型
Eloquent模型慣例
取得模型
集合
分塊結果
取得單一模型或Aggregate
取得Aggregate
插入與更新模型
插入
更新
批量賦值
其他建立方法
刪除模型
軟刪除
查詢被軟刪除的模型
查詢Scope
全域的Scope
局部的Scope
事件
Observer
介紹
Laravel的EloquentORM提供了漂亮、簡潔的ActiveRecord實作來和資料庫互動。
每個資料庫表有一個對應的「模型」可以用來跟資料表互動。
你可以透過模型查詢資料表內的資料,以及新增記錄到資料表中。
在開始之前,一定要在config/database.php中設定一個資料庫連接。
更多資料庫的設定資訊,請查看資料庫設定。
定義模型
開始之前,讓我們先建立一個Eloquent模型。
模型通常放在app目錄,不過你可以自由地把他們放在任何可以透過你的composer.json自動載入的地方。
所有的Eloquent模型都繼承Illuminate\Database\Eloquent\Model類別。
建立模型實例的最簡單的方法是使用Artisan指令的make:model:
phpartisanmake:modelUser
假設當你產生一個模型時,想要產生一個資料庫遷移,可以使用--migration或-m選項:
phpartisanmake:modelUser--migration
phpartisanmake:modelUser-m
Eloquent模型慣例
現在,讓我們來看一個Flight模型的範例,我們將會用它來從flights資料表取回與儲存資訊:
依照慣例,除非明確地指定其他名稱,不然類別的小寫、底線、複數形式會拿來當作資料表的表單名稱。
所以,這個案例中,Eloquent將會假設Flight模型儲存記錄在flights資料表。
你可以在模型上定義一個table屬性,用來指定自訂的資料表:
*
*@varstring
*/
protected$table='my_flights';
}
主鍵
Eloquent也會假設每個資料表有一個主鍵欄位叫做id。
你可以定義一個$primaryKey屬性來覆寫這個慣例。
此外,Eloquent會假設主鍵是一個遞增的整數值,這表示預設的主鍵位自動轉換成int。
如果你希望使用非遞增或非數字的主鍵,務必把模型上的public$incrementing屬性設定為false。
如果你的主鍵不是一個整數,你應該將模型上的protected$keyType屬性設定為string。
時間戳記
預設的Eloquent會預期你的資料表會有created_at和updated_at欄位。
如果你不希望Eloquent自動管理這些欄位,請將模型上的$timestamps屬性設定為false:
*
*@varbool
*/
public$timestamps=false;
}
如果你需要客製化你的時間戳記格式,在你的模型內設定$dateFormat屬性。
這個屬性決定日期如何在資料庫中儲存,以及當模型被序列化成陣列或是JSON時的格式:
*
*@varstring
*/
protected$dateFormat='U';
}
如果你需要自訂欄位名稱,並用來儲存時間戳記,你可以在模型中設定CREATED_AT和UPDATED_AT常數:
如果你想為模型指定不同的連線,請使用$connection屬性:
*
*@varstring
*/
protected$connection='connection-name';
}
取得模型
一旦你建立了一個模型並且將模型關聯到資料表,你就可以從資料庫中取得資料。
把每個Eloquent模型想像成強大的查詢建構器,讓你可以流暢地查詢與模型關聯的資料表。
例如:
name;
}
新增額外的限制
Eloquent的all方法會回傳在模型資料表中所有的結果。
由於每個Eloquent模型可以當作一個查詢建構器,所以你可以在查詢中增加規則,然後透過get方法來取得結果:
$flights=App\Flight::where('active',1)
->orderBy('name','desc')
->take(10)
->get();
{tip}由於Eloquent模型是查詢建構器,應該檢閱所有查詢建構器可用的方法。
你可以在你的Eloquent查詢中使用這其中的任何方法。
集合
像是all和get能夠取得多個結果的Eloquent方法,會回傳Illuminate\Database\Eloquent\Collection實例。
Collection類別為處理你的Eloquent結果提供各種有用的方法:
$flights=$flights->reject(function($flight){
return$flight->cancelled;
});
當然,你也可以像陣列一樣簡單地遍歷集合:
foreach($flightsas$flight){
echo$flight->name;
}
分塊結果
如果你需要處理上千筆Eloquent查詢結果,可以使用chunk命令。
chunk方法將會取得一個Eloquent模型的「分塊」,將它們送到給定的閉包(Closure)進行處理。
當你在處理大量的結果時,使用chunk方法可以節省記憶體:
Flight::chunk(200,function($flights){
foreach($flightsas$flight){
//
}
});
傳遞到方法的第一個參數是表示你希望每次「分塊」要接收的資料數量。
閉包則作為第二個參數傳遞,它將會在每次從資料取出分塊時被呼叫。
資料庫查詢會將執行接收到的每個記錄塊傳入閉包中。
使用指標
cursor方法可以讓你使用指標來搜索資料庫記錄,並只會執行一次查詢。
在處理大量資料時,使用cursor方法可以大幅減少記憶體的使用量:
foreach(Flight::where('foo','bar')->cursor()as$flight){
//
}
取得單一模型或Aggregate
當然,除了取得給定資料表的所有記錄,你還可以使用find或first來取得單一記錄。
這些方法會回傳單一模型實例,而非回傳模型的集合:
//透過主鍵取得模型...
$flight=App\Flight::find(1);
//取得符合查詢條件的第一個模型...
$flight=App\Flight::where('active',1)->first();
你也可以使用主鍵陣列作為參數來呼叫find方法,這會回傳符合記錄的集合:
$flights=App\Flight::find([1,2,3]);
NotFound拋出例外
有時你可能希望因為找不到模型而拋出例外。
這在路由或控制器中特別有用。
findOrFail和firstOrFail方法會取得查詢的第一個結果。
然而,如果未能找到結果,Illuminate\Database\Eloquent\ModelNotFoundException會拋出例外:
$model=App\Flight::findOrFail(1);
$model=App\Flight::where('legs','>',100)->firstOrFail();
如果沒有捕獲例外,則會自動發送404HTTP會應給使用者。
在使用這些方法時,不必仔細撰寫檢查要回傳的404回應:
Route::get('/api/flights/{id}',function($id){
returnApp\Flight::findOrFail($id);
});
取得Aggregate
你也可以使用查詢建構器提供的count、sum、max和其他aggregate方法。
這些方法會回傳確切的純量值,而不是完整的模型實例:
$count=App\Flight::where('active',1)->count();
$max=App\Flight::where('active',1)->max('price');
插入與更新模型
插入
要在資料庫建立一筆新紀錄,只要建立一個新模型實例,並在模型上設定屬性,接著呼叫save方法:
*
*@paramRequest$request
*@returnResponse
*/
publicfunctionstore(Request$request)
{
//驗證請求...
$flight=newFlight;
$flight->name=$request->name;
$flight->save();
}
}
在這個範例中,我們把進來的HTTP請求的name參數簡單地指定給App\Flight模型實例的name屬性。
當我們呼叫save方法,就會新增一筆記錄到資料庫中。
當save方法被呼叫時,created_at以及updated_at時間戳記將會自動被設定,所以不需要手動去設定它們。
更新
save方法也可以用於更新資料庫中已經存在的模型。
要更新模型,你必須先取回模型,設定任何你希望更新的屬性,接著呼叫save方法。
同樣的,updated_at時間戳記將會自動被更新,所以不需要手動設定它的值:
$flight=App\Flight::find(1);
$flight->name='NewFlightName';
$flight->save();
批量更新
也可以針對符合給定查詢的任意數量模型執行更新。
在這個範例中,所有active並且destination為SanDiego的航班,將會被標記為延遲:
App\Flight::where('active',1)
->where('destination','SanDiego')
->update(['delayed'=>1]);
update方法預期收到一個欄位與值成對的陣列,來代表應該被更新的欄位。
{note}透過Eloquent來批量更新時,更新後的模型將不觸發saved和updated模型事件。
這是因為發出批量更新時,實際上並不會取得模型。
批量賦值
你也可以在使用create方法來儲存一個新的模型。
被插入的模型實例會從該方法回傳給你。
然而,在這之前,你將需要在你的模型上指定一個fillable或是guarded屬性,所有的Eloquent模型預設會針對批量賦值作保護。
當使用者透過請求傳入一個非預期的HTTP參數時,該參數會非預期竄改資料庫中的欄位,發生批量賦值的漏洞。
例如,一個惡意的使用者透過HTTP請求發送一個is_admin參數,接著被傳送到你模型的create方法,讓使用者可以把自己提升為一位管理員。
所以,你應該開始定義想要被批量賦值的模型屬性。
你可以在模型上使用$fillable屬性來達到這點。
例如讓我們實際去做Flight模型的name屬性的批量賦值:
*
*@vararray
*/
protected$fillable=['name'];
}
一旦我們已經設定屬性為可以被批量賦值的,我們可以使用create方法來新增一筆新記錄到資料庫。
create方法回傳已經被儲存的模型實例:
$flight=App\Flight::create(['name'=>'Flight10']);
如果你已經有一個模型實例,你可以使用fill方法來填充屬性陣列:
$flight->fill(['name'=>'Flight22']);
屬性的白名單
$fillable作為一個可以被批量賦值的屬性的「白名單」,然而你也可以選擇使用$guarded。
$guarded屬性應該包含一個屬性的陣列,是你不想要被批量賦值的。
所有不在陣列裡面的其他屬性將會是可以被批量賦值的。
所以,$guarded的功能像是一個「黑名單」。
當然,你應該使用$fillable或$guarded-而不是兩者。
在下列範例中,除了price的所有屬性都可被批量賦值:
*
*@vararray
*/
protected$guarded=['price'];
}
如果你想要所有屬性都能被批量賦值,你可以定義$guarded屬性為空陣列:
/**
*不可被批量賦值的屬性。
*
*@vararray
*/
protected$guarded=[];
其他建立方法
firstOrCreate和firstOrNew
你還可以使用其他兩種方法透過批量賦值屬性來建立模型:firstOrCreate和firstOrNew。
firstOrCreate方法會嘗試使用給定的欄位與值來找資料庫記錄。
如果在資料庫中無法找到該模型,會從屬性的第一個參數以及那些可選的參數第二個參數中插入一筆記錄。
firstOrNew方法類似firstOrCreate,會嘗試在資料庫查詢符合給定屬性的記錄。
然而,如果沒找到模型,將會回傳一個新模型。
該注意firstOrNew回傳的模型還未存到資料庫,你還需要手動呼叫save來儲存它:
//依名稱取得航班,或因為不存在而建立它...
$flight=App\Flight::firstOrCreate(['name'=>'Flight10']);
//依名稱取得航班,或建立該名稱與延遲的屬性...
$flight=App\Flight::firstOrCreate(
['name'=>'Flight10'],['delayed'=>1]
);
//依名稱取得航班,或實例...
$flight=App\Flight::firstOrNew(['name'=>'Flight10']);
//依名稱取得航班,或實例的名稱和延遲的屬性...
$flight=App\Flight::firstOrNew(
['name'=>'Flight10'],['delayed'=>1]
);
updateOrCreate
你還可能遇到想要去更新已存在的模型或者如果模型不存在去新增的情況。
Laravel提供updateOrCreate方法,只需要一步即可做到。
updateOrCreate類似firstOrCreate方法儲存模型,所以這裡不需要呼叫save():
//如果是從奧克蘭到聖地牙哥的航班,將價格訂為99美金。
//如果沒有符合的模型,就建立一個。
$flight=App\Flight::updateOrCreate(
['departure'=>'Oakland','destination'=>'SanDiego'],
['price'=>99]
);
刪除模型
在模型實例上呼叫delete方法來刪除一個模型:
$flight=App\Flight::find(1);
$flight->delete();
透過主鍵刪除存在的模型
在上述範例中,我們會在呼叫delete方法前,從資料庫取得模型。
然而,如果你知道該模型的主鍵,你可以沒有取得模型就刪除它。
呼叫destroy方法來達成:
App\Flight::destroy(1);
App\Flight::destroy([1,2,3]);
App\Flight::destroy(1,2,3);
透過查詢來刪除模型
當然,你也可以在一組模型上執行刪除的語法。
在這個範例中,我們會刪除所有被標記無效的航班。
批量刪除類似批量更新,不會觸發刪除模型的任何模型事件:
$deletedRows=App\Flight::where('active',0)->delete();
{note}透過Eloquent執行批量刪除語法,deleting和deleted模型不會觸發刪除模型的事件。
這是因為在執行刪除語法時,並未實際取得模型。
軟刪除
除了從資料庫確實的移除記錄,Eloquent也能「軟刪除」模型。
當模型被軟刪除時,它們不會真的從你的資料庫中移除,deleted_at屬性是在模型上設定並寫入到資料庫。
如果模型有不能是null的deleted_at值,該模型將會被軟刪除。
要為模型啟用軟刪除,請在模型上使用Illuminate\Database\Eloquent\SoftDeletestrait並新增deleted_at欄位到你的$dates屬性:
*
*@vararray
*/
protected$dates=['deleted_at'];
}
當然,你應該新增deleted_at欄位到你的資料表。
Laravelschema建構器具有一個輔助函式來建立這個欄位:
Schema::table('flights',function($table){
$table->softDeletes();
});
現在,當你在模型上呼叫delete方法時,deleted_at欄位會設定當前日期與時間。
還有,查詢有使用軟刪除的模型時,被軟刪除的模型會自動從所有查詢結果中排除。
如果要確定給定的模型實例是否被軟刪除,請使用trashed方法:
if($flight->trashed()){
//
}
查詢被軟刪除的模型
包含被軟刪除的模型
如上所述,被軟刪除的模型會自動從查詢結果中排除。
然而,你可以在查詢上使用withTrashed方法,強制被軟刪除的模型出現在結果中:
$flights=App\Flight::withTrashed()
->where('account_id',1)
->get();
withTrashed方法也可以被用在Eloquent的關聯查詢上:
$flight->history()->withTrashed()->get();
只取得被軟刪除的模型
onlyTrashed方法會只有取得被軟刪除的模型:
$flights=App\Flight::onlyTrashed()
->where('airline_id',1)
->get();
恢復被軟刪除的模型
有時你可能希望「取消刪除」被軟刪除的模型。
要把被軟刪除的資料恢復到一般狀態,請在模型實例上使用restore方法:
$flight->restore();
你也可以在查詢中使用restore方法來快速恢復多筆資料。
就像其他「批量」操作,這不會在恢復資料的時候觸發任何模型實例:
App\Flight::withTrashed()
->where('airline_id',1)
->restore();
像是withTrashed方法,restore方法也可被用在Eloquent關聯上:
$flight->history()->restore();
永久刪除模型
有時你可能需要真的從資料庫中刪除一筆資料。
使用forceDelete方法可以從資料庫中永久移除一筆被軟刪除的資料:
//強制刪除單一個模型實例...
$flight->forceDelete();
//強制刪除所有關聯模型...
$flight->history()->forceDelete();
查詢Scope
全域的Scope
全域的Scope可以讓你為給定模型新增條件到所有查詢。
Laravel自己的軟刪除功能利用全域的Scope能從資料庫中只取出「未刪除」模型。
撰寫自己全域的Scope能提供更方便且簡單的方式來確保給定模型的每個查詢都能受到一定的限制。
撰寫全域的Scope
撰寫全域的scope是相當簡單的事情。
定義實作Illuminate\Database\Eloquent\Scope介面的類別。
這個介面會要求你實作apply方法,apply方法可以根據需要來新增where來查詢:
*
*@param\Illuminate\Database\Eloquent\Builder$builder
*@param\Illuminate\Database\Eloquent\Model$model
*@returnvoid
*/
publicfunctionapply(Builder$builder,Model$model)
{
$builder->where('age','>',200);
}
}
{tip}如果你的全域的Scope正在新增欄位到查詢的select子句,你應該使用addSelect方法,而非select。
這可避免不小心換掉現有的查詢select子句。
應用在全域的Scope
要指派全域的Scope到模型,你應該覆寫給定模型的boot方法並使用addGlobalScope方法:
*
*@returnvoid
*/
protectedstaticfunctionboot()
{
parent::boot();
static::addGlobalScope(newAgeScope);
}
}
新增了Scope之後,User::all()查詢會產生以下的SQL:
select*from`users`where`age`>200
匿名全域的Scope
Eloquent也可以讓你使用閉包來定義全域的,這對於簡單的作用域內相當的有用,但是不保證在獨立的類別內:
*
*@returnvoid
*/
protectedstaticfunctionboot()
{
parent::boot();
static::addGlobalScope('age',function(Builder$builder){
$builder->where('age','>',200);
});
}
}
移除全域的Scope
如果你想要為給定的查詢移除全域的Scope,你可以使用withoutGlobalScope方法。
該方法接受全域的Scope的類別名稱作為唯一參數:
User::withoutGlobalScope(AgeScope::class)->get();
如果你想要移除幾個或甚至所有的全域的Scope,你可以使用withoutGlobalScopes方法:
//移除所有的全域的Scope...
User::withoutGlobalScopes()->get();
//移除某些全域的Scope...
User::withoutGlobalScopes([
FirstScope::class,SecondScope::class
])->get();
局部的Scope
局部的Scope讓你定義限制的共用集合,它可以輕鬆地在你的應用程式重複使用。
例如,你可能需要頻繁地取得所有被認為是「受歡迎的」使用者。
要定義的Scope,必須簡單地在Eloquent模型方法前面加上前綴scope
Scope總是會回傳一個查詢建構器的實例:
*
*@param\Illuminate\Database\Eloquent\Builder$query
*@return\Illuminate\Database\Eloquent\Builder
*/
publicfunctionscopePopular($query)
{
return$query->where('votes','>',100);
}
/**
*只查詢活躍使用者的Scope。
*
*@param\Illuminate\Database\Eloquent\Builder$query
*@return\Illuminate\Database\Eloquent\Builder
*/
publicfunctionscopeActive($query)
{
return$query->where('active',1);
}
}
利用局部的Scope
Scope一旦被定義,你可以在查詢模型時呼叫scope的方法。
然而,你並不需要在呼叫該方法時引入scope前綴。
你甚至能鏈結呼叫各種Scope,例如:
$users=App\User::popular()->active()->orderBy('created_at')->get();
動態Scope
有時你可能希望去定義一個接受參數的Scope。
開始之前,只要新增額外參數到你的Scope。
Scope參數應該在$query參數之後被定義:
*
*@param\Illuminate\Database\Eloquent\Builder$query
*@parammixed$type
*@return\Illuminate\Database\Eloquent\Builder
*/
publicfunctionscopeOfType($query,$type)
{
return$query->where('type',$type);
}
}
現在,你可以在呼叫scope的時候傳入該參數:
$users=App\User::ofType('admin')->get();
事件
Eloquent模型可以讓你觸發下列模型的生命週期幾個時間點的事件:retrieved、creating、created、updating、updated、saving、saved、deleting、deleted、restoring、restored。
事件可以讓你每次在資料庫中儲存或更新指定的模型類別時,能夠輕易的執行程式碼。
從資料庫中取得已存在的資料時,會觸發retrieved事件。
當新的資料被第一次儲存時,會觸發creating和created事件。
如果資料已經存在於資料庫,並呼叫save,就會觸發updating和updated事件。
然而,這兩種案例都會觸發saving和saved事件。
開始之前,在你的Eloquent模型上定義$dispatchesEvents屬性,它會映射Eloquent模型的生命週期到你的事件類別:
*
*@vararray
*/
protected$dispatchesEvents=[
'saved'=>UserSaved::class,
'deleted'=>UserDeleted::class,
];
}
Observer
如果你在給定模型上監聽多個事件,你可以使用Observer來組織你的所有監聽器到單一個類別。
Observer類別有個方法名稱,會反射你想監聽的Eloquent事件。
這些方法中的每一個都會接收模型作為它們的參數。
Laravel預設並沒有Observer的目錄,不過你可以建立任何你想要的目錄來放置Observer類別:
延伸文章資訊
- 1Eloquent ORM 快速指南
Eloquent ORM 快速指南![](https://i.imgur.com/XdzoHHz.png) ## DB Facade vs Eloquent 當要進行複雜的原生SQL語.
- 2eloquent中文(繁體)翻譯:劍橋詞典
She made an eloquent appeal for action. 她發表了富有說服力的演講,呼籲採取行動。 The pictures were an eloquent remind...
- 3Eloquent ORM - Laravel - 為網頁藝術家創造的PHP 框架
所有的Eloquent 模型都繼承 Illuminate\Database\Eloquent\Model 。 定義一個Eloquent 模型. class User extends Model ...
- 4eloquent - Yahoo奇摩字典搜尋結果
雄辯的,有說服力的;富於表現的. Dr.eye 譯典通 · eloquent · 查看更多. IPA[ˈeləkwənt]. 美式. 英式. adj. 雄辯的;傳神達意的. 牛津中文字典. el...
- 5Eloquent ORM | Laravel 4 入門 - Tony
Eloquent ORM. Object Relational Mapping (ORM) 是一種物件和關聯對映的技術。物件指的是物件導向程式語言中的物件,關聯則是關聯式資料庫,ORM 是一個中...