Laravel 有限制的急切加载
Laravel eager loading with limit
我有两个表,比如 "users" 和 "users_actions",其中 "users_actions" 与用户有一个 hasMany 关系:
用户
id | name | surname | email...
动作
id | id_action | id_user | log | created_at
型号Users.php
class Users {
public function action()
{
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc');
}
}
现在,我想检索所有用户的列表,他们的最后一个操作 .
我看到了 Users::with('action')->get();
只需获取关系的第一个结果,就可以轻松地为我提供最后一个操作:
foreach ($users as $user) {
echo $user->action[0]->description;
}
但我当然想避免这种情况,只为每个用户选择最后一个操作。
我试过使用约束,比如
Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')
->limit(1);
}])
->get();
但这给了我一个错误的结果,因为 Laravel 执行了这个查询:
SELECT * FROM users_actions WHERE user_id IN (1,2,3,4,5)
ORDER BY created_at
LIMIT 1
这当然是错误的。是否有可能在不使用 Eloquent 对每条记录执行查询的情况下获得此信息?
我是否犯了一些我没有看到的明显错误?我对使用 Eloquent 还很陌生,有时关系让我很烦恼。
编辑:
出于代表性目的的一部分,我还需要此功能来在关系中搜索,例如我想搜索 LAST ACTION = 'something' 的用户
我尝试使用
$actions->whereHas('action', function($query) {
$query->where('id_action', 1);
});
但是这给了我所有有一个动作 = 1 的用户,因为它是一个日志,每个人都通过了那一步。
编辑 2:
感谢@berkayk 看起来我解决了问题的第一部分,但我仍然无法在关系中搜索。
Actions::whereHas('latestAction', function($query) {
$query->where('id_action', 1);
});
仍然没有执行正确的查询,它生成如下内容:
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
我需要获取最新动作为1
的记录
Laravel 使用 take() 函数而不是 Limit
试试下面的代码,希望它对你有用
Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')->take(1);
}])->get();
或者简单地向您的关系添加一个 take 方法,如下所示
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc')->take(1);
我认为您要求的解决方案已在此处解释 http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
在用户模型中定义这个关系,
public function latestAction()
{
return $this->hasOne('Action')->latest();
}
并通过
得到结果
User::with('latestAction')->get();
如果您想轻松获得最新的 hasMany
相关模型,我通过@berbayk 链接的解决方案很酷。
但是,它无法解决您所要求的其他部分,因为使用 where
子句查询此关系会导致与您已经经历过的几乎相同的结果 - 所有行都将是returned,实际上只有 latest
不是最新的(但最新匹配 where 约束)。
现在开始:
简单的方法 - 获取所有并过滤集合:
User::has('actions')->with('latestAction')->get()->filter(function ($user) {
return $user->latestAction->id_action == 1;
});
或困难的方法 - 在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();
通过比较性能选择这些解决方案之一 - 第一个将 return 所有行并过滤可能的大集合 。
后者将 运行 带有嵌套子查询 (where('id', function () {..}
) 的子查询 (whereHas
),因此这两种方式都可能会很慢大 table.
让我们稍微更改一下@berkayk 的代码。
在 Users
模型中定义此关系,
public function latestAction()
{
return $this->hasOne('Action')->latest();
}
和
Users::with(['latestAction' => function ($query) {
$query->where('id_action', 1);
}])->get();
要为每个用户加载最新的相关数据,您可以使用自连接方法获取它 table 类似
select u.*, a.*
from users u
join actions a on u.id = a.user_id
left join actions a1 on a.user_id = a1.user_id
and a.created_at < a1.created_at
where a1.user_id is null
a.id_action = 1 // id_action filter on related latest record
要通过查询生成器方式完成,您可以将其写为
DB::table('users as u')
->select('u.*', 'a.*')
->join('actions as a', 'u.id', '=', 'a.user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('a.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('a.created_at < a1.created_at'));
})
->whereNull('a1.user_id')
->where('aid_action', 1) // id_action filter on related latest record
->get();
要渴望获得用户的最新关系,您可以将其定义为模型上的 hasOne
关系,例如
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class User extends Model
{
public function latest_action()
{
return $this->hasOne(\App\Models\Action::class, 'user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('actions.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('actions.created_at < a1.created_at'));
})->whereNull('a1.user_id')
->select('actions.*');
}
}
不需要依赖子查询,只需在 whereHas
中应用常规过滤器
User::with('latest_action')
->whereHas('latest_action', function ($query) {
$query->where('id_action', 1);
})
->get();
- Laravel - Get the last entry of each UID type
- Laravel Eloquent group by most recent record
我为此创建了一个包:https://github.com/staudenmeir/eloquent-eager-limit
在父模型和相关模型中使用 HasEagerLimit
特征。
class User extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
class Action extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
然后简单地链接 ->limit(1)
调用您的预加载查询(您似乎已经这样做了),您将获得每个用户的最新操作。
我有两个表,比如 "users" 和 "users_actions",其中 "users_actions" 与用户有一个 hasMany 关系:
用户
id | name | surname | email...
动作
id | id_action | id_user | log | created_at
型号Users.php
class Users {
public function action()
{
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc');
}
}
现在,我想检索所有用户的列表,他们的最后一个操作 .
我看到了 Users::with('action')->get();
只需获取关系的第一个结果,就可以轻松地为我提供最后一个操作:
foreach ($users as $user) {
echo $user->action[0]->description;
}
但我当然想避免这种情况,只为每个用户选择最后一个操作。
我试过使用约束,比如
Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')
->limit(1);
}])
->get();
但这给了我一个错误的结果,因为 Laravel 执行了这个查询:
SELECT * FROM users_actions WHERE user_id IN (1,2,3,4,5)
ORDER BY created_at
LIMIT 1
这当然是错误的。是否有可能在不使用 Eloquent 对每条记录执行查询的情况下获得此信息? 我是否犯了一些我没有看到的明显错误?我对使用 Eloquent 还很陌生,有时关系让我很烦恼。
编辑:
出于代表性目的的一部分,我还需要此功能来在关系中搜索,例如我想搜索 LAST ACTION = 'something' 的用户
我尝试使用
$actions->whereHas('action', function($query) {
$query->where('id_action', 1);
});
但是这给了我所有有一个动作 = 1 的用户,因为它是一个日志,每个人都通过了那一步。
编辑 2:
感谢@berkayk 看起来我解决了问题的第一部分,但我仍然无法在关系中搜索。
Actions::whereHas('latestAction', function($query) {
$query->where('id_action', 1);
});
仍然没有执行正确的查询,它生成如下内容:
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
我需要获取最新动作为1
的记录Laravel 使用 take() 函数而不是 Limit
试试下面的代码,希望它对你有用
Users::with(['action' => function ($query) {
$query->orderBy('created_at', 'desc')->take(1);
}])->get();
或者简单地向您的关系添加一个 take 方法,如下所示
return $this->hasMany('Action', 'user_id')->orderBy('created_at', 'desc')->take(1);
我认为您要求的解决方案已在此处解释 http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/
在用户模型中定义这个关系,
public function latestAction()
{
return $this->hasOne('Action')->latest();
}
并通过
得到结果User::with('latestAction')->get();
如果您想轻松获得最新的 hasMany
相关模型,我通过@berbayk 链接的解决方案很酷。
但是,它无法解决您所要求的其他部分,因为使用 where
子句查询此关系会导致与您已经经历过的几乎相同的结果 - 所有行都将是returned,实际上只有 latest
不是最新的(但最新匹配 where 约束)。
现在开始:
简单的方法 - 获取所有并过滤集合:
User::has('actions')->with('latestAction')->get()->filter(function ($user) {
return $user->latestAction->id_action == 1;
});
或困难的方法 - 在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();
通过比较性能选择这些解决方案之一 - 第一个将 return 所有行并过滤可能的大集合 。
后者将 运行 带有嵌套子查询 (where('id', function () {..}
) 的子查询 (whereHas
),因此这两种方式都可能会很慢大 table.
让我们稍微更改一下@berkayk 的代码。
在 Users
模型中定义此关系,
public function latestAction()
{
return $this->hasOne('Action')->latest();
}
和
Users::with(['latestAction' => function ($query) {
$query->where('id_action', 1);
}])->get();
要为每个用户加载最新的相关数据,您可以使用自连接方法获取它 table 类似
select u.*, a.*
from users u
join actions a on u.id = a.user_id
left join actions a1 on a.user_id = a1.user_id
and a.created_at < a1.created_at
where a1.user_id is null
a.id_action = 1 // id_action filter on related latest record
要通过查询生成器方式完成,您可以将其写为
DB::table('users as u')
->select('u.*', 'a.*')
->join('actions as a', 'u.id', '=', 'a.user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('a.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('a.created_at < a1.created_at'));
})
->whereNull('a1.user_id')
->where('aid_action', 1) // id_action filter on related latest record
->get();
要渴望获得用户的最新关系,您可以将其定义为模型上的 hasOne
关系,例如
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class User extends Model
{
public function latest_action()
{
return $this->hasOne(\App\Models\Action::class, 'user_id')
->leftJoin('actions as a1', function ($join) {
$join->on('actions.user_id', '=', 'a1.user_id')
->whereRaw(DB::raw('actions.created_at < a1.created_at'));
})->whereNull('a1.user_id')
->select('actions.*');
}
}
不需要依赖子查询,只需在 whereHas
中应用常规过滤器User::with('latest_action')
->whereHas('latest_action', function ($query) {
$query->where('id_action', 1);
})
->get();
- Laravel - Get the last entry of each UID type
- Laravel Eloquent group by most recent record
我为此创建了一个包:https://github.com/staudenmeir/eloquent-eager-limit
在父模型和相关模型中使用 HasEagerLimit
特征。
class User extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
class Action extends Model {
use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
}
然后简单地链接 ->limit(1)
调用您的预加载查询(您似乎已经这样做了),您将获得每个用户的最新操作。