【问题标题】:How can i convert this sql query in a eloquent laravel command?如何在雄辩的 laravel 命令中转换此 sql 查询?
【发布时间】:2020-06-01 22:05:09
【问题描述】:

在 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 ids

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

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

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

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

【问题讨论】:

  • 嗨乔纳森,你试过约束急切加载吗?类似Viagem::with('status', function($query){ return $query->orderBy('created_at', 'desc')->first(); }。你可以使用toSql查看生成的sql查询。
  • 嗨@bagus,我们快到了。我使用这个命令:``` $viagens = Viagem::with(['status' => function ($query) { $query->orderBy('created_at', 'desc')->first(); }] )->get(); ``` 但它不像 sql 命令那样工作并且显示错误的结果。

标签: laravel eloquent


【解决方案1】:

在 laravel 中有多种查询方式。我创建了一个test project 供您尝试。要点是:

1。雄辩的 ORM

Eloquent ORM 是 Laravel 的魔法,它有 some limitations in eager loading - 我只是在考虑你的问题几个小时时才遇到它。它不能很好地与first()last() 以及受约束的急切加载闭包中的其他一些函数配合使用。

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

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

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

但是,如果您只想检索viagem.idstatus_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 并根据外键将它们映射到内存中来工作。您可以观察到将get 替换为toSql 只会给您第一个查询。请启用Query Logging 以查看第二个查询。

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() 有时会有所帮助,但我真的不想提。

【讨论】:

  • 感谢@Bagus 的精彩回答。我将其设置为最佳答案。我在问题中添加了一个新部分(临时解决方案)。见。
【解决方案2】:

我不确定 viagens 和 viagem 代表什么,但我认为其中一个关系必须是 belongsToMany() 而另一个是 hasMany()。

那么在你正确设置关系之后,你就可以像这样使用 Eloquent 了:

$status_id = Viagem::with('status')->orderBy('created_at', 'desc')->first()->pluck('status_id');

【讨论】:

  • 嗨@Siba,它是多对多的,就像documentation 所说的反向映射。
  • @JhonatanMorais 你试过上面的查询吗?我认为它应该工作。
  • 嗨@Siba,但它不起作用,结果如下:SQLSTATE[42703]:未定义列:7 错误:列“status_id”不存在第 1 行:选择“status_id” " from "viagens" ^ (SQL: select "status_id" from "viagens") 我已经用更多细节改进了这个问题。
【解决方案3】:

试试这个。

$status_id = Viagem::join('status','viagem.id','status_viagem.viagem_id')
             ->select('viagem.id','status_viagem.status_id')
             ->get();

【讨论】:

  • 嗨@Ryan,你的建议也没有用:结果如下:SQLSTATE[42P01]: Undefined table: 7 ERRO: missing entry to table "viagem" at statement FROM LINE 1: ..."status_id" from "viagens" 内部连接 ​​"status" on "viagem"."... ^ (SQL: select "viagem"."id", "status_viagem"."status_id" from "viagens"内连接 "status" on "viagem"."id" = "status_viagem"."viagem_id")
猜你喜欢
  • 2020-08-06
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 2016-09-14
  • 1970-01-01
  • 2018-01-26
  • 2021-07-23
相关资源
最近更新 更多