Create direct single-model relationships for the newest or oldest item in a one-to-many relationship using ofMany() helpers.
When an entity owns multiple child records (such as a Customer having many Orders, or a User having many Logins), accessing only the most recent child often leads to loading all children and sorting in memory.
Laravel provides latestOfMany() and oldestOfMany() to define efficient HasOne relationships backed by a SQL subquery.
Defining the Relationship
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Customer extends Model
{
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
public function latestOrder(): HasOne
{
return $this->hasOne(Order::class)->latestOfMany();
}
public function oldestOrder(): HasOne
{
return $this->hasOne(Order::class)->oldestOfMany();
}
}
Custom Aggregate Sorting
You can also specify custom column sorting (such as the highest order amount):
public function largestOrder(): HasOne
{
return $this->hasOne(Order::class)->ofMany('total_amount', 'max');
}
Eager Loading and Query Usage
Because latestOrder is a standard HasOne relationship, it can be eager-loaded without fetching unnecessary historical records:
$customers = Customer::with('latestOrder')->get();
foreach ($customers as $customer) {
echo $customer->latestOrder?->total_amount;
}
Summary
- Eliminates N+1 memory overhead by querying single record subsets in SQL.
- Works as a standard
HasOnerelationship that supports full eager loading. - Supports custom aggregates via
ofMany('column', 'max')orofMany('column', 'min').
Related Tips
View all tips →Scope Pivot Table Queries with Closures in wherePivot() in Laravel 13.26
Laravel 13.26 allows wherePivot() and orWherePivot() to accept closures, enabling direct reuse of local scopes defined on custom Pivot models.
Stream Large Datasets: cursor() vs lazy() in Eloquent
cursor() hydrates single model instances sequentially using database cursors; lazy() streams records in chunks backed by LazyCollection.