【问题标题】:Laravel eager load relationship with condition based on the parentLaravel 渴望加载与基于父级的条件的关系
【发布时间】:2021-11-12 21:35:22
【问题描述】:

我有这个查询来列出所有用户及其对应关系。

用户有很多任务
每个tasks有很多worktimes,这些worktimes可以有不同的users

即,每个任务可以由不同的用户共享,因此每个任务都有单独的工作时间。

我试过下面的代码

$users = User::select('users.id', 'users.first_name', 'users.last_name', 'users.contract_type')
    ->with([
        'tasks' => function($query) use ($from, $to){
            $query->whereBetween('date', [$from, $to])
                ->select('tasks.id', 'tasks.date');
        },
        'tasks.worktimes' => function($query) {
            //$query->where('user_id', ?)
            $query->withCount([
                    'tags as late_tag_count' => function ($subQuery) {
                        $subQuery->where('tags.id', 1);
                    },
                    'tags as early_tag_count' => function ($subQuery) {
                        $subQuery->where('tags.id', 2);
                    },
                    'tags as others_tag_count' => function ($subQuery) {
                        $subQuery->where('tags.id', 3);
                    }
                ]
            );
        }
     ])
     ->get();     

这里的关系 tasks.worktimes 还获取其他用户的工作时间(这是意料之中的),但我想将其限制为仅获取父用户的工作时间。 任何人请帮我弄清楚我应该使用什么其他条件来实现这一目标?

模型

用户.php

public function tasks()
{
    return $this->belongsToMany(Task::class, 'task_members')->withTimestamps();
}   

Task.php

public function worktimes()
{
    return $this->hasMany(Worktime::class, 'task_id');
}   

Worktime.php

public function task()
{
    return $this->belongsTo(Task::class);
} 

public function users()
{
    return $this->belongsToMany(User::class, 'task_members', 'task_id', 'user_id', 'task_id');
}

【问题讨论】:

    标签: laravel eloquent laravel-relations


    【解决方案1】:

    闭包中的 $query 仍然是 QueryBuilder 的一个实例,因此您也可以使用它进行预加载

    $users = User::select('users.id', 'users.first_name', 'users.last_name', 'users.contract_type')
                ->with([
                    'tasks' => function ($query) use ($from, $to) {
                        $query->with([
                            'worktimes' => function ($subQuery) {
                                $subQuery->withCount(
                                    [
                                        'tags as late_tag_count' => function ($subsubQuery) {
                                            $subsubQuery->where('tags.id', 1);
                                        },
                                        'tags as early_tag_count' => function ($subsubQuery) {
                                            $subsubQuery->where('tags.id', 2);
                                        },
                                        'tags as others_tag_count' => function ($subsubQuery) {
                                            $subsubQuery->where('tags.id', 3);
                                        }
                                    ]
                                );
                            }
                        ])
                            ->whereBetween('date', [$from, $to])
                            ->select('tasks.id', 'tasks.date');
                    }
                ])
                ->get();    
    

    这样,工作时间和总计数按任务分组,任务按用户分组

    编辑 1

    您必须先检索每个用户的工作时间,然后才能延迟加载它们。然而,这并不是很高效,尤其是在有很多用户的情况下

    $users = User::select('users.id', 'users.first_name', 'users.last_name', 'users.contract_type')
        ->with([
            'tasks' => function ($query) use ($from, $to) {
                $query->whereBetween('date', [$from, $to])->select('tasks.id', 'tasks.date');
            }
        ])
        ->get();
    
    foreach ($users as $user) {
        $user->tasks->load([
            'worktimes' => function ($query) use ($user) {
                $query->where('user_id', $user->id)
                    ->withCount([
                        'tags as late_tag_count' => function ($subQuery) {
                            $subQuery->where('tags.id', 1);
                        },
                        'tags as early_tag_count' => function ($subQuery) {
                            $subQuery->where('tags.id', 2);
                        },
                        'tags as others_tag_count' => function ($subQuery) {
                            $subQuery->where('tags.id', 3);
                        }
                    ]);
            }
        ]);
    }
    
    

    编辑 2

    您已经在 Worktime 模型上建立了用户 (belongsToMany) 关系。在这种情况下,在 User 模型上定义该关系的逆向效果最好。

    //User.php
    
    public function worktimes()
    {
        return $this->belongsToMany(Worktimes::class, 'task_members', 'user_id', 'task_id', 'user_id');
    }
    
    //YourController.php
    $users = User::select('users.id', 'users.first_name', 'users.last_name', 'users.contract_type')
        ->with(
            [
                'tasks' => function ($query) use ($from, $to) {
                    $query->whereBetween('date', [$from, $to])->select('tasks.id', 'tasks.date');
                },
                'worktimes' => function ($query) {
                    $query->withCount([
                        'tags as late_tag_count' => function ($subsubQuery) {
                            $subsubQuery->where('tags.id', 1);
                        },
                        'tags as early_tag_count' => function ($subsubQuery) {
                            $subsubQuery->where('tags.id', 2);
                        },
                        'tags as others_tag_count' => function ($subsubQuery) {
                            $subsubQuery->where('tags.id', 3);
                        }
                    ]);
                }
            ]
        )
        ->get();
    

    希望这会有所帮助。

    【讨论】:

    • 是的,工作时间是按任务分组的,但其他用户的工作时间也是如此。我想从任务限制其他用户的工作时间 => 工作时间关系
    • 如果是这样,那么您可以打破陈述。由于您需要一种访问user_id 的方法,因此您需要先获取用户,然后延迟加载每个用户的工作时间。不优雅,特别是如果有很多用户但仍然有效!最好的解决方案是定义用户和工作时间之间的直接关系。您已经在工作时间模型上建立了用户 (belongsToMany) 关系,您只需定义相反的关系。检查编辑
    • 我不喜欢 #edit1 解决方案,因为这会导致 n+1 问题。我目前正在获取每个任务的所有工作时间,然后根据用户 ID 进行过滤,但是如果每个任务注册了很多工作时间,这可能是一个性能问题。 Edit2 对我来说似乎很好,如果它有效,我会尝试接受答案(我认为它会)。感谢您的宝贵时间。
    猜你喜欢
    • 2019-06-19
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    • 1970-01-01
    • 2016-08-18
    • 2019-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多