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

时间:2013-01-13 02:04:09

标签: laravel eloquent

我一直在使用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");
    }))

5 个答案:

答案 0 :(得分:49)

不确定您是否计划迁移到Laravel 4,但这是一个根据数据透视表字段排序的雄辩示例:

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

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

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

干杯!

答案 1 :(得分:5)

我刚刚在用户指南中找到了一些内容,显然您需要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()ascendingplaycount顺序显示结果,则绝非巧合。我通过测试证实了这一点(因为我还不知道如何在单元测试中显示查询),但你可能不应该依赖这种行为。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

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

如果在连接表中有名为“order”的列,则这可能不明确。如果是这样,您需要指定 - > order_by('article_tag.order')。是的,您需要使用 - > with()来获取结果集中的该列。作为样式的问题,我会将with()从关系方法中删除,而只是返回vanilla关系对象。

答案 4 :(得分:0)

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

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