To achieve the NULLS LAST ordering in Eloquent without resorting to raw SQL for the entire query, you can use the orderByRaw method in combination with your subquery. Here's how you can modify your scope to include NULLS LAST:
public function scopeOrderByFrequentFlyerNumber(Builder $builder, string $sortDir = 'desc'): Builder
{
return $builder->orderByRaw(
'(SELECT frequent_flyer_number FROM frequent_flyer_programmes WHERE user_id = users.user_id ORDER BY frequent_flyer_number ' . $sortDir . ' NULLS LAST LIMIT 1) ' . $sortDir
);
}
Explanation:
-
Subquery with
NULLS LAST: The subquery selects thefrequent_flyer_numberfrom thefrequent_flyer_programmestable where theuser_idmatches theusers.user_id. It orders the results byfrequent_flyer_numberin the specified direction ($sortDir) and appendsNULLS LASTto ensure null values are sorted last. -
Outer Query Ordering: The outer query uses
orderByRawto apply the subquery's result as the ordering criterion, ensuring that theNULLS LASTbehavior is preserved.
This approach keeps the use of raw SQL to a minimum and integrates it seamlessly with Eloquent's query builder.
Here is the complete scope function:
public function scopeOrderByFrequentFlyerNumber(Builder $builder, string $sortDir = 'desc'): Builder
{
return $builder->orderByRaw(
'(SELECT frequent_flyer_number FROM frequent_flyer_programmes WHERE user_id = users.user_id ORDER BY frequent_flyer_number ' . $sortDir . ' NULLS LAST LIMIT 1) ' . $sortDir
);
}
This should give you the desired ordering with NULLS LAST while still leveraging Eloquent's query builder for the rest of your query.