Laravel 提供另一種方式來處理,可以透過定義好的 Model 的 Dynamic Property 來取得關聯,可以在 Laravel 官方網站文件的 Eloquent ORM 當中的 Relationships 看到類似以下的寫法。
class Job extends Model
{
use HasFactory;
protected $table = 'job_listings';
protected $fillable = ['title', 'salary'];
public function employer()
{
return $this->belongsTo(Employer::class);
}
}
現在我們要列出職缺文章清單,有一個很常見的做法,我們在 Controllers 把 Job 所有資料傳到 Views,再由 Views 跑迴圈把清單做出來,然而實際上每當迴圈跑到 PO 文者姓名的時候,背後都必須執行 SQL 一次,如果清單有 100 筆,那麼就必須跑 100 次,這就是所謂的 N+1 Problem,而這種調用方式稱為 Lazy Loading。
@foreach ($jobs as $job)
<a href="/jobs/{{ $job['id'] }}">
<div>{{ $job->employer->name }}</div>
<div>{{ $job['title'] }}: Pays {{ $job['salary'] }} per year.</div>
</a>
@endforeach
為了更有感覺一點,我們可以安裝 Laravel Debugbar 方便看出系統在跑的時候都執行了哪些 SQL。
composer require barryvdh/laravel-debugbar --dev
由於 Debugber 會揭漏很多資訊在網頁上,因此 Laravel 定義只有 .env 檔案中的 APP_ENV=local
與APP_DEBUG=true
才會啟動。
APP_ENV=local
APP_DEBUG=true
實際執行如下,Laravel Debugber 讓整個背後幹了什麼事,一目了然。
為了避免 N+1 Problem,可以透過 with 語法來做 Eager Loading,我們在 Controllers 讀取 Job 資料之時就要一併讀取所有的 PO 文者資料,然後才丟到 Views 去跑迴圈。
$jobs = Job::with('employer')->get();
接著一樣透過 Laravel Debugbar 來看執行結果,可以發現 SQL 的執行方式改變了,執行起來更有效率。
如果專案想禁止 Lazy Loading,app/Providers/AppServiceProvider.php 寫入以下內容。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
}
public function boot(): void
{
// 禁止 Lazy Loading
Model::preventLazyLoading();
}
}