Rebase a query builder after filtering an Eloquent Collection

Sometimes after retrieving models from database, you will need to apply some filtering from your PHP code and then execute a query on the models remaining in the collection.

Let say you want to retrieve all 3nth logged in users:

$loggedInUsers = User::where('logged_in', true)->get();

$nthUsers = $loggedInUsers->nth(3);

And then you want to update them:

$nthUsers->update(/* ... */);

The below code won't work, because the nth method returns an instance of Illuminate\Support\Collection and not an instance of Illuminate\Database\Eloquent\Collection.

This is the case for all the methods which are not overriden from the Eloquent Collection class.

But still, there is a solution, the Eloquent Collection includes a method called toQuery which "rebuild" the query builder, using the first model instance of the collection, and the keys of each models still present in the collection.

if ($nthUsers->isNotEmpty()) {
    $nthUsers->toQuery()->update(/* ... */);
}

Here is a simplified version of the toQuery method

public function toQuery()
{
    $model = $this->first();

    return $model
        ->newModelQuery()
        ->whereKey($this->modelKeys());
}

The original method secure the way you use it, so you can't call it if:

  • The collection is empty (because $this->first() will fail)
  • The collection contains models from multiple tables (which could be the case when retrieve polymorphic relationships)

Comments

Be the first to post a comment!

Add a comment

Preview