Как правильно использовать Laravel, чтобы предотвратить ленивую загрузку моего кода?

У меня есть небольшое приложение Laravel, которое в основном представляет собой несколько таблиц, как показано ниже, и у меня возникла проблема с Laravel Eloquent, генерирующим больше SQL-запросов, чем нужно. В основном я пытаюсь отобразить все «оценки» для вошедшего в систему студента, но свести количество запросов к минимуму.

Вот мой контроллер

    public function index(Request $request)
    {
        $student = $request->user();

        return view('dashboard.index', [
            'user' => $student,
            'grades' => $student->grades->paginate(5)
        ]);
    }

Вот мои структуры таблиц

курсы

select id, name from courses;
+----+--------------------+
| id | name               |
+----+--------------------+
|  1 | English Literature |
|  2 | Stats              |
|  3 | Biology            |
+----+--------------------+

оценки

select id, student_id, course_id, score, letter_grade from grades where student_id = 1;
+----+------------+-----------+--------+--------------+
| id | student_id | course_id | score  | letter_grade |
+----+------------+-----------+--------+--------------+
|  1 |          1 |         1 |  90.00 | A            |
|  2 |          1 |         2 |  50.00 | D            |
|  3 |          1 |         3 | 100.00 | A            |
+----+------------+-----------+--------+--------------+

курс_студент

select * from course_student;
+-----------+------------+
| course_id | student_id |
+-----------+------------+
|         1 |          1 |
|         2 |          1 |
|         3 |          1 |
+-----------+------------+

студенты

select id, email from students limit 1;
+----+---------------------------+
| id | email                     |
+----+---------------------------+
| 1 | [email protected] |
+----+---------------------------+

Мои отношения Laravel в моделях выглядят следующим образом: Студент.php


    public function grades(): \Illuminate\Database\Eloquent\Relations\HasMany
    {
        return $this->hasMany(Grade::class);
    }

    public function courses(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
    {
        return $this->belongsToMany(Course::class);
    }

Оценка.php

class Grade extends Model
{
    use HasFactory;

    public function student(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(Student::class);
    }

    public function course(): \Illuminate\Database\Eloquent\Relations\BelongsTo
    {
        return $this->belongsTo(Course::class);
    }
}

Курс.php

class Course extends Model
{
    use HasFactory;

    public function department(): \Illuminate\Database\Eloquent\Relations\HasOne
    {
        return $this->hasOne(Department::class, 'id', 'department_id');
    }

    public function students(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
    {
        return $this->belongsToMany(Student::class);
    }

    public function teachers(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
    {
        return $this->belongsToMany(Teacher::class);
    }
}

DashboardController.php

    public function index(Request $request)
    {
        $student = $request->user();

        return view('dashboard.index', [
            'user' => $student,
            'grades' => $student->load('grades', 'courses')->grades->paginate(10)
        ]);
    }

представление панели инструментов/index.blade.php

    <div class = "py-2">
        <div class = "max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class = "bg-white dark:bg-gray-800 sm:rounded-lg">
                {{ $grades->links() }}
                <table>
                <tr>
                    <td>Course</td>
                    <td>Grade</td>
                    <td>Score</td>
                    <td>Date</td>
                </tr>
                @foreach ($grades as $key => $grade)
                <tr>
                    <td>{{ $grade->course->name }}</td>
                    <td>{{ $grade->letter_grade }}</td>
                    <td>{{ $grade->score }}</td>
                    <td>{{ $grade->created_at }}</td>
                </tr>
                @endforeach
                </table>
            </div>
        </div>
    </div>

Используя Laravel Debugbar, я вижу все сгенерированные запросы - у меня нет никаких ошибок, но SELECT * из курсов можно оптимизировать для использования IN(), а не всего SELECT * из курсов по первичному ключу каждый раз, как мне оптимизировать логику моего контроллера/модели

select * from `students` where `id` = 1 and `students`.`deleted_at` is null limit 1
select * from `grades` where `grades`.`student_id` = 1 and `grades`.`student_id` is not null
select * from `courses` where `courses`.`id` = 1 limit 1
select * from `courses` where `courses`.`id` = 2 limit 1
select * from `courses` where `courses`.`id` = 3 limit 1
select * from `courses` where `courses`.`id` = 4 limit 1
select * from `courses` where `courses`.`id` = 5 limit 1

Ожидаемый результат будет использовать меньше запросов к таблице курсов и НЕ откладывать запросы на загрузку.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
0
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы проделали отличную работу по настройке отношений в каждой Модели.

Для вашего вопроса вы можете добиться этого, используя вложенную нетерпеливую загрузку.

$student = Student::query()
   ->where('id', auth()->id())
   ->with('grades.courses')
   ->first();

$grades = $student->grades->paginate(5);

return view('dashboard.index', compact('student', 'grades'));

Как описано здесь: https://laravel.com/docs/9.x/eloquent-relationships#nested-eager-loading

Теперь ваш laravel должен выполнять только 3 запроса по таблице студентов, оценок и курсов.

И если вы хотите получать оценки только с разбиением на страницы, попробуйте выполнить этот запрос.

$user = $request->user(); // or auth()->user()
$grades = Grade::query()
   ->where('student_id', $user->id)
   ->with('courses')
   ->paginate(5);

return view('dashboard.index', compact('user', 'grades'));

Это по-прежнему будет выполнять только 3 запроса.

примечание: $request->user() то же, что auth()->user()
и в запросе я использую auth()->id(), чтобы получить идентификатор вошедшего в систему пользователя

$grades = $student->grades->paginate(5); не позволяет разбивать оценки на страницы, поскольку они загружаются в экземпляр $student без разбиения на страницы.
N69S 12.02.2023 18:16

это правда @ N69S, спасибо, что исправили это своим ответом

Win 12.02.2023 18:52
Ответ принят как подходящий

Ваша разбивка на страницы не нажимает queryBuilder, а вместо этого Collection: выполнение $student->grades уже извлекает все оценки учащегося, поэтому запрос не имеет ограничений.

Вы используете отношение courses Grade::class в своем лезвии, но вы загрузили отношение courses Student::class в свой контроллер.

Чтобы улучшить все, вы можете сделать это так

public function index(Request $request)
{
    $student = $request->user();

    return view('dashboard.index', [
        'user' => $student,
        'grades' => $student->grades()->with('courses')->paginate(10),
    ]);
}

Вызов $student->grades() возвращает QueryBuilder вместо вызова $student->grades, который возвращает коллекцию.

Другие вопросы по теме