5 Eloquent features you might have missed

Aug 20, 2022

Eloquent is one of the most powerful features of the Laravel framework. You can use Eloquent daily for years without knowing all the features it has to offer. In this article, I want to show you five features you might have missed while using Eloquent that might make your code better.

Update every item in an Eloquent collection

Without this feature, you need to either loop through every item and update them individually or create a new query with a where-in clause. Fortunately, Laravel offers a shorthand for the second option:


// 🚫 Makes one query per user
foreach ($users as $user) {
    $user->update([
        'subscription' => 'lifetime'
    ]);
}

// 🚫 Only one query, but a little lengthy
User::query()
    ->whereIn('id', $users->pluck('id'))
    ->update([
        'subscription' => 'lifetime'
     ]);
     
// ✅ Only one query, easy to read
$users->toQuery()
      ->update([
        'subscription' => 'lifetime'
      ]);

Warning: keep in mind that this won't trigger the updated/updating events.

Default relationships in models

When fetching a model relationship, you often have to check if the relationship exists. This can lead to multiple null checks in your code, making it less readable. To fix this, there is a feature that allows you to define a default model that will be returned if the given relationship is null.

class Comment extends Model
{
    public function author()
    {
        return $this->belongsTo(User::class)->withDefault([
            // 👇 Assign attributes to the default model
            'name' => 'Guest'
        ]);
    }
}
///... 

// 😬 Instead of doing this
$authorName = $comment->author ? $comment->author->name : 'Guest';

// 👌 You can do this
$authorName = $comment->author->name;

Default models work on belongsTo, hasOne, hasOneThrough, and morphOne relationships.

Use a query builder in where-in clause

Sometimes, in our queries, we need to filter our results by the result of a second query. For example, if we want to get all the posts that belong to promoted users. Instead of fetching the id of all promoted users and using that to filter the post, we can pass the query itself as a parameter to the posts where-in clause:

// 🥱 Before 
$promotedUsers = User::select('id')->where('promoted', true);
Post::query()
    ->whereIn('user_id', $promotedUsers->pluck('id'))
    ->get();
// Generates two queries 🐌
// select id from users where promoted = 1
// select * from posts where user_id in (1, ..., ...)

// 🤩 After
Post::query()
    ->whereIn('user_id', User::select('id')->where('promoted', true))
    ->get();
// Generates one query ⚡
// select * from posts where user_id in
// (select id from users where promoted = 1)

Query time casting

Casts are a feature of Laravel that allows us to convert a column to a defined object. One of the most used cast are datetime casts, that convert a date column to a Carbon object. These casts are defined on the model class, but what happens if we want to cast a generated column? You can do that with query time casting.

$users = User::select([
    'users.*',
    'last_posted_at' => Post::selectRaw('MAX(created_at)')
                            ->whereColumn('user_id', 'users.id')
])->withCasts([
    'last_posted_at' => 'datetime'
])->get();

Now we can use the last_posted_at column as a Carbon instance.

Check if a model changed

Have you ever needed to know if a model changed since it was retrieved from the database? What if you wanted to know a specific column was changed? You can know that by using the wasChanged method on your model. You can even check by one or more columns.

$user = Post::create([
    'title' => 'Signed URLs with Laravel',
    'author' => 'Cosme Escobedo',
    'likes' => 0,
]);

$user->author = 'Taylor Otwell';
$user->save();

$user->wasChanged(); // true
$user->wasChanged(['author']); // true
$user->wasChanged(['author', 'likes']); // true
$user->wasChanged(['title']); // false
$user->wasChanged(['title', 'author']); // true

And that is it, those are 5 Eloquent features you might have missed. If you liked this article, follow me on Twitter, I post tips and other Laravel content there.