laravel雄辩地按关系排序

时间:2016-11-28 05:27:19

标签: php mysql laravel laravel-5

我有3个模特

  • 用户
  • 频道
  • 回复

模型关系

  • userbelongsToMany('App\Channel');
  • channelhasMany('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');
}

编辑:

query result

4 个答案:

答案 0 :(得分:10)

据我所知,急切加载 with方法运行第二个查询。这就是为什么您无法通过急切加载 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以获取频道顺序列表。

对于频道等条件(可能有也可能没有回复),只需使用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 stackoverflow question详细说明此问题。

因此,如果您添加groupBy方法,则必须使用orderByRaw方法,并且应按以下方式实施。

return $this->channels()
                ->leftJoin('replies', 'channels.id', '=', 'replies.channel_id')
                ->groupBy(['channels.id'])
                ->orderByRaw('max(replies.created_at) desc')
                ->get();

答案 1 :(得分:1)

首先,如果您遵循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');
}

答案 2 :(得分:0)

在您的频道类中,您需要创建此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');
}

答案 3 :(得分:0)

如果您有hasOne()关系,则可以通过以下操作对所有记录进行排序:

$results = Channel::with('reply')
                   ->join('replies', 'channels.replay_id', '=', 'replies.id')
                   ->orderBy('replies.created_at', 'desc')
                   ->paginate(10);

这会按照最新的答复对所有channels记录进行排序(假设每个频道只有一个答复。)情况并非如此,但是有人可能正在寻找类似的东西(就像我以前一样。)< / p>