Reputation: 2757
In my Database, I have:
tops
Tableposts
Tabletops_has_posts
Table.When I retrieve a top on my tops
table I also retrieve the posts
about the top.
But what if I want to retrieve these posts in a certain order?
So I added a range
field in my pivot table tops_has_posts
and I tried to order by the result using Eloquent but it didn't work.
I try this :
$top->articles()->whereHas('articles', function($q) {
$q->orderBy('range', 'ASC');
})->get()->toArray();
And this :
$top->articles()->orderBy('range', 'ASC')->get()->toArray();
Both were desperate attempts.
Thank you in advance.
Upvotes: 37
Views: 49254
Reputation: 3
you can use this:
public function keywords() {
return $this->morphToMany(\App\Models\Keyword::class, "keywordable")->withPivot('order');
}
public function getKeywordOrderAttribute() {
return $this->keywords()->first()->pivot->order;
}
and append keyword attribiute to model after geting and use sortby
$courses->get()->append('keyword_order')->sortBy('keyword_order');
Upvotes: 0
Reputation: 13534
In Laravel 5.4 I have the following relation that works fine in Set
model which belongsToMany
of Job
model:
public function jobs()
{
return $this->belongsToMany(Job::class, 'eqtype_jobs')
->withPivot(['created_at','updated_at','id'])
->orderBy('pivot_created_at','desc');
}
The above relation returns all jobs
that the specified Set
has been joined ordered by the pivot table's (eqtype_jobs
) field created_at
DESC.
The SQL printout of $set->jobs()->paginate(20)
Looks like the following:
select
`jobs`.*, `eqtype_jobs`.`set_id` as `pivot_set_id`,
`eqtype_jobs`.`job_id` as `pivot_job_id`,
`eqtype_jobs`.`created_at` as `pivot_created_at`,
`eqtype_jobs`.`updated_at` as `pivot_updated_at`,
`eqtype_jobs`.`id` as `pivot_id`
from `jobs`
inner join `eqtype_jobs` on `jobs`.`id` = `eqtype_jobs`.`job_id`
where `eqtype_jobs`.`set_id` = 56
order by `pivot_created_at` desc
limit 20
offset 0
Upvotes: 20
Reputation: 2508
If you print out the SQL query of belongsToMany
relationship, you will find that the column names of pivot tables are using the pivot_
prefix as a new alias.
For example, created_at
, updated_at
in pivot table have got pivot_created_at
, pivot_updated_at
aliases. So the orderBy
method should use these aliases instead.
Here is an example of how you can do that.
class User {
...
public function posts(): BelongsToMany {
return $this->belongsToMany(
Post::class,
'post_user',
'user_id',
'post_id')
->withTimestamps()
->latest('pivot_created_at');
}
...
}
You can use orderBy
instead of using latest
method if you prefer. In the above example, post_user
is pivot table, and you can see that the column name for ordering is now pivot_created_at
or pivot_updated_at
.
Upvotes: 4
Reputation: 1124
For Laravel 8.17.2+ you can use ::orderByPivot()
.
https://github.com/laravel/framework/releases/tag/v8.17.2
Upvotes: 37
Reputation: 435
In Laravel 5.6+ (not sure about older versions) it's convenient to use this:
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range')->orderBy('tops_has_posts.range');
}
In this case, whenever you will call articles
, they will be sorted automaticaly by range
property.
Upvotes: 26
Reputation: 482
in your blade try this:
$top->articles()->orderBy('pivot_range','asc')->get();
Upvotes: 4
Reputation: 81167
There are 2 ways - one with specifying the table.field
, other using Eloquent alias pivot_field
if you use withPivot('field')
:
// if you use withPivot
public function articles()
{
return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range');
}
// then: (with not whereHas)
$top = Top::with(['articles' => function ($q) {
$q->orderBy('pivot_range', 'asc');
}])->first(); // or get() or whatever
This will work, because Eloquent aliases all fields provided in withPivot
as pivot_field_name
.
Now, generic solution:
$top = Top::with(['articles' => function ($q) {
$q->orderBy('tops_has_posts.range', 'asc');
}])->first(); // or get() or whatever
// or:
$top = Top::first();
$articles = $top->articles()->orderBy('tops_has_posts.range', 'asc')->get();
This will order the related query.
Note: Don't make your life hard with naming things this way. posts
are not necessarily articles
, I would use either one or the other name, unless there is really need for this.
Upvotes: 48