laravel - 如何在雄辩的 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 来解决这个问题。对我有什么不好。
解决方案
laravel 中有很多查询方式。我创建了一个测试项目供您尝试。要点是:
1.雄辩的ORM
雄辩的 ORM 是 Laravel 的魔法,它在急切加载方面有一些限制- 我只是在考虑你的问题几个小时时才遇到的。它不能很好地与受约束的急切加载闭包中的first()
,last()
和其他一些功能配合使用。
在您的情况下,我们几乎可以修复:
App\Models\Viagem::with(['status' => function($query){
return $query->orderBy('pivot_created_at', 'desc');
}])
->get()
它将返回整个字段Viagem
,Status
包括其数据透视表(status_viagem
)。
但是,如果您只想检索viagem.id
and 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
并根据外键将它们映射到内存中来工作的。您可以观察到替换get
为toSql
只会给您第一个查询。请启用查询日志以查看第二个查询。
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()
有时会有所帮助,但我真的不想提及。
推荐阅读
- ios - 我应该如何使用 Firestore 通过 SwiftUI 制作动态视图
- android-fragments - 显示 AlertDialog 扩展 DialogFragment 或从 onCreateView() 中的方法
- django - django rest 密码格式无效或散列算法未知
- r - 条件面板,其中条件是输入的长度
- python - 从python调用matlab函数时出现“程序无响应”的问题如何解决?
- flutter - 颤振映射错误:类型'String'不是类型转换中'Widget'类型的子类型
- javascript - JavaScript 页面导致计算机无响应
- qt - 为视图创建多个 QSortFilterProxyModel 实例
- python - 如何使用随机生成的名称打开特定文件夹中的文件?
- r - 如何在 R 中执行 pslda 显示列表中多个元素的错误率?