代码之家  ›  专栏  ›  技术社区  ›  Jaroslav Klimčík

从关系中获取最后一个where is条件

  •  1
  • Jaroslav Klimčík  · 技术社区  · 6 年前

    我有两张一对多的桌子。我的任务很简单,我需要从关系表中每个父项的最后一个应用条件。

    第一个表是状态:

    | id | partner_id | status | created_at          |
    |----|------------|--------|---------------------|
    | 1  | 1          | 1      | 2018-01-01 00:00:00 |
    | 2  | 1          | 5      | 2018-02-01 00:00:00 |
    | 3  | 2          | 1      | 2018-01-01 00:00:00 |
    

    关系是

    public function partner()
    {
        return $this->belongsTo(Partner::class, 'partner_id');
    }
    

    其次是合作伙伴:

    | id | name      |
    |----|-----------|
    | 1  | partner_1 |
    | 2  | partner_2 |
    

    关系是:

    public function status()
    {
        return $this->hasMany(Status::class, 'partner_id')->orderBy('id', 'desc');
    }
    

    假设我想让所有合作伙伴的最后状态是1。我试过这个:

    Partner::with('status')
        ->whereHas('status', function ($query) use ($status) {
              $query->where('status', $status);
           })
        ->get();
    

    结果是:

    "select * from `partners` where exists (select * from `status` where `partners`.`id` = `status`.`partner_id` and `status` = 1)"
    

    但有了它,我也会得到搭档1,即使他的最后地位是5,但在历史上,他有地位1。

    我创建了sql查询来获得这个结果,但我认为要完成这样一个简单的任务,这是相当复杂的。而且我认为一定有一些简单的拉拉夫尔方法:

    select p.id, ps.status, ps.newest_status
    from partners as p
    INNER JOIN (SELECT a.created_at AS newest_status, a.partner_id, a.status
                  FROM status as a
                    INNER JOIN (
                      SELECT max(created_at) as newest_status, partner_id
                    FROM status
                    GROUP BY partner_id
                  ) as b ON a.partner_id = b.partner_id AND a.created_at = b.newest_status
    ) ps ON p.id = ps.partner_id and ps.status = 1
    GROUP BY p.id
    

    我的目标是只得到搭档2,因为只有他有最后的状态1。

    2 回复  |  直到 6 年前
        1
  •  0
  •   Vincent Mimoun-Prat Krishnan    6 年前

    一种方法是自定义relationship方法以包含一些排序(如@yrv16所建议的)

    但是如果你想提高效率的话,可以使用子查询。非常 good article by J. Reinink 解释得很好。

    简而言之,就是这样:

    class Partner extends Model {
    
        // ...
    
        public function scopeWithLastStatus($query)
        {
            $query->addSubSelect('last_status', Status::select('status')->latest());
        }
    
        // ...
    

    这将添加“virtual”属性 last_status 对你的模特来说。

    然后你可以这样做:

    Partner::withLastStatus()->get()->where('last_status', 'idle');
    
        2
  •  0
  •   Jonas Staudenmeir    6 年前

    您可以使用:

    Partner::select('partners.*')
        ->join('status', 'partners.id', 'status.partner_id')
        ->where('status.status', $status)
        ->where('status.id', function($query) {
            $query->select('id')
                ->from('status')
                ->whereColumn('partner_id', 'partners.id')
                ->orderByDesc('id')
                ->limit(1);
        })->get();