Laravel нетерпеливая связь нагрузки с условием, основанным на родительском

#laravel #eloquent #laravel-relations

Вопрос:

У меня есть этот запрос, чтобы перечислить всех пользователей с их соответствующими отношениями.

у пользователя много задач
у каждой задачи есть много рабочих часов, и у этих рабочих часов могут быть разные пользователи

т. е. каждая задача может быть разделена разными пользователями, следовательно, у них есть индивидуальное рабочее время для каждой задачи.

я попробовал приведенный ниже код

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

здесь задачи по взаимоотношениям.время работы также позволяет получать рабочее время других пользователей (что отчасти ожидаемо), но я хочу ограничить это, чтобы получать только рабочее время родительского пользователя.
кто-нибудь, пожалуйста, помогите мне выяснить, какое еще условие я должен использовать для достижения этой цели?

Модели

User.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');
}
 

Ответ №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

У вас уже есть отношение users ( belongsToMany ) в модели рабочего времени. Определение обратной зависимости в пользовательской модели будет лучше всего работать в этой ситуации.

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

Надеюсь, это поможет.

Комментарии:

1. да, рабочее время группируется по задачам, но и с другими пользователями рабочее время тоже. я хочу ограничить время работы других пользователей отношениями task => рабочее время

2. Если это так, то вы можете прервать заявления. Поскольку вам нужен способ доступа user_id , сначала вам нужно получить пользователей, а затем лениво загрузить время работы для каждого из пользователей. Не элегантно, особенно если много пользователей, но все равно работает! Лучшим решением будет определить прямую связь между пользователями и рабочим временем. У вас уже есть отношение users ( belongsToMany )в модели рабочего времени, вам просто нужно определить обратное. Проверьте редактирование

3. Я не вхожу в решение #edit1, так как это вызывает проблему n 1. В настоящее время я выбираю все время работы для каждой задачи, а затем фильтрую на основе идентификатора пользователя, но это может быть проблемой с производительностью, если для каждой задачи зарегистрировано много времени работы. Edit2 кажется мне хорошим, я постараюсь принять ответ, если он сработает (я думаю, что так и будет). Спасибо, что уделили мне время.