As a final lesson in this section of the course about advanced relationships, I want to show you a method ofMany()
.
The situation: you have a has-many relationship (user has many projects) but you need to query only the latest project.
Only one, not all of them, just the latest. Before Laravel 8, when ofMany()
was introduced, people did that with various hacky workarounds. Now, you can define a hasOne
relationship and provide latestOfMany()
.
use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function latestProject(): HasOne { return $this->hasOne(Project::class)->latestOfMany(); }}
In the same way, you can get the oldest project using the oldestOfMany()
method instead.
use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function latestProject(): HasOne { return $this->hasOne(Project::class)->latestOfMany(); } public function oldestProject(): HasOne { return $this->hasOne(Project::class)->oldestOfMany(); }}
And last, when using ofMany()
, you can provide the column and aggregate min or max. For example, projects have a price, and you want to get the most expensive.
use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function latestProject(): HasOne { return $this->hasOne(Project::class)->latestOfMany(); } public function oldestProject(): HasOne { return $this->hasOne(Project::class)->oldestOfMany(); } public function mostExpensiveProject(): HasOne { return $this->hasOne(Project::class)->ofMany('price', 'max'); }}
Also, ofMany()
can accept multiple columns as an array with different aggregates.
use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function latestProject(): HasOne { return $this->hasOne(Project::class)->latestOfMany(); } public function oldestProject(): HasOne { return $this->hasOne(Project::class)->oldestOfMany(); } public function mostExpensiveProject(): HasOne { return $this->hasOne(Project::class)->ofMany('price', 'max'); } public function projectsWithMultiple(): HasOne { return $this->hasOne(Project::class)->ofMany(['price' => 'max', 'updated_at' => 'min']); }}
And the most complicated is grouping conditions with also adding a condition.
use Illuminate\Database\Eloquent\Builder;use Illuminate\Database\Eloquent\Relations\HasOne; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function latestProject(): HasOne { return $this->hasOne(Project::class)->latestOfMany(); } public function oldestProject(): HasOne { return $this->hasOne(Project::class)->oldestOfMany(); } public function mostExpensiveProject(): HasOne { return $this->hasOne(Project::class)->ofMany('price', 'max'); } public function projectsWithMultiple(): HasOne { return $this->hasOne(Project::class)->ofMany(['price' => 'max', 'updated_at' => 'min']); } public function projectsWithCondition(): HasOne { return $this->hasOne(Project::class) ->ofMany(['price' => 'max', 'updated_at' => 'min'], function (Builder $query) { $query->where('title', 'LIKE', '%something%'); }); }}
This relationship will get one project for the user with the where condition name like something ordered by max price, and if the price is the same, ordered by updated_at
.
The result of all of that is one record of the project without getting all the projects into the memory. These are helpful additions if you want one specific record from the relationship.