通常,我們需要從過濾后的查詢中進(jìn)行多次查詢。所以,大多數(shù)時候我們使用 query()
方法,
讓我們編寫一個查詢來獲取今天創(chuàng)建的已激活和未激活的產(chǎn)品。
$query = Product::query();
$today = request()->q_date ?? today();
if($today){
$query->where('created_at', $today);
}
// 讓我們獲取已激活和未激活的產(chǎn)品
$active_products = $query->where('status', 1)->get(); // 這一行修改了 $query 對象變量
$inactive_products = $query->where('status', 0)->get(); // 所以這里我們不會找到任何未激活的產(chǎn)品
但是,在獲得 $active_products
后,$query
會被修改。$inactive_products
不會從 $query
中獲取任何未激活產(chǎn)品,并且每次都返回空集合。因為,它嘗試從 $active_products
中查找未激活產(chǎn)品($query
僅返回激活產(chǎn)品)。
為了解決這個問題,我們可以通過復(fù)用這個 $query
對象來進(jìn)行多次查詢。
因此,我們需要在執(zhí)行任何 $query
修改操作之前克隆這個 $query
。
$active_products = (clone $query)->where('status', 1)->get(); // 它不會修改 $query
$inactive_products = (clone $query)->where('status', 0)->get(); // 所以我們將從 $query 中獲取未激活的產(chǎn)品
在 Eloquent 中,使用 whereDay()
、whereMonth()
、whereYear()
、whereDate()
和 whereTime()
函數(shù)檢查日期。
$products = Product::whereDate('created_at', '2018-01-31')->get();
$products = Product::whereMonth('created_at', '12')->get();
$products = Product::whereDay('created_at', '31')->get();
$products = Product::whereYear('created_at', date('Y'))->get();
$products = Product::whereTime('created_at', '=', '14:13:58')->get();
如果要增加數(shù)據(jù)庫某個表中的某個列,只需使用 increment()
函數(shù)。你不僅可以增加 1,還可以增加一些數(shù)字,比如 50。
Post::find($post_id)->increment('view_count');
User::find($user_id)->increment('points', 50);
如果你的數(shù)據(jù)庫表不包含 timestamp 字段 created_at
和 updated_at
,你可以使用 $timestamps = false
屬性指定 Eloquent 模型不使用它們。
class Company extends Model
{
public $timestamps = false;
}
使用軟刪除時,您可以在一個句子中恢復(fù)多行。
Post::onlyTrashed()->where('author_id', 1)->restore();
當(dāng)調(diào)用 Eloquent 的 Model::all()
時,你可以指定要返回的列。
$users = User::all(['id', 'name', 'email']);
除了 findOrFail()
之外,還有 Eloquent 方法 firstOrFail()
,如果沒有找到查詢記錄,它將返回 404 頁。
$user = User::where('email', 'povilas@laraveldaily.com')->firstOrFail();
在 Eloquent Query Builder 中,您可以指定「as」以返回具有不同名稱的任何列,就像在普通 SQL 查詢中一樣。
$users = DB::table('users')->select('name', 'email as user_email')->get();
在 Eloquent 查詢之后,您可以使用 Collections 中的 map()
函數(shù)來修改行。
$users = User::where('role_id', 1)->get()->map(function (User $user) {
$user->some_column = some_function($user);
return $user;
});
如果您使用的是非 Laravel 數(shù)據(jù)庫并且時間戳列的名稱不同怎么辦?也許,你有 create_time 和 update_time。 幸運的是,您也可以在模型中指定它們:
class Role extends Model
{
const CREATED_AT = 'create_time';
const UPDATED_AT = 'update_time';
}
不要使用:
User::orderBy('created_at', 'desc')->get();
你可以做的更快:
User::latest()->get();
默認(rèn)情況下,latest()
會按 created_at
降序排序。
有一個相反的方法 oldest()
,它按 created_at
升序排序:
User::oldest()->get();
此外,您可以指定另一列進(jìn)行排序。 例如,如果你想使用 updated_at
,你可以這樣做:
$lastUpdatedUser = User::latest('updated_at')->first();
如果您想在創(chuàng)建記錄時生成一些 DB 列值,請將其添加到模型的 boot()
方法中。
例如,如果您有一個字段 「position」,并且想要將下一個可用位置分配給新記錄(例如 Country::max('position') + 1)
,請執(zhí)行以下操作:
class Country extends Model {
protected static function boot()
{
parent::boot();
Country::creating(function($model) {
$model->position = Country::max('position') + 1;
});
}
}
使用類似 whereRaw()
方法的 SQL 原始查詢,直接在查詢中進(jìn)行一些特定于數(shù)據(jù)庫的計算,而不是在 Laravel 中,通常結(jié)果會更快。 例如,如果您想獲得注冊后 30 天以上仍處于活躍狀態(tài)的用戶,請使用以下代碼:
User::where('active', 1)
->whereRaw('TIMESTAMPDIFF(DAY, created_at, updated_at) > ?', 30)
->get();
您可以在 Eloquent 中組合和鏈?zhǔn)讲樵冏饔糜颍诓樵冎惺褂枚鄠€作用域。
模型:
public function scopeActive($query) {
return $query->where('active', 1);
}
public function scopeRegisteredWithinDays($query, $days) {
return $query->where('created_at', '>=', now()->subDays($days));
}
控制器中使用:
$users = User::registeredWithinDays(30)->active()->get();
如果你使用 whereDate()
查詢今日的記錄,可以直接使用 Carbon 的 now()
方法,會自動轉(zhuǎn)換為日期進(jìn)行查詢,無需指定 ->toDateString()
:
// 今日注冊的用戶
$todayUsers = User::whereDate('created_at', now()->toDateString())->get();
// 無需 toDateString() ,直接 now() 即可
$todayUsers = User::whereDate('created_at', now())->get();
你可以對 Eloquent 結(jié)果進(jìn)行條件分組,下面的示例是由用戶名稱的第一個單詞進(jìn)行分組:
$users = User::all()->groupBy(function($item) {
return $item->name[0];
});
如果有一個數(shù)據(jù)庫字段你想只更新一次,可以使用 Eloquent 的修改器來實現(xiàn):
class User extends Model
{
public function setEmailAttribute($value)
{
if ($this->email) {
return;
}
$this->attributes['email'] = $value;
}
}
find()
不止可以查詢一條數(shù)據(jù),當(dāng)傳入多個 ID 的值會返回這些結(jié)果的集合:
// 返回 Eloquent Model
$user = User::find(1);
// 返回 Eloquent Collection
$users = User::find([1,2,3]);
感謝 @tahiriqbalnajam 提供
find()
可在查詢多條的數(shù)據(jù)的情況下,指定只返回哪些字段:
// 會返回只包含 first_name 和 email 的 Eloquent 模型
$user = User::find(1, ['first_name', 'email']);
// 會返回只包含 first_name 和 email 兩個字段的 Eloquent 集合
$users = User::find([1,2,3], ['first_name', 'email']);
感謝 @tahiriqbalnajam 提供
您還可以使用 whereKey()
方法查找多條記錄,該方法負(fù)責(zé)處理哪個字段正是您的主鍵(id
是默認(rèn)值,但您可以在 Eloquent 模型中覆蓋它):
$users = User::whereKey([1,2,3])->get();
您不想在模型中使用自動遞增 ID?
遷移:
Schema::create('users', function (Blueprint $table) {
// $table->increments('id');
$table->uuid('id')->unique();
});
模型:
class User extends Model
{
public $incrementing = false;
protected $keyType = 'string';
protected static function boot()
{
parent::boot();
User::creating(function ($model) {
$model->setId();
});
}
public function setId()
{
$this->attributes['id'] = Str::uuid();
}
}
從 Laravel 6 開始,您可以在 Eloquent 語句中使用 addSelect (),并對附加的列進(jìn)行一些計算。
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
在進(jìn)行 Eloquent 查詢時,如果您想在返回中隱藏特定字段,最快捷的方法之一是在集合結(jié)果上添加 ->makeHidden()
。
$users = User::all()->makeHidden(['email_verified_at', 'deleted_at']);
如果您想捕獲 Eloquent Query 異常,請使用特定的 QueryException
代替默認(rèn)的 Exception 類,您將能夠獲得錯誤的確切 SQL 代碼。
try {
// 一些 Eloquent/SQL 聲明
} catch (\Illuminate\Database\QueryException $e) {
if ($e->getCode() === '23000') { // integrity constraint violation
return back()->withError('Invalid data');
}
}
不要忘記,當(dāng)您使用 Eloquent 時會排除已軟刪除的條目,但如果您使用查詢構(gòu)造器,則不會起作用。
// 排除軟刪除條目
$users = User::all();
// 不排除軟刪除條目
$users = User::withTrashed()->get();
// 不排除軟刪除條目
$users = DB::table('users')->get();
如果你需要執(zhí)行一個簡單的 SQL 查詢,但沒有得到任何結(jié)果 —— 比如改變數(shù)據(jù)庫模式中的某些東西,只需執(zhí)行 DB::statement()
。
DB::statement('DROP TABLE users');
DB::statement('ALTER TABLE projects AUTO_INCREMENT=123');
如果您執(zhí)行了兩個數(shù)據(jù)庫操作,第二個可能會出錯,那么您應(yīng)該回滾第一個,對嗎?
為此,我建議使用 DB Transactions,它在 Laravel 中非常簡單:
DB::transaction(function () {
DB::table('users')->update(['votes' => 1]);
DB::table('posts')->delete();
});
如果你需要檢查記錄是否存在,然后更新它,或者創(chuàng)建一個新記錄,你可以用一句話來完成 - 使用 Eloquent updateOrCreate()
方法:
// 不要這樣做
$flight = Flight::where('departure', 'Oakland')
->where('destination', 'San Diego')
->first();
if ($flight) {
$flight->update(['price' => 99, 'discounted' => 1]);
} else {
$flight = Flight::create([
'departure' => 'Oakland',
'destination' => 'San Diego',
'price' => 99,
'discounted' => 1
]);
}
// 一句話完成
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
感謝 @pratiksh404 提供
如果您有提供集合 posts
這樣的緩存鍵,想在新增或更新時移除緩存鍵,可以在您的模型上調(diào)用靜態(tài)的 saved
函數(shù):
class Post extends Model
{
// 存儲或更新時移除緩存
public static function boot()
{
parent::boot();
static::saved(function () {
Cache::forget('posts');
});
}
}
感謝 @syofyanzuhad 提供
想要改變 created_at
的格式,您可以在模型中添加一個方法,如下所示:
public function getCreatedAtFormattedAttribute()
{
return $this->created_at->format('H:i d, M Y');
}
你可以在需要改變時間格式時使用 $entry->created_at_formatted
,它會返回 created_at
的屬性如同 04:19 23, Aug 2020
。
你也可以用同樣的方法更改 updated_at
:
public function getUpdatedAtFormattedAttribute()
{
return $this->updated_at->format('H:i d, M Y');
}
在有需要的時候使用 $entry->updated_at_formatted
。它會返回 updated_at
的屬性如同: 04:19 23, Aug 2020
。
感謝 @pratiksh404 提供
如果你的輸入字段有一個數(shù)組需要存儲為 JSON 格式,你可以在模型中使用 $casts
屬性。 這里的 images
是 JSON 屬性。
protected $casts = [
'images' => 'array',
];
這樣你可以以 JSON 格式存儲它,但當(dāng)你從 DB 中讀取時,它會以數(shù)組方式使用。
如果你有兩個非常相似的模型(比如送貨地址和賬單地址),而且你想要復(fù)制其中一個作為另一個,你可以使用 replicate()
方法并更改一部分屬性。
官方文檔 的示例:
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
$billing->save();
有時我們需要將大量的數(shù)據(jù)加載到內(nèi)存中,比如:
$orders = Order::all();
但如果我們有非常龐大的數(shù)據(jù)庫,這可能會很慢,因為 Laravel 會準(zhǔn)備好模型類的對象。在這種情況下,Laravel 有一個很方便的函數(shù) toBase()
。
$orders = Order::toBase()->get();
//$orders 將包含 `Illuminate\Support\Collection` 與對象 `StdClass`
通過調(diào)用這個方法,它將從數(shù)據(jù)庫中獲取數(shù)據(jù),但它不會準(zhǔn)備模型類。同時,向 get()
方法傳遞一個字段數(shù)組通常是個好主意,這樣可以防止從數(shù)據(jù)庫中獲取所有字段。
如果你創(chuàng)建了一個 Laravel 模板作為其他開發(fā)者的「啟動器」, 并且你不能控制他們以后會在模型的 $fillable/$guarded
中填寫什么,你可以使用 forceFill()
$team->update(['name' => $request->name])
如果 name 不在團(tuán)隊模型的 $fillable
中,怎么辦?或者如果根本就沒有 $fillable/$guarded
, 怎么辦?
$team->forceFill(['name' => $request->name])
這將忽略該查詢的 $fillable
并強制執(zhí)行。
如果你有一個 3 層的父子結(jié)構(gòu),比如電子商店中的分類,你想顯示第三層的產(chǎn)品數(shù)量,你可以使用 with('yyy.yyy')
,然后添加 withCount()
作為條件
class HomeController extend Controller
{
public function index()
{
$categories = Category::query()
->whereNull('category_id')
->with(['subcategories.subcategories' => function($query) {
$query->withCount('products');
}])->get();
}
}
class Category extends Model
{
public function subcategories()
{
return $this->hasMany(Category::class);
}
public function products()
{
return $this->hasMany(Product::class);
}
}
<ul>
@foreach($categories as $category)
<li>
{{ $category->name }}
@if ($category->subcategories)
<ul>
@foreach($category->subcategories as $subcategory)
<li>
{{ $subcategory->name }}
@if ($subcategory->subcategories)
<ul>
@foreach ($subcategory->subcategories as $subcategory)
<li>{{ $subcategory->name }} ({{ $subcategory->product_count }})</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
你不僅可以用 find()
來搜索單條記錄,還可以用 IDs 的集合來搜索更多的記錄,方法如下:
return Product::whereIn('id', $this->productIDs)->get();
你可以這樣做:
return Product::find($this->productIDs)
當(dāng)查詢一條記錄時,如果沒有找到,你可能想執(zhí)行一些操作。除了用 ->firstOrFail()
會拋出 404 之外,你可以在失敗時執(zhí)行任何操作,只需要使用 ->firstOr(function() { ... })
$model = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
})
不要使用 find()
,然后再檢查記錄是否存在,使用 findOrFail()
。
$product = Product::find($id);
if (!$product) {
abort(404);
}
$product->update($productDataArray);
更簡單的方法:
$product = Product::findOrFail($id); // 查詢不到時顯示 404
$product->update($productDataArray);
可以使用 abort_if()
作為判斷條件和拋出錯誤頁面的快捷方式。
$product = Product::findOrFail($id);
if($product->user_id != auth()->user()->id){
abort(403);
}
更簡單的方法:
/* abort_if(CONDITION, ERROR_CODE) */
$product = Product::findOrFail($id);
abort_if ($product->user_id != auth()->user()->id, 403)
感謝 @back2Lobby 提供
我們可以使用 Model::delete()
執(zhí)行額外的操作來覆蓋原本的刪除方法。
// App\Models\User.php
public function delete(){
//執(zhí)行你想要的額外操作
//然后進(jìn)行正常的刪除
Model::delete();
}
當(dāng)你需要在保存數(shù)據(jù)到數(shù)據(jù)庫時自動填充一個字段 (例如: slug),使用模型觀察者來代替重復(fù)編寫代碼。
use Illuminate\Support\Str;
class Article extends Model
{
...
protected static function boot()
{
parent:boot();
static::saving(function ($model) {
$model->slug = Str::slug($model->title);
});
}
}
感謝 @sky_0xs 提供
你可以使用 explain()
方法來獲取查詢語句的額外信息。
Book::where('name', 'Ruskin Bond')->explain()->dd();
Illuminate\Support\Collection {#5344
all: [
{#15407
+"id": 1,
+"select_type": "SIMPLE",
+"table": "books",
+"partitions": null,
+"type": "ALL",
+"possible_keys": null,
+"key": null,
+"key_len": null,
+"ref": null,
+"rows": 9,
+"filtered": 11.11111164093,
+"Extra": "Using where",
},
],
}
感謝 @amit_merchant 提供
// 一個例子
if ( 0 === $model->where('status', 'pending')->count() ) {
}
// 我不關(guān)心它有多少數(shù)據(jù)只要它是0
// Laravel 的 exists() 方法會很清晰:
if ( ! $model->where('status', 'pending')->exists() ) {
}
// 但我發(fā)現(xiàn)上面這條語句中的!很容易被忽略。
// 那么 doesntExist() 方法會讓這個例子更加清晰
if ( $model->where('status', 'pending')->doesntExist() ) {
}
感謝 @ShawnHooper 提供
如果你有一個特性,你想把它添加到幾個模型中,自動調(diào)用它們的 boot()
方法,你可以把特質(zhì)的方法作為 boot (特性名稱)來調(diào)用。
class Transaction extends Model
{
use MultiTenantModelTrait;
}
class Task extends Model
{
use MultiTenantModelTrait;
}
trait MultiTenantModelTrait
{
// 這個方法名是 boot[特性名稱]。
// 它將作為事務(wù)/任務(wù)的 boot() 被自動調(diào)用。
public static function bootMultiTenantModelTrait()
{
static::creating(function ($model) {
if (!$isAdmin) {
$isAdmin->created_by_id = auth()->id();
}
})
}
}
// 在 find($id) 方法中第二個參數(shù)可以是返回字段
Studdents::find(1, ['name', 'father_name']);
// 這樣我們可以查詢 ID 為 '1' 并返回 name , father_name 字段
// 我們可以用數(shù)組的方式傳遞更多的 ID
Studdents::find([1,2,3], ['name', 'father_name']);
// 輸出: ID 為 1,2,3 并返回他們的 name , father_name 字段
在 Laravel 中,有兩種常見的方法來確定一個表是否為空表。直接在模型上使用 exists()
或者 count()
!
一個返回嚴(yán)格的布爾值,另一個返回一個整數(shù),你都可以在條件語句中使用。
public function index()
{
if (\App\Models\User::exists()) {
// 如果表有任何保存的數(shù)據(jù),則返回布爾值 true 或 false。
}
if (\App\Models\User::count()) {
// 返回表中數(shù)據(jù)的數(shù)量。
}
}
感謝 @aschmelyun 提供
// 設(shè)定默認(rèn)模型
// 假設(shè)你有一篇 Post (帖子) 屬于一個 Author (作者),代碼如下:
$post->author->name;
// 當(dāng)然你可以像這樣阻止錯誤:
$post->author->name ?? ''
// 或者
@$post->auhtor->name
// 但你可以在Eloquent關(guān)系層面上做到這一點。
// 如果沒有作者關(guān)聯(lián)帖子,這種關(guān)系將返回一個空的App/Author模型。
public function author() {
return $this->belongsTo('App\Author')->withDefaults();
}
// 或者
public function author() {
return $this->belongsTo('App\Author')->withDefault([
'name' => 'Guest Author'
]);
}
感謝 @coderahuljat 提交
Eloquent 模型數(shù)據(jù)改變后,你可以使用 getOriginal () 方法來獲取原始數(shù)據(jù)。
$user = App\User::first();
$user->name; // John
$user->name = "Peter"; // Peter
$user->getOriginal('name'); // John
$user->getOriginal(); // 原始的 $user 記錄
感謝 @devThaer 提交
Laravel 還可以使用 .sql 文件來更簡單的創(chuàng)建數(shù)據(jù)庫。
DB::unprepared(
file_get_contents(__DIR__ . './dump.sql')
);
感謝 @w3Nicolas 提交
更多建議: