如何在 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 中按多对多关系的数据透视表的字段进行排序的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 Eloquent 中具有多对多关系的外部表中的列聚合进行分组?