You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel关联表自定义orderBy排序实现方案咨询

问题与解决方案

问题描述

环境:Laravel 9.52.9,PHP 8.2.4
需求:仅展示分组名为sponsor、featured、boost的文章列表,排序优先级为featured(最高)→boost→sponsor,同时按approved_at倒序排列。目前已通过join实现查询,希望改用模型关联的方式完成排序需求。

现有代码

模型关联代码

// Post Model
public function group()
{
    return $this->belongsTo(Group::class);
}

// Group Model
public function posts()
{
    return $this->hasMany(Post::class);
}

现有Join查询代码

Post::query()
    ->select([
        'posts.id', 'posts.slug', 'posts.created_at',
        'group_id', 'title', 'thumbnail', 'approved_at',
        'groups.name as group_name'
    ])
    ->selectRaw("LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1 AS total_content_word_count")
    ->without('permissions')
    ->join('groups', 'posts.group_id', '=', 'groups.id')
    ->whereIn('groups.name', ['sponsor', 'featured', 'boost'])
    ->where('ads', true)
    ->published()
->orderByRaw("FIELD(groups.name, 'featured','boost','sponsor') ASC")
->latest('approved_at')
->paginate(30);

解决方案

方案1:结合模型关联与Join(性能优先)

通过模型的getTable()方法避免硬编码表名,同时用whereHas复用关联逻辑过滤分组,既符合模型关联设计,又保证查询性能:

Post::query()
    ->select([
        'posts.id', 'posts.slug', 'posts.created_at',
        'group_id', 'title', 'thumbnail', 'approved_at',
        'groups.name as group_name'
    ])
    ->selectRaw("LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1 AS total_content_word_count")
    ->without('permissions')
    // 利用模型获取表名,避免硬编码
    ->join(Group::getTable(), 'posts.group_id', '=', Group::getTable() . '.id')
    // 用whereHas复用关联逻辑过滤分组
    ->whereHas('group', function ($query) {
        $query->whereIn('name', ['sponsor', 'featured', 'boost']);
    })
    ->where('ads', true)
    ->published()
    // 按分组优先级排序,再按approved_at倒序
    ->orderByRaw("FIELD(" . Group::getTable() . ".name, 'featured','boost','sponsor') ASC")
    ->latest('approved_at')
    ->paginate(30);

方案2:子查询获取分组名称(无Join写法)

无需手动写Join,通过子查询从关联模型中获取分组名称,再基于该字段排序,代码更贴近模型关联的使用习惯:

Post::query()
    ->select([
        'posts.id', 'posts.slug', 'posts.created_at',
        'group_id', 'title', 'thumbnail', 'approved_at',
        // 子查询关联分组表获取名称
        Group::select('name')
            ->whereColumn('id', 'posts.group_id')
            ->limit(1)
            ->as('group_name')
    ])
    ->selectRaw("LENGTH(content) - LENGTH(REPLACE(content, ' ', '')) + 1 AS total_content_word_count")
    ->without('permissions')
    ->whereHas('group', function ($query) {
        $query->whereIn('name', ['sponsor', 'featured', 'boost']);
    })
    ->where('ads', true)
    ->published()
    // 基于子查询得到的group_name排序
    ->orderByRaw("FIELD(group_name, 'featured','boost','sponsor') ASC")
    ->latest('approved_at')
    ->paginate(30);

说明

  • 方案1性能更优,适合数据量较大的场景,Join操作能减少数据库查询次数;
  • 方案2无需手动维护Join关系,代码更简洁,但子查询在数据量大时性能略低于Join。

内容的提问来源于stack exchange,提问作者Moxley

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 22:05:13