首页 > 解决方案 > 如何在雄辩的 laravel 命令中转换此 sql 查询?

问题描述

在 sql 查询中,这个突击队正是我想要的:

SELECT 
    v.id,
    ( 
        SELECT sv.status_id 
        FROM status_viagem sv 
        WHERE sv.viagem_id = v.id 
        ORDER BY sv.created_at DESC LIMIT 1 ) AS status_id
FROM viagens v

这是sql结果:

在此处输入图像描述

但我不知道如何使用 Laravel eloquent 做到这一点

基本上,一个 viagem 条目可以有很多状态,但我需要从 status_viagem 表(数据透视表)中获取每个 viagem 及其最后一个状态条目

顺便说一句 viagem/viagens 意味着旅行。

我的班级映射:

class Viagem extends Model
{
  ...
  public function status()
  {
        return $this->belongsToMany('App\Status')->withTimestamps();
  }
  ...
}


class Status extends Model
{

    public function viagens()
    {
        return $this->belongsToMany('App\Viagem')->withTimestamps();
    }
}

两个班级的 belongsToMany 都让我得到了多对多:

多对多

有人能帮我吗?谢谢

----------临时解决方案--------

谢谢大家的帮助。事实上,我找不到仅使用 eloquent 的好解决方案。

步骤 1/3 - 为了绕过这种情况,我首先执行上面的 sql 以仅获取所需 status_id 下的 viagens(最后一个 status_viagem 条目):

$viagens_ids = DB::select(
            "SELECT viagem_id FROM (
                SELECT 
                    v.id AS viagem_id,
                    ( 
                        SELECT sv.status_id 
                        FROM status_viagem sv 
                        WHERE sv.viagem_id = v.id 
                        ORDER BY sv.id DESC LIMIT 1 ) AS status_id
                FROM viagens v
            ) AS tt

            WHERE tt.status_id = {$status->id}"
        );

步骤 2/3 - 然后我使用 array_map 来组织我的 viagens id

$a = array_map(
                function($obj) { return $obj->viagem_id; }, 
                $viagens_ids
            );

步骤 3/3 - 最后我使用了 elequent whereIn 来获取我的 viagens:

 Viagem::with( 'status')->whereIn('id', $a)->get();

事实上,我已经通过一种旧的 sashion 方式解决了这个问题,但我对此并不满意,因为我希望我学会如何使用 eloquent 来解决这个问题。对我有什么不好。

标签: laraveleloquent

解决方案


laravel 中有很多查询方式。我创建了一个测试项目供您尝试。要点是:

1.雄辩的ORM

雄辩的 ORM 是 Laravel 的魔法,它在急切加载方面有一些限制- 我只是在考虑你的问题几个小时时才遇到的。它不能很好地与受约束的急切加载闭包中的first(),last()和其他一些功能配合使用。

在您的情况下,我们几乎可以修复:

   App\Models\Viagem::with(['status' => function($query){
          return $query->orderBy('pivot_created_at', 'desc');
         }])
        ->get()

它将返回整个字段ViagemStatus包括其数据透视表(status_viagem)。

但是,如果您只想检索viagem.idand status_viagem.status_id,您可以map()这样:

   App\Models\Viagem::with(['status' => function($query){
           return $query->orderBy('pivot_created_at', 'desc');
         }])
        ->get()
        ->map(function($data){
           $o = new stdClass();
           $o->id = $data->id;
           $o->status_id = $data->status->first()->id;
           return $o;
        });

请注意,上面的语句需要执行两次 sql 查询。急切加载基本上是通过查询所有第Viagem一个然后查询Status并根据外键将它们映射到内存中来工作的。您可以观察到替换gettoSql只会给您第一个查询。请启用查询日志以查看第二个查询。

2.查询生成器

Ryan Adhitama Putra 开始回答,您可以执行以下操作:

   App\Models\Viagem::join('status_viagem', 'viagens.id', '=', 'status_viagem.viagem_id')
       ->orderBy('status_viagem.created_at', 'desc')
       ->groupBy(['status_viagem.status_id', 'viagens.id'])
       ->select(['viagens.id', 'status_viagem.status_id'])
       ->get();

此查询生成器方法保证只运行一次,您可以替换get()toSql()查看生成的查询。

3. 原始查询

投掷DB::raw()有时会有所帮助,但我真的不想提及。


推荐阅读