如何在 Eloquent ORM 中按多对多关系的数据透视表的字段进行排序

Posted

技术标签:

【中文标题】如何在 Eloquent ORM 中按多对多关系的数据透视表的字段进行排序【英文标题】:How to sort by a field of the pivot table of a many-to-many relationship in Eloquent ORM 【发布时间】:2012-12-27 07:42:07 【问题描述】:

我使用 Eloquent ORM 已经有一段时间了,我很了解它,但是我不能做以下事情,而 在 Fluent 中很容易做到

我的用户拥有多对多歌曲,中间表是 song_user(应该是这样)。从播放次数来看,我想获得用户的热门歌曲。当然,播放次数存储在中间表中。

我可以用 Fluent 做到这一点:

$songs = DB::table('songs')
    ->join('song_user', 'songs.id', '=', 'song_user.song_id')
    ->where('song_user.user_id', '=', $user->id)
    ->orderBy("song_user.play_count", "desc")
    ->get();

简单。但我想用 Eloquent 来做,这当然行不通:

$songs = Song::
    with(array("song_user" => function($query) use ($user) 
        $query->where("user_id", "=", $user->id)->orderBy("play_count", "desc");
    ))

【问题讨论】:

我没有设置来试用它,但是您尝试过类似$songs = $usr->songs()->pivot()->order_by('play_count','desc')->songs(); 的方法吗?问题是您是否可以在数据透视表之后“返回”songs,因为它似乎不是一个正常的模型。 这行不通,很遗憾。 好吧,您总是可以将数据透视表设为 模型 并完成它。您只需要设置额外的外键,以便在它们各自的父项存在时正确删除条目。 我目前正在这样做,但是使用这种方法,您将失去对用户歌曲的自动过滤($user->songs()),您必须记住手动执行此操作(@987654325 @)。 为什么,这就是关系的用途,而不是一对多,而是与数据透视表建立两个一对多的关系。你只需添加类似$this->has_many('song_user') 【参考方案1】:

不确定你是否打算迁移到 Laravel 4,但这里有一个 Eloquent 示例,用于按数据透视表字段排序:

public function songs() 
  return $this
    ->belongsToMany('Song')
    ->withPivot('play_count')
    ->orderBy('pivot_play_count', 'desc');

withPivot 就像雄辩的with 并将数据透视表中的play_count 字段添加到它已经包含的其他键中。所有数据透视表字段在结果中都以pivot为前缀,因此您可以直接在orderBy中引用它们。

我不知道它在 Laravel 3 中会是什么样子,但也许这会帮助你指明正确的方向。

干杯!

【讨论】:

我不知道我可以像这样将 orderBy 粘贴在模型中。你至少应该为此 +1! 请您指出文档中是否提到了枢轴字段前缀? 在 Laravel 5.2 中工作,无需为数据透视表字段添加前缀【参考方案2】:

我刚刚在用户指南中找到了一些东西,显然你需要with() 方法。

来自User-Guide:

默认情况下,只会返回数据透视表中的某些字段 (两个 id 字段和时间戳)。如果您的数据透视表包含 其他列,您也可以使用 with() 方法获取它们 :

class User extends Eloquent 
  public function roles()
  
    return $this->has_many_and_belongs_to('Role', 'user_roles')->with('column');
  

所以你可以在定义你的关系时使用类似的东西:

$this->has_many_and_belongs_to('User')->with('playcount');

示例

我只是用它来确保它有效......

class Song extends Eloquent 
    function users()
    
        return $this->has_many_and_belongs_to('User')->with('playcount');
    


class User extends Eloquent 
    function songs()
    
        return $this->has_many_and_belongs_to('Song')->with('playcount');
    


// My test method
class TestOrm extends phpUnit_Framework_TestCase 
    public function testSomethingIsTrue()
    
        foreach(User::find(3)->songs()->order_by('playcount')->get() as $song)
            echo $song->name, ': ', $song->pivot->playcount, "\n";
        echo "\n";
        foreach(User::find(3)->songs()->order_by('playcount','desc')->get() as $song)
            echo $song->name, ': ', $song->pivot->playcount, "\n";
    

输出

Jingle Bells: 5
Mary had a little lamb: 10
Soft Kitty: 20
The Catalyst: 100

The Catalyst: 100
Soft Kitty: 20
Mary had a little lamb: 10
Jingle Bells: 5

注意:如果不使用order_by(),结果会按照ascending 的顺序按playcount 排序,这绝非巧合。我通过测试确认了这一点(因为我还不知道如何在单元测试中显示查询),但您可能不应该依赖这种行为。

【讨论】:

【参考方案3】:

在 Fluent 中可用的任何方法也应该在 Eloquent 中可用。也许这就是您要找的东西?

$songs = Song->join('song_user', 'songs.id', '=', 'song_user.song_id')
->where('song_user.user_id', '=', $user->id)
->orderBy("song_user.play_count", "desc")
->get();

【讨论】:

不,我正在寻找一种更有说服力的方法,请参阅我的示例,它不起作用。 我发布的答案是用户 Eloquent。它将返回一个属于用户并通过 play_count 字段(来自连接表)排序的 Eloquent 对象(在本例中为歌曲)数组。这也可以直接从您要查找其歌曲的用户对象 $user->songs()->order_by('play_count', 'asc')->get() 中完成。您失败的示例没有任何意义:急切加载 song_user 数据透视表与创建连接不同,在这种情况下,您确实没有理由急切加载。一个简单的连接应该可以满足您的需要。【参考方案4】:

我一直在做这个(在几个版本上),只是像你一样使用关系方法。我经常在数据透视表中使用“订单”列,然后执行类似的操作。

$article->tags()->order_by( 'order')->get();

如果您在连接表中有名为“order”的列,这可能会产生歧义。如果是这样,您需要指定 ->order_by( 'article_tag.order' )。是的,您需要使用 ->with() 在结果集中获取该列。就风格而言,我会将 with() 排除在关系方法之外,而是返回原版关系对象。

【讨论】:

【参考方案5】:

在您的 Eloquent 模型中,如果您包含表名,则可以链接 orderBy 列:

return $this->belongsToMany('App\Post')->withTimestamps()->orderByDesc('posts.created_at');

【讨论】:

以上是关于如何在 Eloquent ORM 中按多对多关系的数据透视表的字段进行排序的主要内容,如果未能解决你的问题,请参考以下文章

SQLAlchemy按多对多关系排序

Django:按多对多字段订购模型

如何通过 Eloquent 中具有多对多关系的外部表中的列聚合进行分组?

Eloquent 中与分类学的多对多关系

如何在 Eloquent ORM 中从 JSON 中保存具有相关记录的模型

Laravel eloquent 检查不存在的多对多关系