Laravel Eloquent查询:获取用户已完成的全部项目
解决思路:查询用户完成所有任务的项目
你的当前scopeIsCompleted存在两个核心问题:
- 直接在作用域里调用了
get(),这会立即执行查询并返回集合,破坏了Eloquent的链式查询能力 - 逻辑上只筛选出了用户至少完成一个任务的项目,而非完成了该项目所有任务的项目
要实现目标,我们需要对比「项目总任务数」和「用户在该项目下完成的任务数」,当两者相等时,就说明用户完成了这个项目的所有任务。
步骤1:确保模型关联正确
先确认你的模型关联已经正确定义:
Project模型:
public function tasks() { return $this->hasMany(Task::class); } public function userTasks() { return $this->hasManyThrough(UserTask::class, Task::class); }
Task模型:
public function userTasks() { return $this->hasMany(UserTask::class); }
步骤2:编写正确的查询作用域
修改你的scopeIsCompleted,用子查询或聚合函数来实现对比逻辑:
public function scopeIsCompleted($query) { $userId = Auth::user()->id; // 1. 计算每个项目的总任务数 $totalTasksSubquery = Task::selectRaw('count(*)') ->whereColumn('project_id', 'projects.id'); // 2. 计算该用户在项目下完成的任务数 $completedTasksSubquery = UserTask::selectRaw('count(distinct task_id)') ->join('tasks', 'tasks.id', '=', 'user_tasks.task_id') ->whereColumn('tasks.project_id', 'projects.id') ->where('user_tasks.user_id', $userId); // 3. 筛选总任务数等于完成任务数的项目 return $query->select('projects.*') ->selectSub($totalTasksSubquery, 'total_tasks') ->selectSub($completedTasksSubquery, 'completed_tasks') ->havingRaw('total_tasks = completed_tasks') ->havingRaw('total_tasks > 0'); // 排除没有任务的项目(可选) }
步骤3:使用这个作用域
现在你可以链式调用这个作用域,比如:
$completedProjects = Project::isCompleted()->get();
补充说明
- 用
count(distinct task_id)是为了避免同一个任务被用户重复标记完成的情况(如果你的业务允许重复标记的话) havingRaw用来对比聚合后的数值,因为where无法直接使用聚合列- 不要在作用域里调用
get(),保持作用域只负责构建查询,让调用方决定何时执行查询
内容的提问来源于stack exchange,提问作者Skintest
相关产品推荐
相关产品推荐

