快速入門
快速入門
Eloquent:入門
簡(jiǎn)介
Laravel 的 Eloquent ORM 提供了一個(gè)漂亮、簡(jiǎn)潔的 ActiveRecord 實(shí)現(xiàn)來和數(shù)據(jù)庫(kù)交互。每個(gè)數(shù)據(jù)庫(kù)表都有一個(gè)對(duì)應(yīng)的「模型」用來與該表交互。你可以通過模型查詢數(shù)據(jù)表中的數(shù)據(jù),以及在數(shù)據(jù)表中插入新記錄。
在開始之前,請(qǐng)確保在 config/database.php
中配置數(shù)據(jù)庫(kù)連接。更多關(guān)于數(shù)據(jù)庫(kù)配置的信息,請(qǐng)查看 文檔。
模型定義
首先,創(chuàng)建一個(gè) Eloquent 模型。 模型通常在 app
目錄中,但你可以根據(jù) composer.json
文件將他們放置在可以被自動(dòng)加載的任意位置。所有的 Eloquent 模型都繼承至 Illuminate\Database\Eloquent\Model
類。
創(chuàng)建模型最簡(jiǎn)單的方法就是使用 make:model
Artisan 命令:
php artisan make:model Flight
如果要在生成模型的時(shí)候生成 數(shù)據(jù)庫(kù)遷移 ,可以使用 --migration
或 -m
選項(xiàng):
php artisan make:model Flight --migration php artisan make:model Flight -m
Eloquent 模型約定
現(xiàn)在,我們來看一個(gè) Flight
模型的示例,我們將用它從 flights
數(shù)據(jù)庫(kù)表中檢索和存儲(chǔ)數(shù)據(jù)信息:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ // }
數(shù)據(jù)表名稱
請(qǐng)注意,我們并沒有告訴 Eloquent 我們的 Flight
模型使用哪個(gè)數(shù)據(jù)表。 除非明確地指定了其它名稱,否則將使用類的復(fù)數(shù)形式「蛇形命名」來作為表名。因此,在這種情況下,Eloquent 將假設(shè) Flight
模型存儲(chǔ)的是 flights
數(shù)據(jù)表中的數(shù)據(jù)。你可以通過在模型上定義 table
屬性來指定自定義數(shù)據(jù)表:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 與模型關(guān)聯(lián)的表名 * * @var string */ protected $table = 'my_flights'; }
主鍵
Eloquent 也會(huì)假設(shè)每個(gè)數(shù)據(jù)表都有一個(gè)名為 id
的主鍵列。你可以定義一個(gè)受保護(hù)的 $primaryKey
屬性來重寫約定。
此外,Eloquent 假設(shè)主鍵是一個(gè)自增的整數(shù)值,這意味著默認(rèn)情況下主鍵會(huì)自動(dòng)轉(zhuǎn)換為 int
類型。如果您希望使用非遞增或非數(shù)字的主鍵則需要設(shè)置公共的 $incrementing
屬性設(shè)置為 false
。如果你的主鍵不是一個(gè)整數(shù),你需要將模型上受保護(hù)的 $keyType
屬性設(shè)置為 string
。
時(shí)間戳
默認(rèn)情況下,Eloquent 預(yù)期你的數(shù)據(jù)表中存在 created_at
和 updated_at
。如果你不想讓 Eloquent 自動(dòng)管理這兩個(gè)列, 請(qǐng)將模型中的 $timestamps
屬性設(shè)置為 false
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 指示模型是否自動(dòng)維護(hù)時(shí)間戳 * * @var bool */ public $timestamps = false; }
如果需要自定義時(shí)間戳的格式,在你的模型中設(shè)置 $dateFormat
屬性。這個(gè)屬性決定日期屬性在數(shù)據(jù)庫(kù)的存儲(chǔ)方式,以及模型序列化為數(shù)組或者 JSON 的格式:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 模型日期列的存儲(chǔ)格式。 * * @var string */ protected $dateFormat = 'U'; }
如果你需要自定義存儲(chǔ)時(shí)間戳的字段名,可以在模型中設(shè)置 CREATED_AT
和 UPDATED_AT
常量的值來實(shí)現(xiàn):
<?php class Flight extends Model{ const CREATED_AT = 'creation_date'; const UPDATED_AT = 'last_update'; }
數(shù)據(jù)庫(kù)連接
默認(rèn)情況下,Eloquent 模型將使用你的應(yīng)用程序配置的默認(rèn)數(shù)據(jù)庫(kù)連接。如果你想為模型指定一個(gè)不同的連接,設(shè)置 $connection
屬性:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 模型的連接名稱 * * @var string */ protected $connection = 'connection-name'; }
默認(rèn)屬性值
如果要為模型的某些屬性定義默認(rèn)值,可以在模型上定義 $attributes
屬性:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 模型的默認(rèn)屬性值。 * * @var array */ protected $attributes = [ 'delayed' => false, ]; }
模型檢索
創(chuàng)建模型和 它關(guān)聯(lián)的數(shù)據(jù)庫(kù)表后,你就可以從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù)了。將每個(gè) Eloquent 模型想象成一個(gè)強(qiáng)大的查詢構(gòu)造器 query builder ,你可以用它更快速的查詢與其相關(guān)聯(lián)的數(shù)據(jù)表。例如:
<?php $flights = App\Flight::all(); foreach ($flights as $flight) { echo $flight->name; }
附加約束
Eloquent 的 all
方法會(huì)返回模型中所有的結(jié)果。由于每個(gè) Eloquent 模型都充當(dāng)一個(gè)查詢構(gòu)造器,所以你也可以添加查詢條件,然后使用 get
方法獲取查詢結(jié)果:
$flights = App\Flight::where('active', 1) ->orderBy('name', 'desc') ->take(10) ->get();
{tip} 因?yàn)?Eloquent 模型也是查詢構(gòu)造器,所以你也應(yīng)當(dāng)閱讀 查詢構(gòu)造器可用的所有方法。你可以在 Eloquent 查詢中使用這些方法。
重新加載模型
你可以使用 fresh
和 refresh
方法重新加載模型。 fresh
方法會(huì)重新從數(shù)據(jù)庫(kù)中檢索模型。現(xiàn)有的模型實(shí)例不受影響:
$flight = App\Flight::where('number', 'FR 900')->first(); $freshFlight = $flight->fresh();
refresh
方法使用數(shù)據(jù)庫(kù)中的新數(shù)據(jù)重新賦值現(xiàn)有模型。此外,已經(jīng)加載的關(guān)系會(huì)被重新加載:
$flight = App\Flight::where('number', 'FR 900')->first(); $flight->number = 'FR 456';$flight->refresh(); $flight->number; // "FR 900"
集合
對(duì)于 Eloquent 中的 all
和 get
方法可以查詢多個(gè)結(jié)果,返回一個(gè) Illuminate\Database\Eloquent\Collection
實(shí)例。 Collection
類提供了 很多輔助函數(shù) 來處理 Eloquent 結(jié)果:
$flights = $flights->reject(function ($flight) { return $flight->cancelled; });
你可以像數(shù)組一樣遍歷集合:
foreach ($flights as $flight) { echo $flight->name; }
分塊結(jié)果
如果你需要處理數(shù)以千計(jì)的 Eloquent 結(jié)果,使用 chunk
命令。 chunk
方法會(huì)檢索 Eloquent 模型中的『分塊』將他們提供給指定的 Closure
處理。在處理大型結(jié)果集時(shí),使用 chunk
方法可以節(jié)省內(nèi)存:
Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { // } });
傳遞到方法的第一個(gè)參數(shù)是希望每個(gè)『分塊』接收的數(shù)據(jù)量。閉包作為第二個(gè)參數(shù)傳遞,它在每次從數(shù)據(jù)庫(kù)中檢索分塊的時(shí)候調(diào)用。它將執(zhí)行數(shù)據(jù)庫(kù)查詢把檢索分塊的結(jié)果傳遞給閉包方法。
使用游標(biāo)
cursor
方法允許你使用游標(biāo)遍歷數(shù)據(jù)庫(kù),它只執(zhí)行一次查詢。處理大量的數(shù)據(jù)時(shí), cursor
方法可以大大減少內(nèi)存的使用量:
foreach (Flight::where('foo', 'bar')->cursor() as $flight) { // }
檢索單個(gè)模型 / 集合
除了從指定的數(shù)據(jù)表檢索所有記錄外,你可以使用 find
或 first
方法來檢索單條記錄。這些方法返回單個(gè)模型實(shí)例,而不是返回模型集合:
// 通過主鍵檢索一個(gè)模型... $flight = App\Flight::find(1); // 檢索符合查詢限制的第一個(gè)模型... $flight = App\Flight::where('active', 1)->first();
你也可以使用主鍵數(shù)組作為參數(shù)調(diào)用 find
方法,它將返回匹配記錄的集合:
$flights = App\Flight::find([1, 2, 3]);
『未找到』異常
有時(shí)你希望在未找到模型時(shí)拋出異常。這在控制器和路由中非常有用。 findOrFail
和 firstOrFail
方法會(huì)檢索查詢的第一個(gè)結(jié)果,如果未找到,將拋出 Illuminate\Database\Eloquent\ModelNotFoundException
異常:
$model = App\Flight::findOrFail(1); $model = App\Flight::where('legs', '>', 100)->firstOrFail();
如果沒有捕獲異常,則會(huì)自動(dòng)返回 404
響應(yīng)給用戶。也就是說,使用這些方法時(shí),沒有必要再寫個(gè)檢查來返回 404
響應(yīng)::
Route::get('/api/flights/{id}', function ($id) { return App\Flight::findOrFail($id); });
檢索集合
你還可以使用 查詢構(gòu)造器 提供的 count
, sum
, max
, 和其他的聚合函數(shù)。這些方法只會(huì)返回適當(dāng)?shù)臉?biāo)量值而不是一個(gè)模型實(shí)例:
$count = App\Flight::where('active', 1)->count(); $max = App\Flight::where('active', 1)->max('price');
插入 & 更新模型
插入
要往數(shù)據(jù)庫(kù)新增一條記錄,先創(chuàng)建新模型實(shí)例,給實(shí)例設(shè)置屬性,然后調(diào)用 save
方法:
<?php namespace App\Http\Controllers; use App\Flight;use Illuminate\Http\Request; use App\Http\Controllers\Controller; class FlightController extends Controller{ /** * 創(chuàng)建一個(gè)新的航班實(shí)例 * * @param Request $request * @return Response */ public function store(Request $request) { // 校驗(yàn)請(qǐng)求... $flight = new Flight; $flight->name = $request->name; $flight->save(); } }
在這個(gè)示例中,我們將 HTTP 請(qǐng)求參數(shù) name
賦值給了 App\Flight
模型實(shí)例的 name
屬性。當(dāng)調(diào)用 save
方法時(shí),將會(huì)插入一條新記錄。 created_at
和 updated_at
時(shí)間戳將會(huì)自動(dòng)設(shè)置,不需要手動(dòng)賦值。
更新
save
方法也可以用來更新數(shù)據(jù)庫(kù)已經(jīng)存在的模型。更新模型,你需要先檢索出來,設(shè)置要更新的屬性,然后調(diào)用 save
方法。同樣, updated_at
時(shí)間戳?xí)詣?dòng)更新,所以也不需要手動(dòng)賦值:
$flight = App\Flight::find(1); $flight->name = 'New Flight Name';$flight->save();
批量更新
也可以更新匹配查詢條件的多個(gè)模型。在這個(gè)示例中,所有的 active
和 destination
為 San Diego
的航班會(huì)標(biāo)記為延誤:
App\Flight::where('active', 1) ->where('destination', 'San Diego') ->update(['delayed' => 1]);
update
方法接受一個(gè)鍵為字段名稱數(shù)據(jù)為值的數(shù)組。
{note} 通過 Eloquent 批量更新時(shí), 更新的模型不會(huì)觸發(fā)
saved
和updated
事件。因?yàn)樵谂扛聲r(shí),從不會(huì)去檢索模型。
批量賦值
你也可以使用 create
方法來保存新模型,此方法會(huì)返回模型實(shí)例。不過,在使用之前,你需要在模型上指定 fillable
或 guarded
屬性,因?yàn)樗械?Eloquent 模型都默認(rèn)不可進(jìn)行批量賦值。
當(dāng)用戶通過 HTTP 請(qǐng)求傳入一個(gè)意外的參數(shù),并且該參數(shù)更改了數(shù)據(jù)庫(kù)中你不需要更改的字段時(shí)。比如:惡意用戶可能會(huì)通過 HTTP 請(qǐng)求傳入 is_admin
參數(shù),然后將其傳給 create
方法,此操作能讓用戶將自己升級(jí)成管理員。
所以,在開始之前,你應(yīng)該定義好模型上的哪些屬性是可以被批量賦值的。你可以通過模型上的 $fillable
屬性來實(shí)現(xiàn)。 例如:讓 Flight
模型的 name
屬性可以被批量賦值:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 可以被批量賦值的屬性。 * * @var array */ protected $fillable = ['name']; }
一旦我們?cè)O(shè)置好了可以批量賦值的屬性,就可以通過 create
方法插入新數(shù)據(jù)到數(shù)據(jù)庫(kù)中了。 create
方法將返回保存的模型實(shí)例:
$flight = App\Flight::create(['name' => 'Flight 10']);
如果你已經(jīng)有一個(gè)模型實(shí)例,你可以傳遞一個(gè)數(shù)組給 fill
方法來賦值:
$flight->fill(['name' => 'Flight 22']);
保護(hù)屬性
$fillable
可以看作批量賦值的「白名單」, 你也可以使用 $guarded
屬性來實(shí)現(xiàn)。 $guarded
屬性包含的是不允許批量賦值的數(shù)組。也就是說, $guarded
從功能上將更像是一個(gè)「黑名單」。注意:你只能使用 $fillable
或 $guarded
二者中的一個(gè),不可同時(shí)使用。下面這個(gè)例子中,除了 price
屬性,其他的屬性都可以批量賦值:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model{ /** * 不可批量賦值的屬性。 * * @var array */ protected $guarded = ['price']; }
如果你想讓所有屬性都可以批量賦值, 你可以將 $guarded
定義成一個(gè)空數(shù)組:
/** * 不可以批量賦值的屬性。 * * @var array */ protected $guarded = [];
其他創(chuàng)建方法
firstOrCreate
/ firstOrNew
這里有兩個(gè)你可能用來批量賦值的方法: firstOrCreate
和 firstOrNew
。firstOrCreate
方法會(huì)通過給定的 列 / 值 來匹配數(shù)據(jù)庫(kù)中的數(shù)據(jù)。如果在數(shù)據(jù)庫(kù)中找不到對(duì)應(yīng)的模型, 則會(huì)從第一個(gè)參數(shù)的屬性乃至第二個(gè)參數(shù)的屬性中創(chuàng)建一條記錄插入到數(shù)據(jù)庫(kù)。
firstOrNew
方法像 firstOrCreate
方法一樣嘗試通過給定的屬性查找數(shù)據(jù)庫(kù)中的記錄。不同的是,如果 firstOrNew
方法找不到對(duì)應(yīng)的模型,會(huì)返回一個(gè)新的模型實(shí)例。注意 firstOrNew
返回的模型實(shí)例尚未保存到數(shù)據(jù)庫(kù)中,你需要手動(dòng)調(diào)用 save
方法來保存:
// 通過 name 來查找航班,不存在則創(chuàng)建... $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']); // 通過 name 查找航班,不存在則使用 name 和 delayed 屬性創(chuàng)建... $flight = App\Flight::firstOrCreate(['name' => 'Flight 10'], ['delayed' => 1]); // 通過 name 查找航班,不存在則創(chuàng)建一個(gè)實(shí)例... $flight = App\Flight::firstOrNew(['name' => 'Flight 10']); // 通過 name 查找航班,不存在則使用 name 和 delayed 屬性創(chuàng)建一個(gè)實(shí)例... $flight = App\Flight::firstOrNew(['name' => 'Flight 10'], ['delayed' => 1]);
updateOrCreate
你還可能遇到希望更新現(xiàn)有模型或在不存在的情況下則創(chuàng)建新的模型的情景。 Laravel 提供了 updateOrCreate
方法僅一個(gè)步驟就可以實(shí)現(xiàn)。跟 firstOrCreate
方法一樣,updateOrCreate
匹配到對(duì)應(yīng)模型,所以不需要調(diào)用 save()
方法:
// 如果有從奧克蘭到圣地亞哥的航班,則價(jià)格定為99美元。 // 如果沒匹配到存在的模型,則創(chuàng)建一個(gè)。 $flight = App\Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99] );
刪除模型
可以在模型實(shí)例上調(diào)用 delete
方法來刪除實(shí)例:
$flight = App\Flight::find(1); $flight->delete();
通過主鍵刪除模型
在上面的例子中,在調(diào)用 delete
之前需要先去數(shù)據(jù)庫(kù)中查找對(duì)應(yīng)的模型。事實(shí)上,如果你知道了模型的主鍵,你可以直接使用 destroy
方法來刪除模型,而不用先去數(shù)據(jù)庫(kù)中查找。 destroy
方法除了接受單個(gè)主鍵作為參數(shù)之外,還接受多個(gè)主鍵,或者使用數(shù)組,集合來保存多個(gè)主鍵:
App\Flight::destroy(1); App\Flight::destroy(1, 2, 3); App\Flight::destroy([1, 2, 3]); App\Flight::destroy(collect([1, 2, 3]));
通過查詢刪除模型
你也可以在模型上運(yùn)行刪除語(yǔ)句。在這個(gè)例子中,我們將刪除所有標(biāo)記為非活躍的航班。與批量更新一樣,批量刪除不會(huì)為刪除的模型啟動(dòng)任何模型事件:
$deletedRows = App\Flight::where('active', 0)->delete();
{note} 通過 Eloquent 執(zhí)行批量刪除語(yǔ)句時(shí),不會(huì)觸發(fā)
deleting
和deleted
模型事件。因此,在執(zhí)行刪除語(yǔ)句時(shí),從不檢索模型示例。
軟刪除
除了真實(shí)刪除數(shù)據(jù)庫(kù)記錄, Eloquent 也可以「軟刪除」模型。軟刪除的模型并不是真的從數(shù)據(jù)庫(kù)中刪除了。事實(shí)上,是在模型上設(shè)置了 deleted_at
屬性并將其值寫入數(shù)據(jù)庫(kù)。如果 deleted_at
值非空,代表這個(gè)模型已被軟刪除。如果要開啟模型軟刪除功能,你需要在模型上使用 Illuminate\Database\Eloquent\SoftDeletes
trait:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Flight extends Model{ use SoftDeletes; }
{tip}
SoftDeletes
trait 會(huì)自動(dòng)將deleted_at
屬性轉(zhuǎn)換成DateTime
/Carbon
實(shí)例
當(dāng)然,你需要把 deleted_at
字段添加到數(shù)據(jù)表中。 Laravel 的 數(shù)據(jù)庫(kù)遷移 有創(chuàng)建這個(gè)字段的方法:
Schema::table('flights', function (Blueprint $table) { $table->softDeletes(); });
那現(xiàn)在,當(dāng)你在模型實(shí)例上使用 delete
方法, 當(dāng)前日期時(shí)間會(huì)寫入 deleted_at
字段。同時(shí),查詢出來的結(jié)果也會(huì)自動(dòng)排除已被軟刪除的記錄。
你可以使用 trashed
方法來驗(yàn)證當(dāng)前模型是否軟刪除:
if ($flight->trashed()) { // }
查詢軟刪除模型
包括已軟刪除的模型
前面提到,查詢結(jié)果會(huì)自動(dòng)剔除已被軟刪除的結(jié)果。當(dāng)然,你可以使用 withTrashed
方法來獲取包括軟刪除模型在內(nèi)的模型:
$flights = App\Flight::withTrashed() ->where('account_id', 1) ->get();
withTrashed
方法也可以用在 關(guān)聯(lián) 查詢:
$flight->history()->withTrashed()->get();
檢索軟刪除模型
onlyTrashed
方法 只 獲取已軟刪除的模型:
$flights = App\Flight::onlyTrashed() ->where('airline_id', 1) ->get();
恢復(fù)軟刪除模型
有時(shí)會(huì)對(duì)軟刪除模型進(jìn)行 「撤銷」,在已軟刪除的數(shù)據(jù)上使用 restore
方法即可恢復(fù)到有效狀態(tài):
$flight->restore();
你也可以在查詢中使用 restore
方法,從而快速恢復(fù)多個(gè)模型。和其他批量」操作一樣,這個(gè)操作不會(huì)觸發(fā)模型的任何事件:
App\Flight::withTrashed() ->where('airline_id', 1) ->restore();
類似 withTrashed
方法, restore
方法也用在 關(guān)聯(lián)上:
$flight->history()->restore();
永久刪除
要真實(shí)刪除數(shù)據(jù)時(shí),使用 forceDelete
方法即可:
// 單個(gè)模型實(shí)例的永久刪除... $flight->forceDelete(); // 關(guān)聯(lián)模型的永久刪除... $flight->history()->forceDelete();
查詢作用域
全局作用域
全局作用域可以給模型的查詢都添加上約束。Laravel 的 軟刪除 功能就是利用此特性從數(shù)據(jù)庫(kù)中獲取 「未刪除」的模型。 你可以編寫你自己的全局作用域,很簡(jiǎn)單、方便的為每個(gè)模型查詢都加上約束條件:
編寫全局作用域
編寫全局作用域很簡(jiǎn)單。定義一個(gè)實(shí)現(xiàn) Illuminate\Database\Eloquent\Scope
接口的類,并實(shí)現(xiàn) apply
這個(gè)方法。 根據(jù)你的需求,在 apply
方法中加入查詢的 where
條件:
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class AgeScope implements Scope{ /** * 把約束加到 Eloquent 查詢構(gòu)造中。 * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->where('age', '>', 200); } }
{tip} 如果需要在 select 語(yǔ)句里添加字段,應(yīng)使用
addSelect
方法,而不是select
方法。 這將有效防止無意中替換現(xiàn)有 select 語(yǔ)句的情況。
應(yīng)用全局作用域
要將全局作用域分配給模型,需要重寫模型的 boot
方法并使用 addGlobalScope
方法:
<?php namespace App;use App\Scopes\AgeScope; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 模型的 「啟動(dòng)」 方法. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope(new AgeScope); } }
添加作用域后,對(duì) User::all()
的查詢會(huì)生成以下 SQL 查詢語(yǔ)句:
select * from `users` where `age` > 200
匿名全局作用域
Eloquent 同樣允許使用閉包定義全局作用域,這樣就不需要為一個(gè)簡(jiǎn)單的作用域而編寫一個(gè)單獨(dú)的類:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class User extends Model{ /** *模型的「啟動(dòng)」方法. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope('age', function (Builder $builder) { $builder->where('age', '>', 200); }); } }
取消全局作用域
如果需要對(duì)當(dāng)前查詢?nèi)∠肿饔糜颍枰褂?withoutGlobalScope
方法。 該方法僅接受全局作用域類名作為它唯一的參數(shù):
User::withoutGlobalScope(AgeScope::class)->get();
或者,如果使用閉包定義全局作用域的話:
User::withoutGlobalScope('age')->get();
如果你需要取消部分或者全部的全局作用域的話,需要使用 withoutGlobalScopes
方法:
// 取消所有的全局作用域... User::withoutGlobalScopes()->get(); // 取消部分全局作用域... User::withoutGlobalScopes([ FirstScope::class, SecondScope::class ])->get();
本地作用域
本地作用域允許定義通用的約束集合以便在應(yīng)用程序中重復(fù)使用。例如,你可能經(jīng)常需要獲取所有 「流行」的用戶。 要定義這樣一個(gè)范圍,只需要在對(duì)應(yīng)的 Eloquent 模型方法前添加 scope
前綴:
作用域總是返回一個(gè)查詢構(gòu)造器實(shí)例:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 只查詢受歡迎的用戶的作用域. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopePopular($query) { return $query->where('votes', '>', 100); } /** * 只查詢 active 用戶的作用域. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } }
使用本地作用域
一旦定義了作用域,就可以在查詢?cè)撃P蜁r(shí)調(diào)用作用域方法。不過,在調(diào)用這些方法時(shí)不必包含 scope
前綴。甚至可以鏈?zhǔn)秸{(diào)用多個(gè)作用域,例如:
$users = App\User::popular()->active()->orderBy('created_at')->get();
借助 or
查詢運(yùn)行符整合多個(gè) Eloquent 模型,可能需要使用閉包回調(diào):
$users = App\User::popular()->orWhere(function (Builder $query) { $query->active(); })->get();
因?yàn)檫@樣可能會(huì)有點(diǎn)麻煩,Laravel 提供了「高階的」 orWhere
方法,它允許你在鏈?zhǔn)秸{(diào)用作用域時(shí)不使用閉包:
$users = App\User::popular()->orWhere->active()->get();
動(dòng)態(tài)作用域
有時(shí)可能地希望定義一個(gè)可以接受參數(shù)的作用域。把額外參數(shù)傳遞給作用域就可以達(dá)到此目的。作用域參數(shù)要放在 $query
參數(shù)之后:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model{ /** * 將查詢作用域限制為僅包含給定類型的用戶。 * * @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $type * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOfType($query, $type) { return $query->where('type', $type); } }
這樣就可以在調(diào)用作用域時(shí)傳遞參數(shù)了:
$users = App\User::ofType('admin')->get();
模型比較
有時(shí)可能需要判斷兩個(gè)模型是否「相同」。 is
方法可以用來快速校驗(yàn)兩個(gè)模型是否擁有相同的主鍵、表和數(shù)據(jù)庫(kù)連接:
if ($post->is($anotherPost)) { // }
事件
Eloquent 模型觸發(fā)幾個(gè)事件,允許你掛接到模型生命周期的如下節(jié)點(diǎn): retrieved
、 creating
、 created
、 updating
、 updated
、 saving
、 saved
、 deleting
、 deleted
、 restoring
和 restored
。事件允許你每當(dāng)特定模型保存或更新數(shù)據(jù)庫(kù)時(shí)執(zhí)行代碼。每個(gè)事件通過其構(gòu)造器接受模型實(shí)例。
retrieved
事件在現(xiàn)有模型從數(shù)據(jù)庫(kù)中查找數(shù)據(jù)時(shí)觸發(fā)。當(dāng)新模型每一次保存時(shí), creating
和 created
事件被觸發(fā)。如果數(shù)據(jù)庫(kù)中已經(jīng)存在模型并且調(diào)用了 save
方法, updating
/ updated
事件被觸發(fā)。這些情況下, saving
/ saved
事件也被觸發(fā)。
{note} 通過 Eloquent 進(jìn)行批量更新時(shí),被更新模型的
saved
和updated
事件不會(huì)被觸發(fā)。這是因?yàn)榕扛聲r(shí),并沒有真的獲取模型。
首先,在 Eloquent 模型上定義一個(gè) $dispatchesEvents
屬性,將 Eloquent 模型生命周期的幾個(gè)節(jié)點(diǎn)映射到你自己的 event 類 :
<?php namespace App; use App\Events\UserSaved; use App\Events\UserDeleted; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable{ use Notifiable; /** * 為模型事件。 * * @var array */ protected $dispatchesEvents = [ 'saved' => UserSaved::class, 'deleted' => UserDeleted::class, ]; }
定義并且映射了 Eloquent 事件,就可以使用 event 監(jiān)聽器 listeners 處理這些事件了。
觀察者
定義觀察者
如果在一個(gè)模型上監(jiān)聽了多個(gè)事件,可以使用觀察者來將這些監(jiān)聽器組織到一個(gè)單獨(dú)的類中。觀察者類的方法名映射到你希望監(jiān)聽的 Eloquent 事件。 這些方法都以模型作為其唯一參數(shù)。 make:observer
Artisan 命令可以快速建立新的觀察者類:
php artisan make:observer UserObserver --model=User
此命令將在 App/Observers
文件夾放置新的觀察者類。如果這個(gè)目錄不存在,Artisan 將替你創(chuàng)建。使用如下方式開啟觀察者:
<?php namespace App\Observers; use App\User; class UserObserver{ /** * 處理 User 「新建」事件。 * * @param \App\User $user * @return void */ public function created(User $user) { // } /** * 處理 User 「更新」 事件。 * * @param \App\User $user * @return void */ public function updated(User $user) { // } /** * 處理 User 「刪除」 事件。 * * @param \App\User $user * @return void */ public function deleted(User $user) { // } }
在你希望觀察的模型上使用 observe
方法注冊(cè)觀察者。也可以在服務(wù)提供者的 boot
方法注冊(cè)觀察者。下面是在 AppServiceProvider
中注冊(cè)觀察者的示例:
<?php namespace App\Providers; use App\User;use App\Observers\UserObserver; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider{ /** * 啟動(dòng)應(yīng)用服務(wù)。 * * @return void */ public function boot() { User::observe(UserObserver::class); } /** * 注冊(cè)服務(wù)提供者。 * * @return void */ public function register() { // } }