laravel 雄辩的按关系排序
Posted
技术标签:
【中文标题】laravel 雄辩的按关系排序【英文标题】:laravel eloquent sort by relationship 【发布时间】:2017-04-11 18:50:27 【问题描述】:我有 3 个模型
用户 频道 回复模型关系
user
有belongsToMany('App\Channel');
channel
有hasMany('App\Reply', 'channel_id', 'id')->oldest();
假设我有 2 个频道 - 通道 1 - 通道 2
channel-2
比channel-1
有最新回复
现在,我想通过用户频道的当前回复来订购用户的频道。 就像一些聊天应用程序一样。 我怎样才能像这样订购用户的频道?
通道 2 频道 1我已经尝试了一些代码。但什么也没发生
// User Model
public function channels()
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies'])
->orderBy('replies.created_at'); // error
// also
public function channels()
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved')
->with(['replies' => function($qry)
$qry->latest();
]);
// but i did not get the expected result
编辑 另外,我试过这个。是的,我确实得到了预期的结果,但如果没有回复,它不会加载所有频道。
public function channels()
return $this->belongsToMany('App\Channel')
->withPivot('is_approved')
->join('replies', 'replies.channel_id', '=', 'channels.id')
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'ASC');
编辑:
【问题讨论】:
对于这种情况,更喜欢“join”而不是“with” 嗯,所以我不能用 eloquent 做到这一点?.. @kapil.dev 嗨,我尝试了加入方法。它按我的预期工作。但它只会返回所有有回复的频道。 使用左连接,语法:join('table_name','table_name.col1','=','table2.fkcol','left') 这个包可以帮你github.com/fico7489/laravel-eloquent-join 【参考方案1】:据我所知,eager load with
方法运行第二个查询。这就是为什么你无法通过 eager loading with
方法实现你想要的。
我认为将join
方法与关系方法结合使用是解决方案。以下解决方案经过全面测试并且运行良好。
// In User Model
public function channels()
return $this->belongsToMany('App\Channel', 'channel_user')
->withPivot('is_approved');
public function sortedChannels($orderBy)
return $this->channels()
->join('replies', 'replies.channel_id', '=', 'channel.id')
->orderBy('replies.created_at', $orderBy)
->get();
然后你可以调用$user->sortedChannels('desc')
通过回复created_at
属性获取频道排序列表。
对于像 channels 这样的情况(可能有回复也可能没有回复),只需使用leftJoin
方法即可。
public function sortedChannels($orderBy)
return $this->channels()
->leftJoin('replies', 'channel.id', '=', 'replies.channel_id')
->orderBy('replies.created_at', $orderBy)
->get();
编辑:
如果你想在查询中添加groupBy
方法,你必须特别注意你的orderBy
子句。因为在Sql性质中,Group By
子句在Order By
子句之前首先运行。在this *** question 上查看此问题的详细信息。
所以如果你添加groupBy
方法,你必须使用orderByRaw
方法,应该像下面这样实现。
return $this->channels()
->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
->groupBy(['channels.id'])
->orderByRaw('max(replies.created_at) desc')
->get();
【讨论】:
为什么在我们已经使用 eloquent 关系的情况下使用 join,我建议只使用 join 并应用 orderby 因为连接表的字段将在同一行,所以可以排序。 所以我使用了这样的连接方法。return $this->belongsToMany('App\Channel', 'channel_user') ->withPivot('is_approved') ->join('replies', 'replies.channel_id', '=', 'channels.id') ->orderBy('replies.created_at', 'DESC');
但是当特定频道没有回复时。这会导致错误。
@Hitori。我会在我自己的帖子下发表评论,因为我不想惹恼其他人。我仔细分析了你的问题。首先,您最初的问题是询问频道。但是现在您描述了您要查询回复的问题。请准确描述问题。是“查询频道”还是“查询各个频道的回复”。
@Steve.NayLinAung,我很抱歉误导。唔。把它想象成 facebook 消息。我想通过回复显示所有频道。created_at【参考方案2】:
首先,如果您关注Laravel's naming convention,则不必指定数据透视表的名称,因此您的代码看起来更简洁:
public function channels()
return $this->belongsToMany('App\Channel') ...
其次,您必须显式调用join
才能在一个查询中获得结果:
public function channels()
return $this->belongsToMany(Channel::class) // a bit more clean
->withPivot('is_approved')
->leftJoin('replies', 'replies.channel_id', '=', 'channels.id') // channels.id
->groupBy('replies.channel_id')
->orderBy('replies.created_at', 'desc');
【讨论】:
谢谢,我的代码已经是这个样子了。但它不会得到所有尚未回复的频道 @Hitori 只需使用leftJoin
然后
@Hitori 是的,leftJoin
应该可以解决问题。即使没有回复,它也会获得所有频道(看看我的编辑)
谢谢!现在可以了。我还按 channel_id 对回复进行了分组。
嗨,@Quinn Daley。我注意到我所做的查询似乎只需要频道上的第一个回复,而不是最新的回复。【参考方案3】:
在您的频道类中,您需要创建此hasOne
关系(您的频道hasMany
回复,但它是hasOne
最新回复):
public function latestReply()
return $this->hasOne(\App\Reply)->latest();
您现在可以获得所有按最新回复排序的频道,如下所示:
Channel::with('latestReply')->get()->sortByDesc('latestReply.created_at');
要获取用户按最新回复订购的所有频道,您需要该方法:
public function getChannelsOrderdByLatestReply()
return $this->channels()->with('latestReply')->get()->sortByDesc('latestReply.created_at');
channels()
由以下给出:
public function channels()
return $this->belongsToMany('App\Channel');
【讨论】:
【参考方案4】:如果您有hasOne()
关系,您可以通过以下方式对所有记录进行排序:
$results = Channel::with('reply')
->join('replies', 'channels.replay_id', '=', 'replies.id')
->orderBy('replies.created_at', 'desc')
->paginate(10);
这会按最新回复对所有 channels
记录进行排序(假设您每个频道只有一个回复。)这不是您的情况,但有人可能正在寻找类似的东西(就像我一样。)
【讨论】:
以上是关于laravel 雄辩的按关系排序的主要内容,如果未能解决你的问题,请参考以下文章