Laravel5.7 Eloquent ORM快速入門詳解
簡(jiǎn)介
Laravel 內(nèi)置的 Eloquent ORM 提供了一個(gè)美觀、簡(jiǎn)單的與數(shù)據(jù)庫(kù)打交道的 ActiveRecord 實(shí)現(xiàn),每張數(shù)據(jù)表都對(duì)應(yīng)一個(gè)與該表進(jìn)行交互的模型(Model),通過(guò)模型類,你可以對(duì)數(shù)據(jù)表進(jìn)行查詢、插入、更新、刪除等操作。
在開(kāi)始之前,確保在 config/database.php 文件中配置好了數(shù)據(jù)庫(kù)連接。更多關(guān)于數(shù)據(jù)庫(kù)配置的信息,請(qǐng)查看文檔。
定義模型
我們從創(chuàng)建一個(gè) Eloquent 模型開(kāi)始,模型類通常位于 app 目錄下,你也可以將其放在其他可以被 composer.json 文件自動(dòng)加載到的地方。所有 Eloquent 模型都繼承自 Illuminate\Database\Eloquent\Model 類。
創(chuàng)建模型實(shí)例最簡(jiǎn)單的辦法就是使用 Artisan 命令 make:model:
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)在,讓我們來(lái)看一個(gè) Flight 模型的例子,我們將用該類獲取和存取數(shù)據(jù)表 flights 中的信息:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { // }
表名
注意我們并沒(méi)有告訴 Eloquent 我們的 Flight 模型使用哪張表,默認(rèn)規(guī)則是小寫的模型類名復(fù)數(shù)格式作為與其對(duì)應(yīng)的表名(除非在模型類中明確指定了其它名稱)。所以,在本例中,Eloquent 認(rèn)為 Flight 模型存儲(chǔ)記錄在 flights 表中。你也可以在模型中定義 table 屬性來(lái)指定自定義的表名:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * 關(guān)聯(lián)到模型的數(shù)據(jù)表 * * @var string */ protected $table = 'my_flights'; }
主鍵
Eloquent 默認(rèn)每張表的主鍵名為 id,你可以在模型類中定義一個(gè) $primaryKey 屬性來(lái)覆蓋該約定。
此外,Eloquent 默認(rèn)主鍵字段是自增的整型數(shù)據(jù),這意味著主鍵將會(huì)被自動(dòng)轉(zhuǎn)化為 int 類型,如果你想要使用非自增或非數(shù)字類型主鍵,必須在對(duì)應(yīng)模型中設(shè)置 $incrementing 屬性為 false,如果主鍵不是整型,還要設(shè)置 $keyType 屬性值為 string。
時(shí)間戳
默認(rèn)情況下,Eloquent 期望 created_at 和 updated_at 已經(jīng)存在于數(shù)據(jù)表中,如果你不想要這些 Laravel 自動(dòng)管理的數(shù)據(jù)列,在模型類中設(shè)置 $timestamps 屬性為 false:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * 表明模型是否應(yīng)該被打上時(shí)間戳 * * @var bool */ public $timestamps = false; }
如果你需要自定義時(shí)間戳格式,設(shè)置模型中的 $dateFormat 屬性。該屬性決定日期被如何存儲(chǔ)到數(shù)據(jù)庫(kù)中,以及模型被序列化為數(shù)組或 JSON 時(shí)日期的格式:
<?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 常量:
<?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ù)連接,如果你想要為模型指定不同的連接,可以通過(guò) $connection 屬性來(lái)設(shè)置:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The connection name for the model. * * @var string */ protected $connection = 'connection-name'; }
獲取模型
創(chuàng)建完模型及其關(guān)聯(lián)的數(shù)據(jù)表后,就可以從數(shù)據(jù)庫(kù)中獲取數(shù)據(jù)了。將 Eloquent 模型看作功能強(qiáng)大的查詢構(gòu)建器,你可以使用它來(lái)流暢的查詢與其關(guān)聯(lián)的數(shù)據(jù)表。例如:
<?php use App\Flight; $flights = App\Flight::all(); foreach ($flights as $flight) { echo $flight->name; }
添加額外約束
Eloquent 的 all 方法返回模型表的所有結(jié)果,由于每一個(gè) Eloquent 模型都是一個(gè)查詢構(gòu)建器,你還可以添加約束條件到查詢,然后使用 get 方法獲取對(duì)應(yīng)結(jié)果:
$flights = App\Flight::where('active', 1) ->orderBy('name', 'desc') ->take(10) ->get();
注:由于 Eloquent 模型本質(zhì)上就是查詢構(gòu)建器,你可以在 Eloquent 查詢中使用查詢構(gòu)建器的所有方法。
集合
對(duì) Eloquent 中獲取多個(gè)結(jié)果的方法(比如 all 和 get)而言,其返回值是 Illuminate\Database\Eloquent\Collection 的一個(gè)實(shí)例,Collection 類提供了多個(gè)有用的函數(shù)來(lái)處理 Eloquent 結(jié)果集:
$flights = $flights->reject(function ($flight) { return $flight->cancelled; });
當(dāng)然,你也可以像數(shù)組一樣循環(huán)遍歷該集合:
foreach ($flights as $flight) { echo $flight->name; }
組塊結(jié)果集
如果你需要處理數(shù)據(jù)量很大的 Eloquent 結(jié)果集,可以使用 chunk 方法。chunk 方法會(huì)獲取一個(gè)指定數(shù)量的 Eloquent 模型“組塊”,并將其填充到給定閉包進(jìn)行處理。使用 chunk 方法在處理大量數(shù)據(jù)集合時(shí)能夠有效減少內(nèi)存消耗:
Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { // } });
傳遞給該方法的第一個(gè)參數(shù)是你想要獲取的“組塊”數(shù)目,閉包作為第二個(gè)參數(shù)被傳入用于處理每個(gè)從數(shù)據(jù)庫(kù)獲取的組塊數(shù)據(jù)。
使用游標(biāo)
cursor 方法允許你使用游標(biāo)迭代處理數(shù)據(jù)庫(kù)記錄,一次只執(zhí)行單個(gè)查詢,在處理大批量數(shù)據(jù)時(shí),cursor 方法可大幅減少內(nèi)存消耗:
foreach (Flight::where('foo', 'bar')->cursor() as $flight) { // }
獲取單個(gè)模型/聚合結(jié)果
當(dāng)然,除了從給定表中獲取所有記錄之外,還可以使用 find 和 first 獲取單個(gè)記錄。這些方法返回單個(gè)模型實(shí)例而不是模型集合:
// 通過(guò)主鍵獲取模型... $flight = App\Flight::find(1); // 獲取匹配查詢條件的第一個(gè)模型... $flight = App\Flight::where('active', 1)->first();
還可以通過(guò)傳遞主鍵數(shù)組來(lái)調(diào)用 find 方法,這將會(huì)返回匹配記錄集合:
$flights = App\Flight::find([1, 2, 3]);
Not Found 異常
有時(shí)候你可能想要在模型找不到的時(shí)候拋出異常,這在路由或控制器中非常有用,findOrFail 和 firstOrFail 方法會(huì)獲取查詢到的第一個(gè)結(jié)果。不過(guò),如果沒(méi)有任何查詢結(jié)果,Illuminate\Database\Eloquent\ModelNotFoundException 異常將會(huì)被拋出:
$model = App\Flight::findOrFail(1); $model = App\Flight::where('legs', '>', 100)->firstOrFail();
如果異常沒(méi)有被捕獲,那么 HTTP 404 響應(yīng)將會(huì)被發(fā)送給用戶,所以在使用這些方法的時(shí)候沒(méi)有必要對(duì)返回 404 響應(yīng)編寫額外的檢查:
Route::get('/api/flights/{id}', function ($id) { return App\Flight::findOrFail($id); });
獲取聚合結(jié)果
當(dāng)然,你還可以使用查詢構(gòu)建器提供的聚合方法,例如 count、sum、max,以及其它查詢構(gòu)建器提供的聚合函數(shù)。這些方法返回計(jì)算后的結(jié)果而不是整個(gè)模型實(shí)例:
$count = App\Flight::where('active', 1)->count(); $max = App\Flight::where('active', 1)->max('price');
插入/更新模型
插入
想要在數(shù)據(jù)庫(kù)中插入新的記錄,只需創(chuàng)建一個(gè)新的模型實(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 * @translator laravelacademy.org */ public function store(Request $request) { // 驗(yàn)證請(qǐng)求... $flight = new Flight; $flight->name = $request->name; $flight->save(); } }
在這個(gè)例子中,我們只是簡(jiǎn)單分配 HTTP 請(qǐng)求中的 name 參數(shù)值給 App\Flight 模型實(shí)例的 name 屬性,當(dāng)我們調(diào)用 save 方法時(shí),一條記錄將會(huì)被插入數(shù)據(jù)庫(kù)。created_at 和 updated_at 時(shí)間戳在 save 方法被調(diào)用時(shí)會(huì)自動(dòng)被設(shè)置,所以沒(méi)必要手動(dòng)設(shè)置它們。
更新
save 方法還可以用于更新數(shù)據(jù)庫(kù)中已存在的模型。要更新一個(gè)模型,應(yīng)該先獲取它,設(shè)置你想要更新的屬性,然后調(diào)用 save 方法。同樣,updated_at 時(shí)間戳?xí)蛔詣?dòng)更新,所以沒(méi)必要手動(dòng)設(shè)置其值:
$flight = App\Flight::find(1); $flight->name = 'New Flight Name'; $flight->save();
批量更新
更新操作還可以同時(shí)修改給定查詢提供的多個(gè)模型實(shí)例,在本例中,所有有效且 destination=San Diego 的航班都被標(biāo)記為延遲:
App\Flight::where('active', 1) ->where('destination', 'San Diego') ->update(['delayed' => 1]);
update 方法要求以數(shù)組形式傳遞鍵值對(duì)參數(shù),代表著數(shù)據(jù)表中應(yīng)該被更新的列。
注:通過(guò) Eloquent 進(jìn)行批量更新時(shí),saved 和 updated 模型事件將不會(huì)在更新模型時(shí)觸發(fā)。這是因?yàn)樵谶M(jìn)行批量更新時(shí)并沒(méi)有從數(shù)據(jù)庫(kù)獲取模型。
批量賦值
還可以使用 create 方法保存一個(gè)新的模型。該方法返回被插入的模型實(shí)例。但是,在此之前,你需要指定模型的 fillable 或 guarded 屬性,因?yàn)樗?Eloquent 模型都通過(guò)批量賦值(Mass Assignment)進(jìn)行保護(hù),這兩個(gè)屬性分別用于定義哪些模型字段允許批量賦值以及哪些模型字段是受保護(hù)的,不能顯式進(jìn)行批量賦值。
當(dāng)用戶通過(guò) HTTP 請(qǐng)求傳遞一個(gè)不被期望的參數(shù)值時(shí)就會(huì)出現(xiàn)安全隱患,然后該參數(shù)以不被期望的方式修改數(shù)據(jù)庫(kù)中的字段值。例如,惡意用戶通過(guò) HTTP 請(qǐng)求發(fā)送一個(gè) is_admin 參數(shù),然后該參數(shù)映射到模型的 create 方法,從而允許用戶將自己變成管理員。
所以,你應(yīng)該在模型中定義哪些屬性是可以進(jìn)行賦值的,使用模型上的 $fillable 屬性即可實(shí)現(xiàn)。例如,我們?cè)O(shè)置 Flight 模型上的 name 屬性可以被賦值:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * 可以被批量賦值的屬性. * * @var array */ protected $fillable = ['name']; }
設(shè)置完可以被賦值的屬性之后,我們就可以使用 create 方法在數(shù)據(jù)庫(kù)中插入一條新的記錄。create 方法返回保存后的模型實(shí)例:
$flight = App\Flight::create(['name' => 'Flight 10']);
如果你已經(jīng)有了一個(gè)模型實(shí)例,可以使用 fill 方法通過(guò)數(shù)組屬性來(lái)填充:
$flight->fill(['name' => 'Flight 22']);
黑名單屬性
$fillable 就像是可以被賦值屬性的“白名單”,還可以選擇使用 $guarded。$guarded 屬性包含你不想被賦值的屬性數(shù)組。所以不被包含在其中的屬性都是可以被賦值的,因此,$guarded 功能就像“黑名單”。當(dāng)然,這兩個(gè)屬性你只能同時(shí)使用其中一個(gè)而不能一起使用,因?yàn)樗鼈兪腔コ獾摹O旅娴睦又?,除?price 之外的所有屬性都是可以賦值的:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * 不能被批量賦值的屬性 * * @var array */ protected $guarded = ['price']; }
如果你想要讓所有屬性都是可批量賦值的,可以將 $guarded 屬性設(shè)置為空數(shù)組:
/** * The attributes that aren't mass assignable. * * @var array */ protected $guarded = [];
其它創(chuàng)建方法
firstOrCreate/firstOrNew
還有其它兩種可以用來(lái)創(chuàng)建模型的方法:firstOrCreate 和 firstOrNew。firstOrCreate 方法先嘗試通過(guò)給定列/值對(duì)在數(shù)據(jù)庫(kù)中查找記錄,如果沒(méi)有找到的話則通過(guò)給定屬性創(chuàng)建一個(gè)新的記錄。
firstOrNew 方法和 firstOrCreate 方法一樣先嘗試在數(shù)據(jù)庫(kù)中查找匹配的記錄,如果沒(méi)有找到,則返回一個(gè)新的模型實(shí)例。需要注意的是,通過(guò) firstOrNew 方法返回的模型實(shí)例并沒(méi)有持久化到數(shù)據(jù)庫(kù)中,你還需要調(diào)用 save 方法手動(dòng)持久化:
// 通過(guò)屬性獲取航班, 如果不存在則創(chuàng)建... $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']); // 通過(guò)name獲取航班,如果不存在則通過(guò)name和delayed屬性創(chuàng)建... $flight = App\Flight::firstOrCreate( ['name' => 'Flight 10'], ['delayed' => 1] ); // 通過(guò)屬性獲取航班, 如果不存在初始化一個(gè)新的實(shí)例... $flight = App\Flight::firstOrNew(['name' => 'Flight 10']); // 通過(guò)name獲取,如果不存在則通過(guò)name和delayed屬性創(chuàng)建新實(shí)例... $flight = App\Flight::firstOrNew( ['name' => 'Flight 10'], ['delayed' => 1] );
updateOrCreate
與此類似的,你還會(huì)碰到如果模型已存在則更新,否則創(chuàng)建新模型的場(chǎng)景,Laravel 提供了一個(gè) updateOrCreate 方法來(lái)一步完成。和 firstOrCreate 方法一樣,updateOrCreate 方法會(huì)持久化模型,所以無(wú)需調(diào)用 save():
// 如果有從奧克蘭到圣地亞哥的航班則將價(jià)格設(shè)置為 $99 // 如果沒(méi)有匹配的模型則創(chuàng)建之 $flight = App\Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99] );
刪除模型
要?jiǎng)h除一個(gè)模型,調(diào)用模型實(shí)例上的 delete 方法:
$flight = App\Flight::find(1); $flight->delete();
通過(guò)主鍵刪除模型
在上面的例子中,我們?cè)谡{(diào)用 delete 方法之前從數(shù)據(jù)庫(kù)中獲取該模型,不過(guò),如果你知道模型的主鍵的話,可以調(diào)用 destroy 方法直接刪除而不需要獲取它:
App\Flight::destroy(1); App\Flight::destroy([1, 2, 3]); App\Flight::destroy(1, 2, 3);
通過(guò)查詢刪除模型
當(dāng)然,你還可以通過(guò)查詢刪除多個(gè)模型,在本例中,我們刪除所有被標(biāo)記為無(wú)效的航班:
$deletedRows = App\Flight::where('active', 0)->delete();
注:通過(guò) Eloquent 進(jìn)行批量刪除時(shí),deleting 和 deleted 模型事件在刪除模型時(shí)不會(huì)被觸發(fā),這是因?yàn)樵谶M(jìn)行模型刪除時(shí)不會(huì)獲取模型。
軟刪除
除了從數(shù)據(jù)庫(kù)物理刪除記錄外,Eloquent 還可以對(duì)模型進(jìn)行“軟刪除”。當(dāng)模型被軟刪除后,它們并沒(méi)有真的從數(shù)據(jù)庫(kù)刪除,而是在模型上設(shè)置一個(gè) deleted_at 屬性并插入數(shù)據(jù)庫(kù),如果模型有一個(gè)非空 deleted_at 值,那么該模型已經(jīng)被軟刪除了。要啟用模型的軟刪除功能,可以使用模型上的Illuminate\Database\Eloquent\SoftDeletes trait 并添加 deleted_at 列到 $dates 屬性:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Flight extends Model { use SoftDeletes; /** * 應(yīng)該被調(diào)整為日期的屬性 * * @var array */ protected $dates = ['deleted_at']; }
當(dāng)然,應(yīng)該添加 deleted_at 列到數(shù)據(jù)表。Laravel Schema 構(gòu)建器包含一個(gè)輔助函數(shù)來(lái)創(chuàng)建該數(shù)據(jù)列:
Schema::table('flights', function ($table) { $table->softDeletes(); });
現(xiàn)在,當(dāng)調(diào)用模型的 delete 方法時(shí),deleted_at 列將被設(shè)置為當(dāng)前日期和時(shí)間,并且,當(dāng)查詢一個(gè)使用軟刪除的模型時(shí),被軟刪除的模型將會(huì)自動(dòng)從查詢結(jié)果中排除。
判斷給定模型實(shí)例是否被軟刪除,可以使用 trashed 方法:
if ($flight->trashed()) { // }
查詢被軟刪除的模型
包含軟刪除模型
正如上面提到的,軟刪除模型將會(huì)自動(dòng)從查詢結(jié)果中排除,不過(guò),如果你想要軟刪除模型出現(xiàn)在查詢結(jié)果中,可以使用 withTrashed 方法:
$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í)候你希望恢復(fù)一個(gè)被軟刪除的模型,可以使用 restore 方法:
$flight->restore();
你還可以在查詢中使用 restore 方法來(lái)快速恢復(fù)多個(gè)模型,同樣,這也不會(huì)觸發(fā)任何模型事件:
App\Flight::withTrashed() ->where('airline_id', 1) ->restore();
和 withTrashed 方法一樣,restore 方法也可以用于關(guān)聯(lián)查詢:
$flight->history()->restore();
永久刪除模型
有時(shí)候你真的需要從數(shù)據(jù)庫(kù)中刪除一個(gè)模型,要從數(shù)據(jù)庫(kù)中永久刪除記錄,可以使用 forceDelete 方法:
// 強(qiáng)制刪除單個(gè)模型實(shí)例... $flight->forceDelete(); // 強(qiáng)制刪除所有關(guān)聯(lián)模型... $flight->history()->forceDelete();
查詢作用域
全局作用域
全局作用域允許我們?yōu)榻o定模型的所有查詢添加條件約束。Laravel 自帶的軟刪除功能就使用了全局作用域來(lái)從數(shù)據(jù)庫(kù)中拉出所有沒(méi)有被刪除的模型。編寫自定義的全局作用域可以提供一種方便的、簡(jiǎn)單的方式來(lái)確保給定模型的每個(gè)查詢都有特定的條件約束。
編寫全局作用域
自定義全局作用域很簡(jiǎn)單,首先定義一個(gè)實(shí)現(xiàn) Illuminate\Database\Eloquent\Scope 接口的類,該接口要求你實(shí)現(xiàn)一個(gè)方法:apply。需要的話可以在 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 { /** * 應(yīng)用作用域到給定的Eloquent查詢構(gòu)建器. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void * @translator laravelacademy.org */ public function apply(Builder $builder, Model $model) { return $builder->where('age', '>', 200); } }
Laravel 應(yīng)用默認(rèn)并沒(méi)有為作用域預(yù)定義文件夾,所以你可以按照自己的喜好在 app 目錄下創(chuàng)建 Scopes 目錄。
注:如果你的全局作用域需要添加列到查詢的 select 子句,需要使用 addSelect 方法來(lái)替代 select,這樣就可以避免已存在的 select 查詢子句造成影響。
應(yīng)用全局作用域
要將全局作用域應(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); } }
添加作用域后,如果使用 User::all() 查詢則會(huì)生成如下 SQL 語(yǔ)句:
select * from `users` where `age` > 200
匿名的全局作用域
Eloquent 還允許我們使用閉包定義全局作用域,這在實(shí)現(xiàn)簡(jiǎn)單作用域的時(shí)候特別有用,這樣的話,我們就沒(méi)必要定義一個(gè)單獨(dú)的類了:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class User extends Model{ /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope('age', function(Builder $builder) { $builder->where('age', '>', 200); }); } }
移除全局作用域
如果想要在給定查詢中移除指定全局作用域,可以使用 withoutGlobalScope 方法,該方法接收全局作用域的類名作為其唯一參數(shù):
User::withoutGlobalScope(AgeScope::class)->get();
或者,如果你使用閉包定義的全局作用域的話:
User::withoutGlobalScope('age')->get();
如果你想要移除某幾個(gè)或全部全局作用域,可以使用 withoutGlobalScopes 方法:
// 移除所有全局作用域 User::withoutGlobalScopes()->get(); //移除某些全局作用域 User::withoutGlobalScopes([FirstScope::class, SecondScope::class])->get();
本地作用域
本地作用域允許我們定義通用的約束集合以便在應(yīng)用中復(fù)用。例如,你可能經(jīng)常需要獲取最受歡迎的用戶,要定義這樣的一個(gè)作用域,只需簡(jiǎn)單在對(duì)應(yīng) Eloquent 模型方法前加上一個(gè) scope 前綴。
作用域總是返回查詢構(gòu)建器實(shí)例:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * 只包含活躍用戶的查詢作用域 * * @return \Illuminate\Database\Eloquent\Builder */ public function scopePopular($query) { return $query->where('votes', '>', 100); } /** * 只包含激活用戶的查詢作用域 * * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } }
使用本地作用域
作用域被定義好了之后,就可以在查詢模型的時(shí)候調(diào)用作用域方法,但調(diào)用時(shí)不需要加上 scope 前綴,你甚至可以同時(shí)調(diào)用多個(gè)作用域,例如:
$users = App\User::popular()->active()->orderBy('created_at')->get();
動(dòng)態(tài)作用域
有時(shí)候你可能想要定義一個(gè)可以接收參數(shù)的作用域,你只需要將額外的參數(shù)添加到你的作用域即可。作用域參數(shù)應(yīng)該被定義在 $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); } }
現(xiàn)在,你可以在調(diào)用作用域時(shí)傳遞參數(shù)了:
$users = App\User::ofType('admin')->get();
比較模型
有時(shí)候你可能需要確定兩個(gè)模型是否是一樣的,is 方法可用于快速驗(yàn)證兩個(gè)模型是否有相同的主鍵、數(shù)據(jù)表、以及數(shù)據(jù)庫(kù)連接:
if ($post->is($anotherPost)) { // }
事件
Eloquent 模型可以觸發(fā)事件,允許你在模型生命周期中的多個(gè)時(shí)間點(diǎn)調(diào)用如下這些方法:retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored。事件允許你在一個(gè)指定模型類每次保存或更新的時(shí)候執(zhí)行代碼。
retrieved 事件會(huì)在從數(shù)據(jù)庫(kù)中獲取已存在模型時(shí)觸發(fā)。當(dāng)一個(gè)新模型被首次保存的時(shí)候,creating 和 created 事件會(huì)被觸發(fā)。如果一個(gè)模型已經(jīng)在數(shù)據(jù)庫(kù)中存在并調(diào)用 save 方法,updating/updated 事件會(huì)被觸發(fā),無(wú)論是創(chuàng)建還是更新,saving/saved 事件都會(huì)被觸發(fā)。
注:通過(guò) Eloquent 進(jìn)行批量更新時(shí),模型事件 saved 和 updated 不會(huì)在更新模型上觸發(fā),這是因?yàn)檫@些模型在進(jìn)行批量更新時(shí)沒(méi)有真正檢索過(guò)。
舉個(gè)例子,在 Eloquent 模型中定義一個(gè) $dispatchesEvents 屬性來(lái)映射模型生命周期中多個(gè)時(shí)間點(diǎn)與對(duì)應(yīng)事件類:
<?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; /** * The event map for the model. * * @var array */ protected $dispatchesEvents = [ 'saved' => UserSaved::class, 'deleted' => UserDeleted::class, ]; }
觀察者
定義觀察者
如果你在給定模型中監(jiān)聽(tīng)多個(gè)事件,可以使用觀察者來(lái)對(duì)所有監(jiān)聽(tīng)器分組到一個(gè)類中,觀察者類擁有反射你想要監(jiān)聽(tīng)的 Eloquent 事件對(duì)應(yīng)的方法名,每個(gè)方法接收模型作為唯一參數(shù)。Artisan 命令 make:observer 是創(chuàng)建新的觀察者類的最簡(jiǎn)單的方法:
php artisan make:observer UserObserver --model=User
該命令會(huì)將新的觀察者生成到 App/Observers 目錄,如果這個(gè)目錄不存在,Artisan 會(huì)自動(dòng)創(chuàng)建。剛創(chuàng)建的觀察者類代碼如下:
<?php namespace App\Observers; use App\User; class UserObserver { /** * Handle to the User "created" event. * * @param \App\User $user * @return void */ public function created(User $user) { // } /** * Handle the User "updated" event. * * @param \App\User $user * @return void */ public function updated(User $user) { // } /** * Handle the User "deleted" event. * * @param \App\User $user * @return void */ public function deleted(User $user) { // } }
要注冊(cè)觀察者,使用你想要觀察模型的 observe 方法,你可以在某個(gè)服務(wù)提供者的 boot 方法中注冊(cè)觀察者,在本例中,我們?cè)?AppServiceProvider 中注冊(cè)觀察者:
<?php namespace App\Providers; use App\User; use App\Observers\UserObserver; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { User::observe(UserObserver::class); } /** * Register the service provider. * * @return void */ public function register() { // } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Laravel 5框架學(xué)習(xí)之Eloquent (laravel 的ORM)
- Laravel Eloquent ORM 實(shí)現(xiàn)查詢表中指定的字段
- Laravel Eloquent ORM 多條件查詢的例子
- laravel 解決Eloquent ORM的save方法無(wú)法插入數(shù)據(jù)的問(wèn)題
- Laravel框架Eloquent ORM新增數(shù)據(jù)、自定義時(shí)間戳及批量賦值用法詳解
- laravel框架數(shù)據(jù)庫(kù)操作、查詢構(gòu)建器、Eloquent ORM操作實(shí)例分析
- Laravel框架Eloquent ORM刪除數(shù)據(jù)操作示例
- laravel 數(shù)據(jù)遷移與 Eloquent ORM的實(shí)現(xiàn)方法
- Laravel框架Eloquent ORM簡(jiǎn)介、模型建立及查詢數(shù)據(jù)操作詳解
- Laravel框架Eloquent ORM修改數(shù)據(jù)操作示例
- laravel5.6 框架操作數(shù)據(jù) Eloquent ORM用法示例
相關(guān)文章
CI框架出現(xiàn)mysql數(shù)據(jù)庫(kù)連接資源無(wú)法釋放的解決方法
這篇文章主要介紹了CI框架出現(xiàn)mysql數(shù)據(jù)庫(kù)連接資源無(wú)法釋放的解決方法,分析了CI框架出現(xiàn)連接超過(guò)最大值的原因與相應(yīng)的解決方法,涉及CI框架相關(guān)配置技巧,需要的朋友可以參考下2016-05-05php定義數(shù)組和使用示例(php數(shù)組的定義方法)
這篇文章主要介紹了php定義數(shù)組和使用示例(php數(shù)組的定義方法),需要的朋友可以參考下2014-03-03ThinkPHP寫數(shù)組插入與獲取最新插入數(shù)據(jù)ID實(shí)例
這篇文章主要介紹了ThinkPHP寫數(shù)組插入與獲取最新插入數(shù)據(jù)ID的方法,實(shí)例講述了ThinkPHP基于數(shù)組操作數(shù)據(jù)庫(kù)的方法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-11-11四個(gè)常見(jiàn)html網(wǎng)頁(yè)亂碼問(wèn)題及解決辦法
當(dāng)我們做網(wǎng)頁(yè)時(shí),經(jīng)常會(huì)遇到網(wǎng)頁(yè)中文亂碼,那么此問(wèn)題如何解決呢,下面腳本之家的小編給大家分享四個(gè)常見(jiàn)html網(wǎng)頁(yè)亂碼問(wèn)題及解決辦法,需要的朋友可以參考下2015-09-09php用戶注冊(cè)頁(yè)面利用js進(jìn)行表單驗(yàn)證具體實(shí)例
這篇文章介紹了php用戶注冊(cè)頁(yè)面利用js進(jìn)行表單驗(yàn)證具體實(shí)例,有需要的朋友可以參考一下2013-10-10