php Laravel 8数据库查询-如何构建相关数据的集合?

xggvc2p6  于 2023-02-28  发布在  PHP
关注(0)|答案(2)|浏览(128)

我正试图弄清楚如何形成我的数据库查询来建立一个项目集合,每个项目都有一组人员,以及他们提交的分配的工作时间和每日轮班时间。
在我目前所拥有的代码中,我选择了当月正在运行的项目。对于其中的每个项目,我选择了当月正在处理该项目的人员。然后,对于每个人员,我又进行了两次查询,以获取他们的分配日期和提交的轮班详细信息。
这确实有效,并且创建了我想要的数据结构,但是由于需要进行大量的单独数据库查询,所以速度很慢。
一定有一种更有效的方法通过子查询或其他方式来完成这件事?

$projects = Project::select([
    'projects.id',
    'projects.name',
    'projects.start',
    'projects.finish'
])
->where('projects.start', '<=', Carbon::now()->endOfMonth())
->where('projects.finish', '>=', Carbon::now()->startOfMonth())
->get();

foreach($projects as $project)
{
    $project->personnel = ProjectUser::select([
        'project_users.id',
        'project_users.user_id',
        'users.name',
        'project_users.role'
    ])
    ->join('users', 'users.id', '=', 'project_users.user_id')
    ->join('assignments', 'assignments.project_user_id', '=', 'project_users.id')
    ->where('project_users.project_id', $project->id)
    ->where('assignments.start', '<=', Carbon::now()->endOfMonth())
    ->where('assignments.finish', '>=', Carbon::now()->startOfMonth())
    ->distinct('users.name')
    ->get();

    foreach($project->personnel as $person)
    {
        $person->assignments = Assignment::select([
            'assignments.start',
            'assignments.finish',
            'assignments.travel_out',
            'assignments.travel_home'
        ])
        ->where('assignments.project_user_id', $person->project_user_id)
        ->get();

        $person->shifts = WorkShift::select([
            'work_shifts.id',
            'work_shifts.role',
            'work_shifts.date',
            'work_shifts.start',
            'work_shifts.hours',
            'work_shifts.status',
            'work_shifts.remarks'
        ])
        ->where('work_shifts.user_id', $person->user_id)
        ->where('work_shifts.project_id', $project->id)
        ->get();
    }
}

更新

我现在主要是通过使用模型关系来实现这一点。我的最后一个问题是,我可以在控制器的顶层过滤日期范围内的项目,但我还需要将该过滤器应用于分配和work_shift。
因此,我只想检索在请求的日期范围内具有分配的project_users,然后只挑选出匹配的分配和work_shift。
我目前掌握的是...

// Controller
public function load(Request $request)
{
  $projects = Project::select([
    Project::ID,
    Project::NAME,
    Project::START,
    Project::FINISH
  ])
    // Only able to filter by date range here?!?
    ->where(Project::START, '<=', Carbon::now()->endOfYear())
    ->where(Project::FINISH, '>=', Carbon::now()->startOfYear())
    ->with('project_staff')
    ->orderBy(Project::START)
    ->get();

  return response()->json([
    'projects' => $projects
  ]);
}

// Project Model
// Just want to get the staff that are assigned to the project
// between the selected date range
public function project_staff()
{
  return $this->hasMany(ProjectUser::class)
    ->select([
      ProjectUser::ID,
      ProjectUser::PROJECT_ID,
      ProjectUser::USER_ID,
      User::NAME,
      ProjectUser::ROLE
    ])
    ->whereIn(ProjectUser::STATUS, [
      ProjectUser::STATUS_RESERVED,
      ProjectUser::STATUS_ASSIGNED,
      ProjectUser::STATUS_ACCEPTED
    ])
    ->join(User::TABLE, User::ID, '=', ProjectUser::USER_ID)
    ->with([
      'assignments',
      'shifts'
    ]);
}

// Assignments Model
// Again, just want the assignments and workshifts that fall
// within the selected date range
public function assignments()
{
  return $this->hasMany(Assignment::class, 'projectUser_id')
    ->select([
      Assignment::PROJECT_USER_ID,
      Assignment::START,
      Assignment::FINISH,
      Assignment::TRAVEL_OUT,
      Assignment::TRAVEL_HOME
    ]);
}

public function shifts()
{
  return $this->hasMany(WorkShift::class, ['project_id','user_id'], ['project_id','user_id'])
    ->select([
      WorkShift::ID,
      WorkShift::USER_ID,
      WorkShift::PROJECT_ID,
      WorkShift::ROLE,
      WorkShift::DATE,
      WorkShift::START,
      WorkShift::HOURS,
      WorkShift::STATUS,
      WorkShift::REMARKS
    ]);
}

// WorkShift Model
public function projectUser()
{
    return $this->belongsTo(ProjectUser::class, ['project_id','user_id'], ['project_id','user_id']);
}
yhuiod9q

yhuiod9q1#

经过一天的学习,我找到的解决方案是在模型之间建立非常简单的关系,并在使用 * with *()调用关系查询时,在附加到关系查询的过滤器函数中实现所有额外的查询逻辑。
请参见www.example.comhttps://laravel.com/docs/8.x/eloquent-relationships#constraining-eager-loads
我的代码现在工作效率高得多,在dev-env中,在大约10 - 20ms内只对数据库进行4次调用,并以正确的形式构造结果数据,以便将整个集合作为JSON响应返回。

