Laravel按关联计数排序

87

我试图获取最流行的黑客马拉松,需要按照各自黑客马拉松的partipants->count()排序。如果难以理解,对不起。

我的数据库格式如下:

hackathons
    id
    name
    ...

hackathon_user
    hackathon_id
    user_id

users
    id
    name

Hackathon 模型是:

class Hackathon extends \Eloquent {
    protected $fillable = ['name', 'begins', 'ends', 'description'];

    protected $table = 'hackathons';

    public function owner()
    {
        return $this->belongsToMany('User', 'hackathon_owner');
    }

    public function participants()
    {
        return $this->belongsToMany('User');
    }

    public function type()
    {
        return $this->belongsToMany('Type');
    }
}

HackathonParticipant 可以定义为:

class HackathonParticipant extends \Eloquent {

    protected $fillable = ['hackathon_id', 'user_id'];

    protected $table = 'hackathon_user';

    public function user()
    {
        return $this->belongsTo('User', 'user_id');
    }

    public function hackathon()
    {
        return $this->belongsTo('Hackathon', 'hackathon_id');
    }
}

我尝试过 Hackathon::orderBy(HackathonParticipant::find($this->id)->count(), 'DESC')->take(5)->get());,但我感觉可能犯了大错误(可能是 $this->id),因为它根本不起作用。

我该如何获取与最高数量的相关 hackathonParticipants 基于最受欢迎的 hackathons?

7个回答

268

这对我在 Laravel 5.3 中使用你的示例是有效的:

Hackathon::withCount('participants')->orderBy('participants_count', 'desc')->paginate(10); 

这样查询结果就会按照顺序排列,分页效果也更好。


24
这个答案比被采纳的那个要好得多。 - tremby
1
Laravel 5.2 中新增了 withCount() 方法。 - Marek Skiba
6
提供信息,如果你的关联名称有大写字母,例如 maleParticipants,则查询应该像这样 withCount('maleParticipants')->orderBy('male_participants', 'desc') - Syamsoul Azrien
1
谢谢。在Laravel Backpack中,我能够使用$this->crud->query->withCount('contactTags')->orderBy('contact_tags_count', 'desc') - Ryan
使用 ->orderByDesc('participants_count') 可以使订单查询更加优雅。 - Muh Ghazali Akbar
显示剩余2条评论

49

45

编辑:如果使用 Laravel 5.2 或更高版本,请使用 kJamesy 的答案。这可能会表现得更好,因为它不需要将所有参与者和黑客马拉松加载到内存中,只需对分页的黑客马拉松和这些黑客马拉松的参与者计数。

您应该能够使用 Collection 的 sortBy() 和 count() 方法相当容易地完成此操作。

$hackathons = Hackathon::with('participants')->get()->sortBy(function($hackathon)
{
    return $hackathon->participants->count();
});

1
如何更改升序/降序? - FooBar
4
只需将第三个参数设为true即可。sortBy(Closure $callback, $options = SORT_REGULAR, bool $descending = false) - user1669496
46
按照排序方式无法在数据库层面上实现这一点...因此,在分页时它不能正确工作!但当返回整个数据集时很有用。 - Sabrina Leggett
2
非常糟糕的性能。 因为它将获取查询中每行的计数。所有计数都应该在一个查询中进行,以获得快速结果。 - Majed DH
如果你需要使用集合而不是查询构建器,这将完成工作。要按降序排序,请使用sortByDesc()方法。 - Stefan Pavlov
显示剩余2条评论

15

我遇到了类似的问题,使用sortBy()方法不适合因为需要分页,就像Sabrina Gelbart在之前的解决方案中评论的那样。所以我使用了db raw,这是简化后的查询:

Tag::select( 
array(
    '*',
    DB::raw('(SELECT count(*) FROM link_tag WHERE tag_id = id) as count_links')) 
)->with('links')->orderBy('count_links','desc')->paginate(5);   

2
我发现的早期Laravel版本的最佳解决方案!(新版本可以使用withCount)我不确定是什么问题,但我必须这样做:tag_id = tag.id 而不是 tag_id = id...另外,你甚至可以更简化!不必再使用select,只需要用 orderByRaw('(SELECT count(*) FROM link_tag WHERE tag_id = tag.id) as count_links') DESC')->paginate(5)。对于那些想知道的人,除非你在加载关系,否则不需要使用 ->with - Sabrina Leggett
而且..如果需要的话,您也可以在范围内执行此操作... Tag::orderByLinkCount()->paginate(5),然后在您的标签模型中创建scopeOrderByLinkCount,并添加您的orderByRaw语句。 - Sabrina Leggett
我认为应该是 WHERE tag_id = tag.id 而不是 WHERE tag_id = id - Yahya Uddin

6
你也可以使用join操作符。正如Sabrina所说,你不能在数据库级别使用sortby。
$hackathons = Hackathon::leftJoin('hackathon_user','hackathon.id','=','hackathon_user.hackathon_id')
           ->selectRaw('hackathon.*, count(hackathon_user.hackathon_id) AS `count`')
           ->groupBy('hackathon.id')
           ->orderBy('count','DESC')
           ->paginate(5);

但是这段代码会从数据库中获取所有记录,因此您需要手动进行分页。

       $hackathons = Hackathon::leftJoin('hackathon_user','hackathon.id','=','hackathon_user.hackathon_id')
           ->selectRaw('hackathon.*, count(hackathon_user.hackathon_id) AS `count`')
           ->groupBy('hackathon.id')
           ->orderBy('count','DESC')
           ->skip(0)->take(5)->get();

引用自:https://dev59.com/goTba4cB1Zd3GeqP30vh#26384024

在IT技术中,“RESTful”是一种Web服务的架构风格,它使用HTTP协议进行通信。RESTful API是基于RESTful架构的API,它通过HTTP请求来访问和操作数据资源。RESTful API的设计原则包括客户端-服务器、无状态、可缓存、分层系统和统一接口。这些原则使得RESTful API易于扩展、灵活和可维护。


6

我需要对多个计数进行求和,然后使用它来设置排序。在 Laravel 8 中,以下查询对我有效。

$posts = Post::withCount('comments','likes')->orderBy(\DB::raw('comments_count + likes_count'),'DESC')->get();

4
您可以使用以下代码。
Hackathon::withCount('participants')->orderByDesc("participants_count")->paginate(15)

或者如果您想使用单个方法实现ASC/DESC排序

Hackathon::withCount('participants')->orderBy("participants_count", 'asc')->paginate(15)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接