Laravel 5渴望加载限制

时间:2015-11-09 10:27:51

标签: php laravel-5 eloquent greatest-n-per-group eager-loading

我有两张桌子,比如"用户"和" users_actions",其中" users_actions"与用户有很多关系:

用户

id | name | surname | email...

操作

id | id_action | id_user | log | created_at

Model 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,有时也会让我感到困难。

修改:

来自代表性目的的一部分,我还需要此功能用于在关系中搜索,例如我想搜索最后行动='某事物的用户

我尝试使用

$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的记录

6 个答案:

答案 0 :(得分:11)

我认为您要求的解决方案在此处解释http://softonsofa.com/tweaking-eloquent-relations-how-to-get-latest-related-model/

在用户模型中定义此关系,

public function latestAction()
{
  return $this->hasOne('Action')->latest();
}

获得结果
User::with('latestAction')->get();

答案 1 :(得分:7)

如果您希望轻松获得最新的hasMany相关模型,那么@berbayk链接的解决方案很酷。

然而,它无法解决您要求的其他部分,因为使用where子句查询此关系将导致与您已经经历的几乎相同 - 所有行都将被返回,事实上只有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();

通过比较效果选择其中一种解决方案 - 第一种将返回所有行并过滤可能的大集合

后者将使用嵌套子查询(whereHas)运行子查询(where('id', function () {..}),因此大表上的两种方式都可能会很慢。

答案 2 :(得分:2)

我为此创建了一个包: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)应用于您的关系,您将获得每位用户的最新操作。

答案 3 :(得分:0)

Laravel使用take()函数而不是限制

尝试以下代码我希望它能正常使用

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);

答案 4 :(得分:0)

让我们改变@ berkayk的代码。

Users模型

中定义此关系
public function latestAction()
{
    return $this->hasOne('Action')->latest();
}

Users::with(['latestAction' => function ($query) {
    $query->where('id_action', 1);
}])->get();

答案 5 :(得分:0)

要为每个用户加载最新的相关数据,您可以使用操作表上的自联接方法获取它,例如

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();