// Controller
public function load(Request $request)
{
  $start = Carbon::parse($request->input('start_date'));
  $end = Carbon::parse($request->input('end_date'));

  $projects = Project::select([
    Project::ID,
    Project::NAME,
    Project::START,
    Project::FINISH
  ])
    ->where(Project::START, '<=', $end)
    ->where(Project::FINISH, '>=', $start)
    ->with([
      // Filter results of the model relationship, passing $start and
      // $end dates down to propagate through to the nested layers
      'project_staff' => $this->filter_project_staff($start, $end)
    ])
    ->orderBy(Project::START)
    ->get();

    return response()->json([
      'projects' => $projects
    ]);
}

private function filter_project_staff($start, $end)
{
  return function ($query) use ($start, $end) {
    $query
      ->select([
        ProjectUser::ID,
        ProjectUser::PROJECT_ID,
        ProjectUser::USER_ID,
        User::NAME,
        ProjectUser::ROLE
      ])
      ->whereIn(ProjectUser::STATUS, [
        ProjectUser::STATUS_RESERVED,
        ProjectUser::STATUS_ASSIGNED,
        ProjectUser::STATUS_ACCEPTED
      ])
      ->join(User::TABLE, User::ID, '=', ProjectUser::USER_ID)
      ->join(Assignment::TABLE, Assignment::PROJECT_USER_ID, '=', ProjectUser::ID)
      ->where(Assignment::START, '<=', $end)
      ->where(Assignment::FINISH, '>=', $start)
      ->distinct(User::NAME)
      ->orderBy(ProjectUser::ROLE, 'desc')
      ->orderBy(User::NAME)
      ->with([
        // Filter results of the nested model relationships
        'assignments' => $this->filter_assignments($start, $end),
        'shifts' => $this->filter_shifts($start, $end)
      ]);
  };
}

private function filter_assignments($start, $end)
{
  return function ($query) use ($start, $end) {
    $query->select([
      Assignment::PROJECT_USER_ID,
      Assignment::START,
      Assignment::FINISH,
      Assignment::TRAVEL_OUT,
      Assignment::TRAVEL_HOME
    ])
      ->where(Assignment::START, '<=', $end)
      ->where(Assignment::FINISH, '>=', $start)
      ->orderBy(Assignment::START);
  };
}

private function filter_shifts($start, $end)
{
  return function ($query) use ($start, $end) {
    $query->select([
      WorkShift::ID,
      WorkShift::USER_ID,
      WorkShift::PROJECT_ID,
      WorkShift::ROLE,
      WorkShift::DATE,
      WorkShift::START,
      WorkShift::HOURS,
      WorkShift::STATUS,
      WorkShift::REMARKS
    ])
      ->where(WorkShift::DATE, '>=', $start)
      ->where(WorkShift::DATE, '<=', $end)
      ->orderBy(WorkShift::DATE);
  };
}

///////////////
//Project Model
public function project_staff(): HasMany
{
    return $this->hasMany(ProjectUser::class);
}

////////////////////
// ProjectUser Model
public function project(): BelongsTo
{
    return $this->belongsTo(Project::class);
}

public function assignments(): HasMany
{
    return $this->hasMany(Assignment::class, 'projectUser_id');
}

public function shifts()
{
    // Using awobaz/compoships to simplify creating the relationship using a composite key
    return $this->hasMany(WorkShift::class, ['project_id','user_id'], ['project_id','user_id']);
}

///////////////////
// Assignment Model
public function project_user(): BelongsTo
{
    return $this->belongsTo(ProjectUser::class, 'projectUser_id');
}

// WorkShift Model
public function project_user()
{
    return $this->belongsTo(ProjectUser::class, ['project_id','user_id'], ['project_id','user_id']);
}

非常感谢任何花时间和思想来帮助我解决这个问题的人。非常感谢你的努力。

plicqrtu

plicqrtu2#

如前所述,您可以使用Laravel内置的关系,或者,您可以构造一个巨大的查询并将所有数据连接在一起;
就像这样:

<?php
    
$projectUsers = ProjectUser::select([
        'project_users.id', // define aliases for all columns
        'project_users.user_id',
        'users.name',
        'project_users.role',
        'projects.id',
        'projects.name',
        'projects.start',
        'projects.finish',
        'assignments.start',
        'assignments.finish',
        'assignments.travel_out',
        'assignments.travel_home',
        'work_shifts.id',
        'work_shifts.role',
        'work_shifts.date',
        'work_shifts.start',
        'work_shifts.hours',
        'work_shifts.status',
        'work_shifts.remarks'
    ])
    ->join('users', 'users.id', 'project_users.user_id'
    ->leftJoin('assignments', 'assignment.project_user_id', 'project_users.id')
    ->leftJoin('projects', fn ($query) => $query
        ->where('projects.start', '<=', Carbon::now()->endOfMonth())
        ->where('projects.finish', '>=', Carbon::now()->startOfMonth())
    )
    ->leftJoin('work_shifts', fn ($query) => $query
        ->whereColumn('work_shifts.id', 'project_users.id')
        ->whereColumn('work_shifts.project_id', 'projects.id')
    )
    ->whereColumn('project_users.project_id', 'projects.id')
    ->where('assignments.start', '<=', Carbon::now()->endOfMonth())
    ->where('assignments.finish', '>=', Carbon::now()->startOfMonth())
    ->distinct('users.name')
    ->cursor();

foreach ($projectUsers as $user) {
    // ...
}

相关问题