Let’s say you're building an invoicing system.
Each User
can have many invoices
.
Some invoices are finalized, and others are still in draft mode.
So you decide to add this relationship:
public function draftInvoices()
{
return $this->invoices()->where('status', 'draft');
}
Perfect for retrieving draft invoices:
$user->draftInvoices()->get(); // ✅ Works
But then you try to create one:
$user->draftInvoices()->create([
'amount' => 500,
'due_date' => now()->addDays(7),
]);
And here’s the problem…
The invoice gets created with status = null
, not "draft"
Why?
Because the where('status', 'draft')
only affects queries, not creation.
Solution: withAttributes()
in Laravel 11.6+
Laravel introduced a clean fix for this common need:
public function draftInvoices()
{
return $this->invoices()->withAttributes(['status' => 'draft']);
}
Now:
- The relationship will return only draft invoices.
- And when you do
create()
, Laravel will automatically applystatus = 'draft'
.
Example:
$invoice = $user->draftInvoices()->create([
'amount' => 500,
'due_date' => now()->addDays(7),
]);
echo $invoice->status; // "draft" ✅
Want to Only Set the Default (Without Filtering)?
If you want to apply 'status' => 'draft'
only during creation, but still get all invoices in the query, do this:
public function draftInvoices()
{
return $this->invoices()->withAttributes(['status' => 'draft'], asConditions: false);
}
Summary
This feature might seem small, but it prevents bugs and saves you from repeating yourself.
Instead of manually setting attributes in every create()
call, Laravel does it for you, cleanly and within your relationship logic.
Top comments (0)