您可以在關聯(lián)關系中直接指定 orderBy()
。
public function products()
{
return $this->hasMany(Product::class);
}
public function productsByName()
{
return $this->hasMany(Product::class)->orderBy('name');
}
假如你經(jīng)常在模型關聯(lián)關系中添加某些相同的 where 條件,可以創(chuàng)建一個獨立方法。
Model:
public function comments()
{
return $this->hasMany(Comment::class);
}
public function approved_comments()
{
return $this->hasMany(Comment::class)->where('approved', 1);
}
你可以在很多地方使用原始數(shù)據(jù)庫查詢,比如在 groupBy()
后面調(diào)用 havingRaw()
。
Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();
你可以在關聯(lián)關系查詢中使用 has()
實現(xiàn)兩層關聯(lián)查詢。
// Author -> hasMany(Book::class);
// Book -> hasMany(Rating::class);
$authors = Author::has('books.ratings')->get();
在一對多關系中,你可以通過條件過濾,獲取符合的數(shù)據(jù)。比如需要查找有哪些作者出版書的數(shù)量大于 5。
// Author -> hasMany(Book::class)
$authors = Author::has('books', '>', 5)->get();
你可以在 belongsTo
關系中設置返回一個默認的模型,從而避免類似于使用 {{ $post->user->name }}
當 $post->user
不存在的時候,引起的致命的錯誤。
public function user()
{
return $this->belongsTo('App\User')->withDefault();
}
在一對多關系中,你可以使用 saveMany()
通過一次提交,保存多條關聯(lián)數(shù)據(jù)。
$post = Post::find(1);
$post->comments()->saveMany([
new Comment(['message' => 'First comment']),
new Comment(['message' => 'Second comment']),
]);
在 Laravel 中,你可以在一條語句中渴求式加載多個層級,在這個例子中,我們不僅加載作者關系,而且還加載作者模型上的國家關系。
$users = App\Book::with('author.country')->get();
你可以在 Laravel 中渴求式加載并指定關聯(lián)中的特定字段。
$users = App\Book::with('author:id,name')->get();
你同樣可以在深層級中這樣做,如第二層級關系:
$users = App\Book::with('author.country:id,name')->get();
如果你想更新一條數(shù)據(jù)同時更新它父級關聯(lián)的 updated_at
字段 (例如:你添加一條帖子評論,想同時更新帖子的 posts.updated_at
),只需要在子模型中使用 $touches = ['post'];
屬性。
class Comment extends Model
{
protected $touches = ['post'];
}
永遠不要在不檢查關聯(lián)是否存在時使用 $model->relationship->field
。
它可能因為任何原因,如在你的代碼之外,被別人的隊列任務等等被刪除。用 if-else
,或者在 Blade 模板中 {{$model->relationship->field ? ? '' }}
,或者 {{optional($model->relationship)->field }}
。在 php8 中,你甚至可以使用 null 安全操作符 {{ $model->relationship?->field) }}
如果你有 hasMany()
的關聯(lián),并且你想統(tǒng)計子關聯(lián)記錄的條數(shù),不要寫一個特殊的查詢。例如,如果你的用戶模型上有帖子和評論,使用 withCount()
。
public function index()
{
$users = User::withCount(['posts', 'comments'])->get();
return view('users', compact('users'));
}
同時,在 Blade 文件中,您可以通過使用 {relationship}_count
屬性獲得這些數(shù)量:
@foreach ($users as $user)
<tr>
<td>{{ $user->name }}</td>
<td class="text-center">{{ $user->posts_count }}</td>
<td class="text-center">{{ $user->comments_count }}</td>
</tr>
@endforeach
也可以按照這些統(tǒng)計字段進行排序:
User::withCount('comments')->orderBy('comments_count', 'desc')->get();
假如您想加載關聯(lián)關系的數(shù)據(jù),同時需要指定一些限制或者排序的閉包函數(shù)。例如,您想獲取人口最多的前 3 座城市信息,可以按照如下方式實現(xiàn):
$countries = Country::with(['cities' => function($query) {
$query->orderBy('population', 'desc');
$query->take(3);
}])->get();
您不僅可以實現(xiàn)對關聯(lián)模型的實時預加載,還可以根據(jù)情況動態(tài)設置某些關聯(lián)關系,需要在模型初始化方法中處理:
class ProductTag extends Model
{
protected $with = ['product'];
public function __construct() {
parent::__construct();
$this->with = ['product'];
if (auth()->check()) {
$this->with[] = 'user';
}
}
}
在關聯(lián)關系中,如果創(chuàng)建子關系的記錄中需要用到父關系的 ID ,那么使用 hasMany
比使用 belongsTo
更簡潔。
// if Post -> belongsTo(User), and User -> hasMany(Post)...
// Then instead of passing user_id...
Post::create([
'user_id' => auth()->id(),
'title' => request()->input('title'),
'post_text' => request()->input('post_text'),
]);
// Do this
auth()->user()->posts()->create([
'title' => request()->input('title'),
'post_text' => request()->input('post_text'),
]);
如果你想要重命名「pivot」并用其他的什么方式來調(diào)用關系,你可以在你的關系聲明中使用 ->as('name')
來為關系取名。
模型 Model:
public function podcasts() {
return $this->belongsToMany('App\Podcast')
->as('subscription')
->withTimestamps();
}
控制器 Controller:
$podcasts = $user->podcasts();
foreach ($podcasts as $podcast) {
// instead of $podcast->pivot->created_at ...
echo $podcast->subscription->created_at;
}
如果有一個 belongsTo()
關系,你可以在僅僅一條語句中更新這個 Elquent 關系:
// if Project -> belongsTo(User::class)
$project->user->update(['email' => 'some@gmail.com']);
從 Laravel 7 開始,你不需要在遷移(migration)中為一些關系字段寫兩行代碼 —— 一行是字段,一行是外鍵。你可以使用 foreignId()
方法。
// Laravel 7 之前
Schema::table('posts', function (Blueprint $table)) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
}
// 從 Laravel 7 開始
Schema::table('posts', function (Blueprint $table)) {
$table->foreignId('user_id')->constrained();
}
// 或者你的字段不同于表中的引用的
Schema::table('posts', function (Blueprint $table)) {
$table->foreignId('created_by_id')->constrained('users', 'column');
}
在 Eloquent 中,你可以在同一條語句中使用 whereHas()
和 orDoesntHave()
。
User::whereHas('roles', function($query) {
$query->where('id', 1);
})
->orDoesntHave('roles')
->get();
如果你的 Eloquent 關系名是動態(tài)的,那么你需要檢查項目中是否存在相同名稱的關系。你可以使用這個 PHP 方法 method_exists($object, $methodName)
。
$user = User::first();
if (method_exists($user, 'roles')) {
// 使用 $user->roles()-> 做其它事情...
}
在多對多關系中,您定義的中間表里面可能會包含擴展字段,甚至可能包含其它的關聯(lián)關系。
下面生成一個中間表模型:
php artisan make:model RoleUser --pivot
然后,給 belongsToMany()
指定 ->using()
方法。下面就是見證奇跡的時刻:
// in app/Models/User.php
public function roles()
{
return $this->belongsToMany(Role::class)
->using(RoleUser::class)
->withPivot(['team_id']);
}
// app/Models/RoleUser.php: 注意繼承的是 Pivot, 不是 Model
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUser extends Pivot
{
public function team()
{
return $this->belongsTo(Team::class);
}
}
// 在控制器里面就可以直接使用如下方式獲取中間表 RoleUser 的 Team 信息:
$firstTeam = auth()->user()->roles()->first()->pivot->team->name;
除了可以使用 Eloquent 中的 withCount()
方法統(tǒng)計子集數(shù)量外,還可以直接用 loadCount()
更加便捷和快速獲取:
// if your Book hasMany Reviews...
$book = App\Book::first();
$book->loadCount('reviews');
// 使用 $book->reviews_count 獲取 Reviews 數(shù)量;
// 還可以在 loadCount 中添加額外的查詢條件
$book->loadCount(['reviews' => function ($query) {
$query->where('rating', 5);
}]);
您可以使用 inRandomOrder()
對 Eloquent 的查詢結果進行隨機排序,同時也可以作用于關聯(lián)關系中,實現(xiàn)關聯(lián)數(shù)據(jù)的隨機排序。
// If you have a quiz and want to randomize questions...
// 1. 獲取隨機答案:
$questions = Question::inRandomOrder()->get();
// 2. 獲取隨機問題的隨機答案:
$questions = Question::with(['answers' => function($q) {
$q->inRandomOrder();
}])->inRandomOrder()->get();
通過我項目中的一個代碼例子,展示了過濾一對多關系的可能性。
TagType
-> hasMany tags
-> hasMany examples
如果你想查詢所有的標簽類型,伴隨他們的標簽,但只包含有實例的標簽,并按照實例數(shù)量倒序。
$tag_types = TagType::with(['tags' => function ($query) {
$query->has('examples')
->withCount('examples')
->orderBy('examples_count', 'desc');
}])->get();
如果你有一個多對多關聯(lián),你可以在中間表中添加一個額外字段,這樣你可以在查詢列表時用它排序。
class Tournament extends Model
{
public function countries()
{
return $this->belongsToMany(Country::class)->withPivot(['position']);
}
}
class TournamentsController extends Controller
public function whatever_method() {
$tournaments = Tournament::with(['countries' => function($query) {
$query->orderBy('position');
}])->latest()->get();
}
在 Laravel 8.57 中發(fā)布:通過包含一個簡單條件的簡短方法來寫 whereHas()
。
// 以前
User::whereHas('posts', function ($query) {
$query->where('published_at', '>', now());
})->get();
// 現(xiàn)在
User::whereRelation('posts', 'published_at', '>', now())->get();
如果你有一個重復使用的回調(diào)函數(shù),你可以提取它作為變量。
// 你有一個很長的包含重復的回調(diào)函數(shù)
$results = Model::with('relationships')
->whereHas('relationships', function($query) use ($var1, $var2) {
return $query->where('field1', $var1)->where('field2', $var2);
})
->withCount('relationships', function($query) use ($var1, $var2) {
return $query->where('field1', $var1)->where('field2', $var2);
})
->get();
// 你可以提取它作為變量
$callback = function($query) use ($var1, $var2) {
return $query->where('field1', $var1)->where('field2', $var2);
});
$results = Model::with('relationships')
->whereHas('relationships', $callback)
->withCount('relationships', $callback)
->get();
class User
{
public function posts()
{
return $this->hasMany(Post::class);
}
// 添加一個獲取器
public function getPublishedPostsAttribute()
{
return $this->posts->filter(fn ($post) => $post->published);
}
// 添加一個關聯(lián)
public function publishedPosts()
{
return $this->hasMany(Post::class)->where('published', true);
}
}
感謝 @anwar_nairi 提供
Laravel 8.63.0 版本帶有一個新的 Eloquent 查詢構建器方法 whereBelongsTo()
。
這允許你從你的查詢中刪除 BelongsTo 外鍵名稱,并使用關聯(lián)方法替代(該方法會根據(jù)類名自動確定關聯(lián)與外鍵,也可以添加第二個參數(shù)手動關聯(lián))。
// 以前:
$query->where('author_id', $author->id)
// 現(xiàn)在:
$query->whereBelongsTo($author)
// 輕松添加更多的過濾功能:
Post::query()
->whereBelongsTo($author)
->whereBelongsTo($cateogry)
->whereBelongsTo($section)
->get();
// 指定一個自定義的關系:
$query->whereBelongsTo($author, 'author')
感謝 @danjharrin 提供
更多建議: