ホーム>Laravel>How to Use Laravel Eloquent: Querying, Updating and Deleting, Verified in Code
Laravel

How to Use Laravel Eloquent: Querying, Updating and Deleting, Verified in Code

Thank you for your continued support.
This article contains advertisements that help fund our operations.

A hands-on tour of Eloquent on Laravel 13 + SQLite, building a support ticket board and checking the return value and the actual SQL for where, orderBy, get, first, count, exists, with, join, update and soft deletes.

I set up Laravel 13 + SQLite in Docker and built a "support ticket board" while going through the Eloquent methods I reach for most often.

Each section follows the same order: the screen I wanted to build, the code and its output, then the return value and the SQL that was actually issued.

By the way, Eloquent is usually pronounced the way it reads in English, "eloquent".

The Environment I Tested On

Every result below was measured in this environment.

ItemValue
Laravel Framework13.31.0
PHP8.3.33 (NTS)
DatabaseSQLite 3.46.1

The SQL comes from DB::enableQueryLog().

Test Data

6 rows in the tickets table

3 rows in the customers table

Only id = 4 in tickets has a NULL customer_id, meaning no customer is linked to it.

How to Configure Eloquent

This is where you decide which database to connect to, and with which settings.

Most of it lives in config/database.php, and you override the values from .env.

The demo has exactly one DB-related line in .env.

.env

DB_CONNECTION=sqlite

config/database.php looks like this.

'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,
    ],

You can check the resolved values with php artisan config:show.

When a change doesn't seem to apply, it is almost always the config cache, and this command clears it.

php artisan config:clear

To confirm the connection itself, use php artisan db:show.

php artisan db:show

  SQLite .............................................................. 3.46.1
  Connection .......................................................... sqlite
  Database .......................................... database/database.sqlite
  Tables .................................................................. 11

The Basics of get()

get() is what you use when you want to fetch multiple rows.

It renders the list of open tickets on the index screen.

laravel eloquent 01 board

The table part of this screen is built by get().

I called get() with a condition.

$tickets = Ticket::get();

get() does not return an array. It returns a Collection, which behaves much like an associative array.

The Basics of first()

first() is what you use when you want a single row.

Think of the "next ticket to handle" card on the dashboard, which shows only the highest priority open ticket.

I called first() with sorting applied.

$t = Ticket::query()
    ->where('status', 'open') //rows whose status column is open
    ->orderByDesc('priority') //highest priority first
    ->orderBy('id') //then by id
    ->first(); //take one of them

get() returns an empty Collection when nothing matches, but first() returns null.

That is the biggest difference from get(), so you need a null check before touching a property such as $t->subject!

findOrFail()

findOrFail() also fetches a single row, but it returns an HTTP 404 response instead of null.

It saves you from writing abort(404) by hand when someone passes an ID that doesn't exist, so

it is handy on detail screens

$t = Ticket::findOrFail($ticketId);

The Basics of where()

where() is what you use to narrow down rows.

Say the search form lets you filter the list by status and subject.

laravel eloquent 04 open priority

I passed both the status and the subject.

$r = Ticket::query()
    ->where('status', 'open')
    ->where('subject', 'like', '%請求書%') //"invoice" in Japanese
    ->get();

echo $r->count(); // 2
echo $r->pluck('id')->implode(', '); // 2, 5
SQL[1] select * from "tickets" where "status" = ? and "subject" like ? and "tickets"."deleted_at" is null
       -- bindings: ["open","%請求書%"]

Chaining where() combines the conditions with and.

With two arguments, it compares with =.

Ticket::query()->where('priority', 2)->get();

With three arguments, the second one becomes the comparison operator.

Ticket::query()->where('priority', '>=', 2)->get();

The Basics of orderBy()

orderBy() changes the order of the results.

To float urgent tickets to the top of the list, I sorted by priority in descending order.

$r = Ticket::query()->orderByDesc('priority')->get();

laravel eloquent 06 customer order

The Basics of count()

count() returns just the number of rows.

To show "4 open" at the top of the screen, I called count() with the filter applied.

$c = Ticket::query()->where('status', 'open')->count();

echo get_debug_type($c); // int
echo $c;                 // 4

The Basics of withCount()

withCount() returns the number of related rows.

I used it to count the tickets per customer.

$customers = Customer::query()->withCount('tickets')->get();
// A商事 tickets_count=int 2
// B工業 tickets_count=int 2
// C制作 tickets_count=int 1

Each customer record gains a tickets_count column holding the count.

The Basics of exists()

exists() tells you whether at least one matching row exists, as a true or false value.

