laravel 4 - 按查询加入最新结果

时间:2014-10-27 10:28:57

标签: mysql join laravel-4 max

此查询为所有“相册”分页,并为每个相册添加图片和说明。现在我想要总是得到每张专辑的最新图片。

我尝试添加第二个orderBy('pics.created_at'),但这不起作用。我想我需要某种子查询,但不知道如何。

        $query = AlbumPic::select(DB::raw('COUNT(pics.id) as picscount, 
                                           pics.url, 
                                           pics.user_id, 
                                           pics.created_at,
                                           albums.id as album_id, 
                                           albums.title, 
                                           albums.text, 
                                           users.username'))
                        ->join('albums','albums.id','=','album_pic.album_id')
                        ->join('pics','pics.id','=','album_pic.pic_id')
                        ->join('users','users.id','=','pics.user_id');

                if(!is_null($user_id)) 
                  $query->where('album_pic.user_id',$user_id);

        $albums = $query->groupBy('albums.id')
                        ->orderBy('albums.created_at','desc')
                        ->paginate(20);

修改

我犯了一个错误。我在album_pic表中没有created_at和updated_at。 所以我的'专辑' - 模型/关系现在是这样的:

public function pics()
{
    return $this->belongsToMany('Pic');
}

public function latestPic()
{
    return $this->belongsToMany('Pic')->latest('pics.created_at');
}

现在查询如下:

        $q = Album::with('pics')->with('latestPic.users');

                if(!is_null($user_id)) 
                  $q->where('albums.user_id',$user_id);

        $albums = $q->orderBy('albums.created_at','desc')
                    ->paginate(20);

这很有效。我唯一想改进的是方式,每张专辑的图片都计算在内。现在我使用with('pics')获取所有内容,然后在视图中执行count($album->pics)。如果有一种方法加载所有内容,但只计算图片,那就太好了。

3 个答案:

答案 0 :(得分:2)

您需要在子查询中获取MAX(created_at);例如,请参阅MySQL select MAX(datetime) not returning max value

但是,真的,如果你在Laravel中这样做,最好将这些全部设置为关系并利用Eloquent的力量。然后,您可以为使用->latest()返回最新图片的图片定义关系。有关示例,请参阅laravel eloquent query group by last id(使用一个表,但多个表的原理相同)。


以下是如何使用Eloquent关系进行设置:

用户模型(User.php)

class User extends Eloquent {

    public function albums()
    {
        return $this->hasMany('Album');
    }
}

相册模型(Album.php)

class Album extends Eloquent {

    public function pics()
    {
        return $this->belongsToMany('Pic');
    }

    public function latestPic()
    {
        return $this->belongsToMany('Pic')->latest('album_pic.created_at');
    }
}

由于您在相册和图片之间存在多对多关系,因此在latestPic()关系中,您必须为album_pic.created_at指定latest()字段 - 因为我们实际上对此感兴趣数据透视表中的条目顺序,而不是pics表中的条目顺序。

最后,将这一切联系起来。例如,对于id 1的用户:

$albums = User::find(1)->albums()->with('pics')->with('latestPic')->paginate(20);
    foreach($albums as $album) {

        echo('<br>Album:');
        var_dump($album->title);

        echo('All pics:');
        foreach($album->pics as $pic) {
            var_dump($pic->url);
        }

        echo('Latest pic:');
        $latestPic = $album->latestPic->first();

        if ($latestPic) {
            var_dump($latestPic->url);
        }
}

请注意,我们急切地加载picslatestPic以减少对数据库的调用次数。另请注意,访问$latestPic->url包含在if语句中,否则没有任何照片的相册会引发错误,因为$album->latestPic将返回null

正如@cedie正确指出的那样,Laravel在使用groupBy语句时并没有有效地处理分页,但在这种情况下这不应该成为一个问题。基础查询不使用groupBy,因此您应该保存以使用->paginate(20)

答案 1 :(得分:0)

尝试在您的选择查询中使用此功能:

max(pics.created_at) as created_at

而不是:

pics.created_at

所以你的代码应该是这样的:

AlbumPic::select(DB::raw('COUNT(pics.id) as picscount, 
                                           pics.url, 
                                           pics.user_id, 
                                           max(pics.created_at) as created_at,
                                           albums.id as album_id, 
                                           albums.title, 
                                           albums.text, 
                                           users.username'))

答案 2 :(得分:0)

也许你可以弄清楚如何根据你的目的进行调整......

SELECT ap.*
     , p.* 
  FROM album_pic ap 
  JOIN pics p
    ON p.id = ap.pic_id
  JOIN 
     ( SELECT ap.*
            , MAX(p.created_at) max_created_at 
         FROM album_pics ap 
         JOIN p.*
           ON p.id = ap.pic_id
     ) x
    ON x.album_id = ap.album_id
   AND x.max_created_at = p.created_at;