首页 文章

如何在laravel视图或控制器中格式化连接查询结果?

提问于
浏览
1

我有五个表 studentsgradessubjectstermsscores . 我正在thes表上执行内部联接以返回结果 . 我的架构看起来如何:

学生表:

students
--------
id *
name
class_id (fk)

科目表:

subjects
--------
id *
name

class 表:

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

当我死掉并转储它时,查询返回结果:

enter image description here

毫无疑问,查询返回正确的结果,但 problem 是我希望结果出现在我的html表中,如下所示:

enter image description here

这是它基于我现在在我的控制器和视图中的代码显示的方式 .

控制器:

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>

结果:

enter image description here

如上面的结果所示,术语名称 1st Period 正在重复,我该如何避免?如何在视图或控制器中重构我的查询或代码以获得我想要的结果?

3 回答

  • 0

    首先,我会稍微简化一下查询,只选择你还不知道的数据 . 没有必要在每一行返回学生姓名和成绩,因为他们总是一样的 .

    $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 函数并使事情变得更容易 . 但我不知道他们 .

    更新2:

    这是另一种更短的方式,但在较大的数据集上可能会很慢,因为每个表格单元都会调用两次昂贵的 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();
        }
    }
    

    更新3:

    最后,我提出了以下解决方案,其中(我认为)是最易读/最简单快速的解决方案 .

    $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()中的unique()方法 . )然后在下面的嵌套循环中使用它们来生成具有空值的表结构(主题✕术语) . 在下一个循环中,查询结果中的分数将填入"table" .

  • 4

    在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}}

    然后对所有其他项目(即成绩,术语等)执行相同的操作 .

  • 0

    我会通过这样的方式收集与他的主题配对的分数:

    $scores_paired = collect();
    
    foreach($student->subjects as $subject){
       $scores_paired->push($scores->where('subject', $subject->name)->sortBy('term','ASC'));
    }
    

    然后将此新集合传递给视图,以便您可以在视图中循环 $scores_paired

相关问题