I used it to check whether there is any open ticket at all.

$e = Ticket::query()->where('status', 'open')->where('priority', 3)->exists();

echo get_debug_type($e);  // bool
var_export($e);           // true

You can do something similar with count() or first(), but

count() counts every matching row and first() instantiates a model object for one row, while

exists() only reads back a boolean without pulling the data into memory, which makes it the lighter option.

doesntExist() is the opposite of exists(): it returns true when nothing matches.

The Basics of has()

has() narrows the query down to rows that have related records.

Use it when you want "customers with at least one ticket".

Customer::query()->has('tickets')->pluck('name');
// ["A商事","B工業","C制作"]

To add a condition to the relation, use whereHas().

Customer::query()
    ->whereHas('tickets', fn ($q) => $q->where('priority', 3)) //customers with an urgent ticket
    ->pluck('name');
// ["A商事"]

The SQL it issued was an exists subquery.

select "name" from "customers" where exists (select * from "tickets" where "customers"."id" = "tickets"."customer_id" and "priority" = ? and "tickets"."deleted_at" is null)

There is no need to fetch every customer and loop over them in PHP.

The Basics of with()

with() fetches the related records together with the main query.

Use it when each row of the list also has to show the customer name.

$tickets = Ticket::query()
    ->with('customer') //loads the relation defined on the Ticket model
    ->get();

To limit it to the columns you need, list them after the relation name.

Ticket::query()->with(['customer:id,name'])->get();

This is called eager loading. Because everything is fetched up front, it avoids the N+1 problem.

The Basics of load()

load() is the same idea as with(), but for a Collection you have already fetched.

$tickets = Ticket::query()->orderBy('id')->get();
$tickets->load('customer');

Both of these matter for keeping server response times down.

Laravel の表示速度をあげたいとき、実装コストに対して効果が高いものはコレ!

The Basics of join()

join() pulls columns from another table in a single SQL statement.

It is essentially the join you already know from SQL.

Use it when you want to filter or sort by the customer name.

$r = Ticket::query()
    ->leftJoin('customers', 'customers.id', '=', 'tickets.customer_id')
    ->select('tickets.*', 'customers.name as customer_name')
    ->orderBy('tickets.id')
    ->get();

The difference from with is that the column is attached to the Ticket record itself.

You get flat columns side by side instead of a relation.

Unless you have a specific reason, I would recommend using with and the relation rather than a JOIN.

Without select(), the id gets overwritten by the customer's ID

When column names collide, one overwrites the other, and select() is how you avoid it.

->select('tickets.id', 'customers.id as customer_id')

Renaming the duplicated column with as, as shown above, lets you use both.

join() drops tickets with no customer

id = 4 has a NULL customer_id, so join() (INNER JOIN) has nothing to match and the row disappears from the result.

Use leftJoin() when you want every row.

The Basics of update()

update() updates a record.

It backs the "mark as done" action on the list screen.

$ticket = Ticket::findOrFail(1);
$result = $ticket->update(['status' => 'done']);

This only works when mass assignment is configured on the Ticket model.

#[Fillable(['customer_id', 'subject', 'body', 'priority','status'])] //UPDATE won't work unless status is listed

Alternatively, enabling preventSilentlyDiscardingAttributes() turns the silent failure into an exception.

Model::preventSilentlyDiscardingAttributes();

Ticket::findOrFail(1)->update(['status' => 'done']);

save() works too

For columns you don't want writable from outside, assign the property and call save().

$ticket = Ticket::findOrFail(1);
$ticket->status = 'done';
$ticket->save();

The Basics of delete()

delete() deletes a record.

With soft deletes, the row stays in the database but disappears from the application, which is what "logical deletion" means.

In Laravel you enable it by adding use SoftDeletes; to the Ticket model.

The row is soft deleted once a timestamp lands in deleted_at.

$ticket = Ticket::findOrFail(2);
$deleted = $ticket->delete();

laravel eloquent 07 after delete

Fetch the list again after deleting, and id = 2 is gone.

To include deleted rows use withTrashed(), and to count only the deleted ones use onlyTrashed().

laravel eloquent 08 with trashed

To bring a row back, restore() puts null into deleted_at again.

Ticket::withTrashed()->findOrFail(2)->restore(); // true

When you really want the row gone, use forceDelete().

Ticket::findOrFail(6)->forceDelete(); // true

That covers the Eloquent methods I use most, written up while actually building with them.

Please Provide Feedback
We would appreciate your feedback on this article. Feel free to leave a comment on any relevant YouTube video or reach out through the contact form. Thank you!