Laravel Eloquentの使い方|検索から更新・削除まで実装して確認
目次
Laravel 13 + SQLite で問い合わせ対応ボードを作り、Eloquent の where・orderBy・get・first・count・exists・with・join・update・ソフトデリートの戻り値と実行SQLを1つずつ実測した記録です。
Docker 上に Laravel 13 + SQLite の環境を構築し、「問い合わせ対応ボード」機能を作りながら、頻繁に使用するメソッドを確認していきます。
各セクションは「作成したい画面」「実装コードと出力結果」「戻り値および発行 SQL」の順で構成しています。
なお、Eloquent の読み方は一般に「エロクアント」と呼ばれます。
検証した環境
すべて以下の環境における実測値です。
| 項目 | 値 |
|---|---|
| Laravel Framework | 13.31.0 |
| PHP | 8.3.33 (NTS) |
| データベース | SQLite 3.46.1 |
SQL は DB::enableQueryLog() を使用します。
テストデータ
tickets テーブルに 6 件
customers テーブルに 3 件
のデータを登録しました。
ticketsテーブルの id = 4 の問い合わせのみ customer_id が NULL(顧客未登録)の状態で用意しました。
Eloquent の設定方法
どのデータベースに、どの設定で接続するかを決められます。
大抵の設定はconfig/database.php で行いますが、.env で上書きして使用します。
今回のデモの .env に書いた DB 関連の行は次の 1 行だけです。
.env
DB_CONNECTION=sqliteconfig/database.php にはこんな感じに書かれています。
'default' => env('DB_CONNECTION', 'sqlite'),
'connections' => [
'sqlite' => [
'driver' => 'sqlite',
'url' => env('DB_URL'),
'database' => env('DB_DATABASE', database_path('database.sqlite')),
'prefix' => '',
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
'busy_timeout' => null,
'journal_mode' => null,
'synchronous' => null,
],ちなみに、反映されたかどうかは php artisan config:show で確認できます。
うまく変更が反映されない場合は、ほぼキャッシュが原因なので、コマンドを打って解決します。
php artisan config:clear接続できたかどうかは、 php artisan db:show で確認できます。
php artisan db:show
SQLite .............................................................. 3.46.1
Connection .......................................................... sqlite
Database .......................................... database/database.sqlite
Tables .................................................................. 11get() の基本的な書き方
get()は、データを複数件取得したいときに使います。
一覧画面に未対応の問い合わせを並べて表示します。
この画面のテーブル表示部分を構成するのが get() です。
条件を指定して get() を呼び出しました。
$tickets = Ticket::get();get() の戻り値は配列ではなく Collection という連想配列みたいな形式で返されます。
first() の基本的な書き方
first()は、データを1件取得したいときに使います。
ダッシュボードの「次に対応する 1 件」カードに、最も優先度が高い未対応案件を 1 件だけ表示したい場合を想定します。
ソート条件を指定して first() を呼び出しました。
$t = Ticket::query()
->where('status', 'open') //status カラムが open のもの
->orderByDesc('priority') //priority が高い順
->orderBy('id') //id が高い順
->first(); //その中から1件get() では空の Collection が返りましたが、first() では null が返ります。
この点が get() との最大の相違点であり、$t->subject のようにプロパティへアクセスする前に null チェックが必要になります!
findOrFail()
findOrFail()は、データを1件取得したいが、nullの時に HTTP 404 レスポンスを返す関数です。
存在しない ID が直接指定された場合に手動で abort(404) を書く手間が省けるため、
詳細画面などで重宝します
$t = Ticket::findOrFail($ticketId);where() の基本的な書き方
where()は、データを絞り込むときに使用します。
検索フォームでステータスと件名を指定し、一覧を絞り込みたい場合を考えます。
ステータスと件名の両方を指定して呼び出しました。
$r = Ticket::query()
->where('status', 'open')
->where('subject', 'like', '%請求書%')
->get();
echo $r->count(); // 2
echo $r->pluck('id')->implode(', '); // 2, 5SQL[1] select * from "tickets" where "status" = ? and "subject" like ? and "tickets"."deleted_at" is null
-- bindings: ["open","%請求書%"]where() を連続して記述すると、各条件は and で結合されます。
引数が 2 つの場合は = 比較になります。
Ticket::query()->where('priority', 2)->get();引数が 3 つの場合は第 2 引数が比較演算子として機能します。
Ticket::query()->where('priority', '>=', 2)->get();orderBy() の基本的な書き方
orderBy()は、並び順を変更することができます。
緊急度の高い問い合わせを一覧の最上位に表示するため、優先度の降順で並べ替えを行いました。
$r = Ticket::query()->orderByDesc('priority')->get();count() の基本的な書き方
count()は、レコードの数を「数字だけ出力する」関数です。
画面上部に「未対応 4 件」と件数を表示するため、抽出条件を指定して count() を呼び出しました。
$c = Ticket::query()->where('status', 'open')->count();
echo get_debug_type($c); // int
echo $c; // 4withCount()の基本的な書き方
withCount()は、リレーション先のレコードの数を出力する関数です。
今回は、「顧客ごとの問い合わせ件数」の集計に使いました。
$customers = Customer::query()->withCount('tickets')->get();
// A商事 tickets_count=int 2
// B工業 tickets_count=int 2
// C制作 tickets_count=int 1customer レコードに、tickets_countというカラムが追加され、件数が入っています。
exists() の基本的な書き方
exists()は、該当するレコードが1件でも存在するのかどうか、True or False で判定してくれる関数です。
今回は、「未対応案件が 1 件でも存在するかどうか」チェックするために使います。
$e = Ticket::query()->where('status', 'open')->where('priority', 3)->exists();
echo get_debug_type($e); // bool
var_export($e); // truecount()やfirst()でも同じようなことは出来ますが、
count() は対象をすべてカウントし、first() は 1 行分のモデルオブジェクトをインスタンス化するため、
exists() は、実データをメモリに読み出すことなく真偽値のみを取得することから、軽量な選択肢となります。
doesntExist() メソッドは、exists() と逆で、「存在しなければ」 True になります。
has() の基本的な書き方
has()は、リレーション先のデータを持つレコードだけに絞り込む関数です。
「問い合わせが 1 件でもある顧客」を取得したい場合に使います。
Customer::query()->has('tickets')->pluck('name');
// ["A商事","B工業","C制作"]条件を付けて絞り込むときは whereHas() を使います。
Customer::query()
->whereHas('tickets', fn ($q) => $q->where('priority', 3)) //緊急の問い合わせを持つ顧客
->pluck('name');
// ["A商事"]発行される SQL は exists のサブクエリでした。
select "name" from "customers" where exists (select * from "tickets" where "customers"."id" = "tickets"."customer_id" and "priority" = ? and "tickets"."deleted_at" is null)顧客を全件取得してから PHP 側でループする必要がありません。
with() の基本的な書き方
with()は、リレーション先のデータをまとめて取得する関数です。
一覧の各行に顧客名も表示したい場合に使います。
$tickets = Ticket::query()
->with('customer') //Ticketモデルに書いたリレーションを取得
->get();必要な列だけに絞る場合は、リレーション名の後ろにカラムを指定します。
Ticket::query()->with(['customer:id,name'])->get();Eagar Load と言って、最初に一気に取得するので N+1 問題を回避することができます。
load()の基本的な書き方
with()と似ていますが、取得済みの Collection に、後から足すときは load() を使います。
$tickets = Ticket::query()->orderBy('id')->get();
$tickets->load('customer');これらはサーバーの処理速度を保つために必要な関数です。
Laravel の表示速度をあげたいとき、実装コストに対して効果が高いものはコレ!
join() の基本的な書き方
join()は、別テーブルの列を 1 本の SQL でまとめて取得する関数です。
SQL のjoinみたいな感じです。
顧客名で絞り込んだり、顧客名で並べ替えたりする場合に使います。
$r = Ticket::query()
->leftJoin('customers', 'customers.id', '=', 'tickets.customer_id')
->select('tickets.*', 'customers.name as customer_name')
->orderBy('tickets.id')
->get();withとの違いは、カラムがTicketレコードに追加されます。
リレーションではなく、カラムが横並びになります。
特段理由がない限り、JOIN ではなく、with を使ってリレーションを使うのを僕はおすすめします。
select() を書かないと id が顧客側の ID に上書きされる
カラムが重複すると、上書きされてしまうため、select()を使って回避できます。
->select('tickets.id', 'customers.id as customer_id')カラムが重複した場合は上記のように、asと命名を変更すると両方使えます。
join() では顧客未登録のレコードが落ちる
customer_id が NULL の ID=4 は、join()(INNER JOIN)だと結合先がないため結果から消えます。
全件を出したいときは leftJoin() を使います。
update() の基本的な書き方
update()は、レコードを更新する関数です。
一覧の「対応済みにする」操作で、ステータスを更新します。
$ticket = Ticket::findOrFail(1);
$result = $ticket->update(['status' => 'done']);上記の書き方は、Ticket モデルに一括代入を設定しているときに可能です。
#[Fillable(['customer_id', 'subject', 'body', 'priority','status'])] //statusを追加しないとUPDATEは使えないまたは、preventSilentlyDiscardingAttributes() を有効にすると、例外に変わります。
Model::preventSilentlyDiscardingAttributes();
Ticket::findOrFail(1)->update(['status' => 'done']);save()でも出来る
外部から変更させたくない列は、プロパティに代入して save() します。
$ticket = Ticket::findOrFail(1);
$ticket->status = 'done';
$ticket->save();delete() の基本的な書き方
delete()は、レコードを削除する関数です。
ソフトデリート機能を使うと、論理削除といって、DB にデータは残しつつ、サービス上ではデータを表示しない状態にできます。
Laravel では、Ticket モデルに use SoftDeletes; を書くことで簡単に導入できます。
deleted_at へ日時が入ることでソフトデリートとなります。
$ticket = Ticket::findOrFail(2);
$deleted = $ticket->delete();削除後に取得し直すと、ID=2 は一覧から消えています。
削除済みも含めて表示したい場合は withTrashed()、削除済みだけを数えたい場合は onlyTrashed() を使います。
戻すときは restore() で、deleted_at に null が再代入されます。
Ticket::withTrashed()->findOrFail(2)->restore(); // true本当に消す時は forceDelete() を使います。
Ticket::findOrFail(6)->forceDelete(); // true以上、よく使う Eloquent の関数について、実装しながらまとめてみました。




