LaravelでDB(データベース)からデータを取得する際その使い道によってはEloquentモデルのインスタンスを作成する必要がない場合があります。例えばCSVのダウンロード機能などではデータの整形だけできれば十分でモデルに紐づくメソッドなどは使いません。
こういった場合Eloquentモデルのインスタンス化は余計な処理となり処理速度やメモリ使用量の面でデメリットになります。
モデルのインスタンス化を避ける方法としては最初から\DB::query()のようにモデルから離れたクエリビルダ機能を使うのも手です。しかしこの方法ではEloquentに紐づいて定義されたグローバルスコープなどの機能を活かすことができません。
そこで便利なのが、Eloquentのクエリビルダを使いながら、結果はEloquentモデルではなくstdClassのインスタンスとして取得する方法です。
具体的には、toBase()メソッドを使用します。
// 全件取得(結果は stdClass のコレクションになる) $users = User::query()->toBase()->get();
この方法を使うことで、Eloquentのクエリビルダの書きやすさをそのままに、モデルのインスタンス化を避けた軽量な処理が可能になります。
以下はCSVをchunkしながら出力する処理の例です。モデルを使うよりもメモリを使わず高速です。
public function downloadCsv()
{
$headers = [
'Content-Type' => 'text/csv',
'Content-Disposition' => 'attachment; filename="users.csv"',
];
return response()->stream(function () {
$handle = fopen('php://output', 'w');
// ヘッダー行
fputcsv($handle, ['ID', '名前', 'メールアドレス']);
User::query()
->toBase()
->orderBy('user_id')
->chunk(1000, function ($users) use ($handle) {
foreach ($users as $user) {
fputcsv($handle, [
$user->user_id,
$user->name,
$user->email,
]);
}
});
fclose($handle);
}, 200, $headers);
}
こんな感じでtoBase()を使えば、モデルを介さずにEloquentのスコープやクエリビルダの機能を活かしたまま軽量にデータを処理することができます。