Skip to content

[5.6] eloquent withDefault() and N queries problem #25214

Closed
@shadoWalker89

Description

@shadoWalker89
  • Laravel Version: 5.6.33
  • PHP Version: 7.1
  • Database Driver & Version: mysql 5.6.31

Short version of the description

When we have a model A with a relation b() that uses the withDefault() and the B model has a relation c()
when we try and do

$rows = A::with('b.c');
foreach($rows as $row)
{
    $row->b->c;
}

For every b instance that is a default model and not a row in the database, when we do ->c it will trigger a database query.

So if we have 50 $rows, where only 2 acutally have a b related row
the eager loading will work only on the 2 existing b rows and we will have 48 other queries trying to fetch the c relation of the b default models

Long version of the description and details:

The withDefault() eloquent method was added to make reduce the conditionals and make the code better.

But when i have a model like this

class Complaint extends Model
{
    public function status()
    {
        return $this->belongsTo(Status::class)->withDefault();
    }
}

And then when i want to display a grid of my complaints by doing something like this

class ComplaintsController extends Controller
{

    public function index(Request $request)
    {
        $complaints = Complaint::with(['status.translations'])->get();
        
        
        return view('modules.complaints.index', compact('complaints'));
    }
}
<table>
...
<tr>
...
<!-- the ->name will get the name from the translation that matches the application locale -->
<td>{{ $complaint->status->name }}</td>
...
</tr>
...
</table>

The problem being is that for every Status that is a default model and does not really exist when i try to fetch the name from the translation a new query will be exectued.

So to avoid having N queries problem now i'm doing this in my blade template

<table>
...
<tr>
...
<!-- the ->name will get the name from the translation that matches the application locale -->
<td>{{ $complaint->status->exists ? $complaint->status->name : null }}</td>
...
</tr>
...
</table>

Which is basically the thing that the default models were addedd for (writing this conditions)

This has been brought up before and closed without a real solution from the laravel framework core team #24241

@jonnsl added a suggestion for solving this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions