I am trying to create a HasOne relationship out of HasMany relationship but with added conditions.
These are my models
Document.php
class Document extends Model
{
public function approvals(): HasMany
{
return $this->hasMany(Approval::class);
}
public function nextApproval(): HasOne
{
return $this->hasOne(Approval::class)->where('status', 'pending')->orderBy('created_at', 'asc');
}
}
Approval.php
class Approval extends Model
{
public function documents(): BelongsTo
{
return $this->belongsTo(Document::class);
}
}
Sample Data of Approval
id | document_id | position_id | status |
---|---|---|---|
1 | 1 | 1 | pending |
2 | 1 | 2 | pending |
Now in documents index page, I only want to display documents if the logged in user’s position is in the next approval.
$documents = Document::query()
->whereHas('nextApproval', fn ($q) => $q->where('position_id', auth()->user()->position_id))
->paginate();
Based on the approval data, user with position ID of 2 should not see any documents because the next pending approval is approval with ID of 1, with position ID of 1.
Now if the first user approved the document, the status of the approval will changed to ‘done’, and the next approval should be the user with position ID of 2. So it’s kind of like dynamic HasOne relationship based on the given condition.
I tried these codes:
$this->approvals()->orderBy('created_at', 'asc')->where('status', 'pending')->one();
$this->hasOne(Approval::class)->where('status', 'pending')->oldest('created_at')->limit(1);
And for query:
$documents = Document::query()
->whereExists(function ($query) {
$query->from('approvals')
->whereColumn('approvals.document_id', 'documents.id')
->where('status', 'pending')
->where('position_id', auth()->user()->position_id)
->orderBy('created_at', 'asc');
});
->paginate();
None of these seems to work. latestOfMany
and oldestOfMany
doesn’t work either because it always gets either latest of oldest and disregards the given conditions.
If its not possible, are there any suggestions on how to achieve this?
2
Answers
the part where you are stuck is that you are applying your position filtering on the relation query, while you need to first "find" the next approval and than check it’s position value.
To do this you can use a subSelect to calculate the value you want to filter on: