hasOneOfMany: latest(), oldest(), ofMany()
Pluck a single 'winner' out of a hasMany relationship — the latest order, the top score.
You have a hasMany, but 90% of the time you only want one of them: a user's most recent login, an account's latest invoice, a product's highest bid. Before Laravel 8 people wrote sub-queries by hand. Now there is a first-class helper.
The three flavours
latestOfMany()— newest by the model's primary key (or a column you name).oldestOfMany()— oldest.ofMany('column', 'max'|'min')— the winner by any aggregate, with optional extra constraints.
// User.php
public function latestOrder()
{
return $this->hasOne(Order::class)->latestOfMany();
}
// $user->latestOrder; // a single Order, eager-loadableBecause it is still a hasOne, you can with('latestOrder') it and Laravel builds an efficient correlated sub-query instead of loading every order into memory.
Example
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class User extends Model
{
// Newest order by created_at (falls back to PK ordering).
public function latestOrder(): HasOne
{
return $this->hasOne(Order::class)->latestOfMany();
}
// The single most expensive PAID order, ties broken by id
// so the result is stable across runs.
public function biggestPaidOrder(): HasOne
{
return $this->hasOne(Order::class)->ofMany(
['total_cents' => 'max', 'id' => 'max'],
fn ($query) => $query->where('status', 'paid'),
);
}
}
// --- Usage: still eager-loadable, still one query per relation ---
$users = User::query()
->with(['latestOrder', 'biggestPaidOrder'])
->get();
foreach ($users as $user) {
// No N+1 — each relation resolved via a correlated subquery.
echo $user->latestOrder?->reference;
echo $user->biggestPaidOrder?->total_cents;
}When to use it
- A user model uses hasOne()->latestOfMany() to always fetch the user's most recently created order without a separate query or PHP sorting.
- A product model uses hasOne()->ofMany('price','min') to expose the cheapest variant as a direct relationship attribute.
- A customer support system uses hasOne()->ofMany() with a custom closure to find each user's oldest unresolved ticket in a single relationship.
More examples
Latest and oldest of many
latestOfMany() and oldestOfMany() narrow a hasMany to a single record using an efficient subquery instead of loading the full collection.
class User extends Model
{
// Most recent order
public function latestOrder(): HasOne
{
return $this->hasOne(Order::class)->latestOfMany();
}
// Very first order
public function oldestOrder(): HasOne
{
return $this->hasOne(Order::class)->oldestOfMany();
}
}Min/max of many with ofMany()
ofMany('price','min') creates a HasOne that automatically selects the variant with the lowest price using a SQL MIN subquery.
class Product extends Model
{
// The cheapest variant
public function cheapestVariant(): HasOne
{
return $this->hasOne(Variant::class)->ofMany('price', 'min');
}
// The most expensive variant
public function premiumVariant(): HasOne
{
return $this->hasOne(Variant::class)->ofMany('price', 'max');
}
}Custom ofMany with additional constraints
Passes a closure to ofMany() to add WHERE constraints so only orders matching a specific status are considered when finding the oldest.
class User extends Model
{
public function oldestActiveOrder(): HasOne
{
return $this->hasOne(Order::class)
->ofMany(
['created_at' => 'min'],
fn($query) => $query->where('status', 'active')
);
}
}
Discussion