如何在 Laravel 的 Eloquent ORM 中按数据透视表数据排序

问题描述 投票:0回答:7

在我的数据库中,我有:

  • tops
    桌子
  • posts
    桌子
  • tops_has_posts
    桌子。

当我检索

tops
桌子上的顶部时,我还会检索有关顶部的
posts
。 但是如果我想按特定顺序检索这些帖子怎么办? 因此,我在数据透视表中添加了一个
range
字段
tops_has_posts
,并尝试使用 Eloquent 按结果进行排序,但没有成功。

我试试这个:

$top->articles()->whereHas('articles', function($q) {
    $q->orderBy('range', 'ASC');
})->get()->toArray();

还有这个:

$top->articles()->orderBy('range', 'ASC')->get()->toArray();

两者都是绝望的尝试。

提前谢谢您。

php mysql laravel eloquent
7个回答
47
投票

有 2 种方法 - 一种是指定

table.field
,另一种是使用 Eloquent 别名
pivot_field
(如果您使用
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

这会起作用,因为 Eloquent 将

withPivot
中提供的所有字段别名为
pivot_field_name

现在,通用解决方案:

$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();

这将对相关查询进行排序。

注意: 不要用这种命名方式让你的生活变得困难。

posts
不一定是
articles
,我会使用其中之一或另一个名称,除非确实需要这样做。


34
投票

对于 Laravel 8.17.2+,您可以使用

::orderByPivot()

https://github.com/laravel/framework/releases/tag/v8.17.2


26
投票

在 Laravel 5.6+(不确定旧版本)中使用这个很方便:

public function articles()
{
  return $this->belongsToMany('Article', 'tops_has_posts')->withPivot('range')->orderBy('tops_has_posts.range');
}

在这种情况下,每当您调用

articles
时,它们都会按
range
属性自动排序。


19
投票

在 Laravel 5.4 我有以下关系,在

Set
模型中运行良好,其中
belongsToMany
模型的
Job

public function jobs()
{
    return $this->belongsToMany(Job::class, 'eqtype_jobs')
        ->withPivot(['created_at','updated_at','id'])
        ->orderBy('pivot_created_at','desc');
}

上述关系返回指定

jobs
已按数据透视表 (
Set
) 字段
eqtype_jobs
DESC 排序的所有
created_at

$set->jobs()->paginate(20)
的 SQL 打印输出如下所示:

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

4
投票

在你的刀片上试试这个:

$top->articles()->orderBy('pivot_range','asc')->get();

4
投票

如果打印出

belongsToMany
关系的SQL查询,你会发现数据透视表的列名使用了
pivot_
前缀作为新别名。

例如,数据透视表中的

created_at
updated_at
pivot_created_at
pivot_updated_at
别名。因此
orderBy
方法应该使用这些别名。

以下是如何做到这一点的示例。

class User {
    ...
    public function posts(): BelongsToMany {
        return $this->belongsToMany(
                   Post::class, 
                   'post_user', 
                   'user_id', 
                   'post_id')
               ->withTimestamps()
               ->latest('pivot_created_at');
    }
    ...
}

如果您愿意,可以使用

orderBy
而不是使用
latest
方法。在上面的示例中,
post_user
是数据透视表,您可以看到用于排序的列名称现在是
pivot_created_at
pivot_updated_at


0
投票

你可以使用这个:

    public function keywords() {
    return $this->morphToMany(\App\Models\Keyword::class, "keywordable")->withPivot('order');
}
public function getKeywordOrderAttribute() {
    return $this->keywords()->first()->pivot->order;
}

并在获取并使用 sortby 后将关键字属性附加到模型

$courses->get()->append('keyword_order')->sortBy('keyword_order');
© www.soinside.com 2019 - 2024. All rights reserved.