我有五个表students
,grades
,subjects
,terms
和scores
。我正在thes表上执行内部联接以返回结果。这是我的架构的样子:
学生表:
students
--------
id *
name
class_id (fk)
主题表:
subjects
--------
id *
name
班级表:
classes
--------
id *
name
条款表:
terms
--------
id *
name
得分表:
scores
---------------
id *
student_id (fk)
subject_id (fk)
class_id (fk)
term_id (fk)
score
我的laravel查询:
$scores = \DB::table('scores')
->join('students', 'students.id', '=', 'scores.student_id')
->join('subjects', 'subjects.id', '=', 'scores.subject_id')
->join('grades', 'grades.id', '=', 'scores.grade_id')
->join('terms', 'terms.id', '=', 'scores.term_id')
->select('students.first_name', 'students.surname', 'subjects.name as subject', 'grades.name as grade', 'terms.name as term', 'score')
->where('students.id', 1)
->whereBetween('scores.term_id', [1, 3])
->get();
当我死掉并转储它时,查询返回结果:
毫无疑问,查询返回了正确的结果,但问题是我希望结果出现在我的html表中,如下所示:
这是基于我现在在控制器和视图中的代码显示的方式。
控制器:
public function index()
{
//
$scores = \DB::table('scores')
->join('students', 'students.id', '=', 'scores.student_id')
->join('subjects', 'subjects.id', '=', 'scores.subject_id')
->join('grades', 'grades.id', '=', 'scores.grade_id')
->join('terms', 'terms.id', '=', 'scores.term_id')
->select('students.first_name', 'students.surname', 'subjects.name as subject', 'grades.name as grade', 'terms.name as term', 'score')
->where('students.id', 1)
->whereBetween('scores.term_id', [1, 3])
->get();
// finding details of the student based on id pased
$student = Student::findOrFail(1);
// getting the name of the student
$name = $student->first_name.' '.$student->surname;
// getting the class or grade of the student (grade 12 or grade 11)
$grade = $student->grade->name;
// getting the current date
$date = Score::date();
return view('scores.home', compact('scores', 'name', 'date', 'grade'));
视图:
<table class="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th scope="row">Name</th>
<td colspan="4">{{$name}}</td>
</tr>
<tr>
<th scope="row">Class</th>
<td colspan="2">{{$grade}}</td>
<th scope="row">Date</th>
<td>{{$date->toFormattedDateString()}}</td>
</tr>
<tr>
<th class="text-center">Subject</th>
@foreach($scores as $score)
<th class="text-center">{{$score->term}}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($scores as $score)
<tr>
<td>{{$score->subject}}</td>
<td>{{$score->score}}</td>
</tr>
@endforeach
</tbody>
</table>
结果:
如上面的结果所示,术语名称1st Period
正在重复,我该如何避免?如何在视图或控制器中重构我的查询或代码以获得我想要的结果?
答案 0 :(得分:4)
首先,我会稍微简化一下查询,只选择你还不知道的数据。 没有必要在每一行返回学生姓名和成绩,因为它们总是一样的。
$student = Student::findOrFail(1);
$scores = \DB::table('scores')
->join('subjects', 'subjects.id', '=', 'scores.subject_id')
->join('terms', 'terms.id', '=', 'scores.term_id')
->select('subjects.name as subject', 'terms.name as term', 'score')
->where('scores.student_id', $student->id)
->whereBetween('scores.term_id', [1, 3])
->get();
您将获得与您的结果类似的以下集合:
[
0 => (object)[
'subject' => 'Mathematics',
'term' => '1st Period',
'score' => 99
],
1 => (object)[
'subject' => 'Biology',
'term' => '2nd Period',
'score' => 99
],
2 => (object)[
'subject' => 'Biology',
'term' => '3rd Period',
'score' => 79
]
]
现在将其转换为嵌套结构:
$scores = $scores->groupBy('subject')->map(function($item){
return $item->keyBy('term')->map(function($item){
return $item->score;
});
});
您将获得以下收藏:
[
'Mathematics' => [
'1st Period' => 99,
],
'Biology' => [
'2nd Period' => 99,
'3nd Period' => 79
]
]
但这不是表结构 - 缺少一些术语。 所以你需要填写缺少的条款,因为你不想在你的视图中这样做。 我会创建一个空表结构并将数据填入其中:
$terms = Term::whereBetween('id', [1, 3])->pluck('name');
// returns: ['1st Period', '2nd Period', '3rd Period']
使用空分数初始化表格:
$scoreTable = [];
foreach ($scores->keys() as $subject){
$scoreTable[$subject] = [];
foreach ($terms as $term){
$scoreTable[$subject][$term] = '';
}
}
在表格中填写给定分数:
foreach ($scores as $subject => $row){
foreach($row as $term => $score){
$scoreTable[$subject][$term] = $score;
}
}
现在“表格”将如下所示:
[
'Mathematics' => [
'1st Period' => 99,
'2nd Period' => '',
'3nd Period' => '',
],
'Biology' => [
'1st Period' => '',
'2nd Period' => 99,
'3nd Period' => 79,
],
]
将其传递给您的视图,然后像这样渲染表格:
<table>
<tr>
<th>Subject</th>
@foreach($terms as $term)
<th>{{$term}}</th>
@endforeach
</tr>
@foreach($scoreTable as $subject => $scores)
<tr>
<td>{{$subject}}</td>
@foreach($terms as $term)
<td>{{$scores[$term]}}</td>
@endforeach
</tr>
@endforeach
</table>
您将获得以下HTML代码:
<table>
<tr>
<th>Subject</th>
<th>1st Period</th>
<th>2nd Period</th>
<th>3rd Period</th>
</tr>
<tr>
<td>Mathematics</td>
<td>99</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Biology</td>
<td></td>
<td>99</td>
<td>79</td>
</tr>
</table>
获取$scoreTable
的较短方式可能是
$terms = Term::whereBetween('id', [1, 3])->pluck('name');
$initRow = $terms
->keyBy(function($term){ return $term; })
->map(function(){ return ''; });
$scoreTable = $scores
->groupBy('subject')
->map(function($subject) use($initRow){
$row = $subject
->keyBy('term')
->map(function($term) use($initRow){
return $term->score;
});
return $initRow->merge($row);
});
但它似乎不太可读。也可能有一个或另一个collection
函数将替换map
函数并使事情变得更容易。但我不知道所有这些。
这是另一种更短的方式,但在较大的数据集上可能会很慢,因为每个表格单元都会调用两次昂贵的where()
函数。
$scoreTable = [];
foreach ($scores->pluck('subject')->unique() as $subject){
foreach ($scores->pluck('term')->unique() as $term) {
$scoreTable[$subject][$term] = $scores
->where('subject', $subject)
->where('term', $term)
->pluck('score')
->first();
}
}
最后,我提出了以下解决方案,其中(我认为)是最易读/最简单快速的解决方案。
$subjects = $scores->pluck('subject')->unique(); // ['Mathematics', 'Biology']
$terms = $scores->pluck('term')->unique(); // ['1st Period', '2nd Period', '3rd Period']
$scoreTable = [];
foreach ($subjects as $subject) {
foreach ($terms as $term) {
$scoreTable[$subject][$term] = '';
}
}
foreach ($scores as $row) {
$scoreTable[$row->subject][$row->term] = $row->score;
}
return view('scores.home', compact('scoreTable', 'terms', 'name', 'date', 'grade'));
前两行将从查询结果中提取唯一的主题和术语。 (有关您在pluck()中找到的unique()和laravel docs方法的更多信息。)然后在以下嵌套循环中使用它们来生成一个表空结构(主题✕条款)值。在下一个循环中,查询结果中的分数将填入“表”中。
答案 1 :(得分:0)
在Laravel,我们有Laravel雄辩,你只需要定义关系,你就可以了。 在你的分数模型中,假设你有一个,只需定义关系
public function student(){
return $this->belongsTo(student::class, 'student_id', 'id');
}
在控制器或路线中,根据您的工作地点,只需拨打分数
$scores = Score::all();
return view('scores.home', compact('scores'));
当你用刀片呼叫学生时,你只需写{{$score->student->name}}
然后对所有其他项目(即成绩,条款等)执行相同操作。
答案 2 :(得分:0)
我会通过这样的方式收集与他的主题配对的分数:
$scores_paired = collect();
foreach($student->subjects as $subject){
$scores_paired->push($scores->where('subject', $subject->name)->sortBy('term','ASC'));
}
然后将此新集合传递给视图,以便您可以在视图中循环$scores_paired