Dam*_*rsy 7 php eager-loading greatest-n-per-group eloquent laravel-5
我有两个表,比如说"users"和"users_actions",其中"users_actions"与用户有一个hasMany关系:
用户
id | name | surname | email...
Run Code Online (Sandbox Code Playgroud)
行动
id | id_action | id_user | log | created_at
Run Code Online (Sandbox Code Playgroud)
Model Users.php
class Users {
public function action()
{
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc');
}
}
Run Code Online (Sandbox Code Playgroud)
现在,我想检索列表所有的用户与他们的最后一个动作.
我看到 Users::with('action')->get();
只通过简单地获取关系的第一个结果就可以轻松地给我最后一个动作:
foreach ($users as $user) {
echo $user->action[0]->description;
}
Run Code Online (Sandbox Code Playgroud)
但我当然希望避免这种情况,只为每个用户选择最后一个动作.
我尝试使用约束,比如
Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')
->limit(1);
}])
->get();
Run Code Online (Sandbox Code Playgroud)
但是由于Laravel执行此查询,这给了我一个不正确的结果:
SELECT * FROM users_actions WHERE user_id IN (1,2,3,4,5)
ORDER BY created_at
LIMIT 1
Run Code Online (Sandbox Code Playgroud)
这当然是错的.有没有可能在没有使用Eloquent对每条记录执行查询的情况下获得此功能?我是否犯了一些我没有看到的明显错误?我很擅长使用Eloquent,有时甚至是关系让我烦恼.
编辑:
作为代表性目的的一部分,我还需要此功能来搜索关系内部,例如我想搜索最后行动='某事'的用户
我试过用
$actions->whereHas('action', function($query) {
$query->where('id_action', 1);
});
Run Code Online (Sandbox Code Playgroud)
但这给了我所有有一个动作= 1的用户,因为它是一个日志,每个人都通过了这一步.
感谢@berkayk看起来我解决了问题的第一部分,但我仍然无法在关系中搜索.
Actions::whereHas('latestAction', function($query) {
$query->where('id_action', 1);
});
Run Code Online (Sandbox Code Playgroud)
仍然没有执行正确的查询,它生成类似于:
select * from `users` where
(select count(*)
from `users_action`
where `users_action`.`user_id` = `users`.`id`
and `id_action` in ('1')
) >= 1
order by `created_at` desc
Run Code Online (Sandbox Code Playgroud)
我需要获取最新动作为1 的记录
ber*_*ayk 11
我认为你要求的解决方案在这里解释http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
在用户模型中定义此关系,
public function latestAction()
{
return $this->hasOne('Action')->latest();
}
Run Code Online (Sandbox Code Playgroud)
并获得结果
User::with('latestAction')->get();
Run Code Online (Sandbox Code Playgroud)
Jon*_*eir 10
我为此创建了一个包:https : //github.com/staudenmeir/eloquent-eager-limit
HasEagerLimit在父模型和相关模型中使用特征。
class User extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
class Action extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
Run Code Online (Sandbox Code Playgroud)
然后您可以申请->limit(1)您的关系,您将获得每个用户的最新操作。
如果你想轻松获得最新的hasMany相关模型,我的@berbayk链接的解决方案很酷.
但是,它无法解决你所要求的其他部分,因为查询这个与where子句的关系会导致你已经经历的几乎相同 - 所有的行都会被返回,但latest实际上并不是最新的(但最新匹配where约束).
所以你走了:
简单的方法 - 获取所有和过滤器集合:
User::has('actions')->with('latestAction')->get()->filter(function ($user) {
return $user->latestAction->id_action == 1;
});
Run Code Online (Sandbox Code Playgroud)
或者很难 - 在sql中做(假设是MySQL):
User::whereHas('actions', function ($q) {
// where id = (..subquery..)
$q->where('id', function ($q) {
$q->from('actions as sub')
->selectRaw('max(id)')
->whereRaw('actions.user_id = sub.user_id');
})->where('id_action', 1);
})->with('latestAction')->get();
Run Code Online (Sandbox Code Playgroud)
通过比较性能选择其中一个解决方案 - 第一个将返回所有行并过滤可能的大集合.
后者将使用嵌套的子查询()运行子whereHas查询(where('id', function () {..}),因此在大表上两种方式都可能很慢.