laravel 雄辩的按关系排序

Posted

技术标签:

【中文标题】laravel 雄辩的按关系排序【英文标题】:laravel eloquent sort by relationship 【发布时间】:2017-04-11 18:50:27 【问题描述】:

我有 3 个模型

用户 频道 回复

模型关系

userbelongsToMany('App\Channel'); channelhasMany('App\Reply', 'channel_id', 'id')->oldest();

假设我有 2 个频道 - 通道 1 - 通道 2

channel-2channel-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 雄辩的按关系排序的主要内容,如果未能解决你的问题,请参考以下文章

雄辩的关系,模型的链接(Laravel 5.4)

Laravel - 雄辩的自我关系

Laravel - 节省雄辩的关系

Laravel 雄辩的一对一通过(级别/树)关系

Laravel,雄辩的 n:m 关系

laravel 雄辩的关系查询