Creating a line through the invoice set invoice_id for you. belongsTo is the other direction: a line knows its invoice, an invoice knows its team. The method name is the relationship, singular. The foreign key still lives on the model that points, which is the invoice for team_id and the line for invoice_id.
Eloquent, properly · Lesson 2
Belongs-to and whereBelongsTo
Video
01 Filter by the parent you already have
whereBelongsTo() reads cleaner than where('team_id', $team->id), and it stays correct if the foreign key is ever renamed. The helper reads the key from the relationship. The controller stops naming the column. A rename of team_id on the relationship is then a change in one method, not a search through every query.
class Example
{
public function team(): BelongsTo
{
return $this->belongsTo(Team::class);
}
}
$invoices = Invoice::query()
->whereBelongsTo($team)
->where('status', 'issued')
->latest('issued_on')
->get();The same idea is the whole of the snippet on querying by a relationship. That page is the short version of this lesson. It also shows a collection of teams, which is the next case once a person can see more than one.
When you already have the parent and want its children, prefer $team->invoices() over a fresh query. The relationship is the constraint. A new query is how the team id gets forgotten. whereBelongsTo() is the tool for a query that started on Invoice. The relationship method is the tool when you started on the team. Both keep the column off the call site.
02 Point an existing invoice at a team, then save it
whereBelongsTo() filters. associate() writes. You already have the invoice, the user has chosen a team they are allowed to move it to, and you need the foreign key updated without putting team_id in $fillable. associate() sets the key on the model and does not touch the database until you save().
$invoice->team()->associate($team);
$invoice->save();Forget the save() and the in-memory invoice looks moved while the row still has the old team_id. The next request loads the old team. dissociate() clears the key the same way, and it also waits for save(). An invoice in this app should not be dissociated: the migration made team_id required, and a null team is not a draft. Use associate() when the product allows a move. Keep creating new invoices through $team->invoices()->create(), so a form still cannot pick a tenant by posting a hidden field.
whereBelongsTo() accepts a collection when the reader may see several teams. The query becomes where team_id is in that set. A global scope on Invoice still applies. whereBelongsTo() does not turn it off, and associate() does not either. If a scope already limits the team, a move that the scope forbids will not be visible on the next read, which is a reason to keep the team filter local and obvious until you mean it to be global.
A line uses the same pair of ideas, with the names swapped. invoice() is the belongsTo. $invoice->lines() is how you already created the row. whereBelongsTo($invoice) is how you find lines when the query did not start from that invoice. One parent, singular method, foreign key on the child.
Recap
You can list a team's invoices with whereBelongsTo(), and move an existing invoice with associate() plus save().
belongsTois the singular side: a line's invoice, an invoice's team.- When you already hold the team,
$team->invoices()is the constraint. - A fresh query is how the team id gets forgotten.
associate()changes the model.save()writes the row.
Check yourself · 3 questions
Question 1 of 3You have the team model and want its invoices. Which call keeps the foreign key on the relationship?