Это работает на всех взаимосвязях, но рассмотрим пример на простой взаимосвязи hasMany. Итак, очень простой пример: есть Категория (модель Cat) и есть Товар (модель Product). В каждой категории может быть несколько товаров Cat hasMany Products.
В модели Cat это, соответственно, выглядит так:
//файл /app/Models/Cat.php public function products() { return $this->hasMany(Product::class); }
Допустим, товаров каждой категории у нас мало и категорий мало и нам нужно вывести:
В контроллере CatsController у нас должно быть две функции index и show, которые выводят, соответственно 1) и 2) пункт.
//файл /app/Http/Controllers/CatsController.php //функция index, отвечающая за функционал 1)-го пункт public function index() { //получаем список всех категорий из бд $cats = Cat::get(); //передаем переменную с массивом полученных категорий в вьюер cats return view('cats.index',compact('cats')); } //функция show, отвечающая за функционал 2)-го пункта public function show($id) { //находим нужную категорию по id $cat = Cat::findOrFail($id); //передаем переменную с массивом найденной категорий в вьюер cats return view('cats.show',compact('cat')); }
В приведенном выше коде ничего сложного вроде нет. Нужно не забыть прописать в маршруты routes соответствующие правила, чтобы этот контроллер работал как надо. Это не сложно, описывать этот процесс сейчас не буду.
Осталось сделать вьюер и прописать в нем вывод данных из полученных массивов категорий. Для index это будет выглядеть так
//файл /resources/views/cats/index.blade.php //для каждой категории из массива категорий @foreach($cats as $cat) //выводим название каждой категории {{ $cat->name }} <br /> //для каждого товара, привязанного к категории (функция products из модели Cat) @foreach($cat->products as $product) //выводим название товара {{ $product->name }} <br /> //конец цикла foreach товаров @endforeach //конец цикла foreach категорий @endforeach
Для show это будет выглядеть также, но без лишнего foreach
//файл /resources/views/cats/show.blade.php //выводим название категории {{ $cat->name }} <br /> //для каждого товара, привязанного к категории (функция products из модели Cat) @foreach($cat->products as $product) //выводим название товара {{ $product->name }} <br /> //конец цикла foreach товаров @endforeach
Поскольку код вывода товаров получился одинаковый, чтобы его не дублировать, можно сделать отдельный вьюер вывода списка товаров и инклудить его в каждый из вьюеров, но сейчас не про это.
Сейчас про запросы к БД. Если посмотреть дебаг запросов к БД данного кода, то увидим, что для каждой категории создается отдельный запрос к БД, который выводит список товаров категории. Выглядит это так:
//вывод запросов к бд из дебага select * from 'products' where 'cat_id' = ...;
Это называется "ленивая загрузка" (lazy load). Привязанные к текущей модели Cat элементы из модели Product не выводятся, пока не запросят их вывод через вьюер методом $cat->products. Т.е. без данного метода запрос к базе данных не будет осуществляться. Но при наличии данного метода по каждой из категорий будет создаваться отдельный запрос к БД для вывода списка товаров данной конкретной категории.
Т.е. lazy load удобно использовать там, где только одна категория (в функции show), но не очень правильно с точки зрения нагрузки на БД использовать там где несколько категорий (в функции index).
Чтобы избежать этого существует "нетерпеливая загрузка". Данный подход предполагает использование метода with()
Вернемся к функции index, отвечающей за функционал 1)-го пункта нашего контроллера.
//файл /app/Http/Controllers/CatsController.php //функция index, отвечающая за функционал 1)-го пункт public function index() { //получаем список всех категорий из бд с помощью "нетерпеливой загрузки" $cats = Cat::with('products'): ->get(); //передаем переменную с массивом полученных категорий в вьюер cats return view('cats.index',compact('cats')); }
Больше ничего менять не нужно. Таким образом мы меняем "ленивую" загрузку на "нетерпеливую" загрузку и убираем несколько запросов к БД по каждой из категорий, оставляя один запрос вида:
//вывод запросов к бд из дебага select * from 'products' where 'cat_id' in (1, 4, 8, ...);
Если необходимо указать несколько отношений для нетерпеливой загрузки то они указываются в массиве:
$cats = Cat::with(['products','comments','posts']) ->get();
Бывают ситуации, когда необходимо указать дополнительные условия вывода привязанных моделей при использовании нетерпеливой загрузки. Делается это так:
$cats = Cat::with(['products', 'posts' => function ($query) { $query->where('user_id', '=', '1'); }, 'comments' => function ($query) { $query->where('user_id', '=', '1'); } ]) ->get();
Ну и под конец обзора пример кода вывода привязанных моделей с использованием нетерпеливой загрузки с использованием условий.
Допустим, у товара есть комментарии и обзоры по принципу Product hasMany Comments и Product hasMany Reviews. Каждый Comment и Review привязан к пользователю User через user_id. Нам нужно вывести в функции index список товаров с привязанными активными комментариями и обзорами текущего авторизованного пользователя к товару если пользователь авторизован. Т.е. пользователь, если авторизовался, то должен увидеть только свои комментарии и обзоры к товару. Товар также имеет привязанные свойства и аналоги (Product hasMany Props и Product hasMany Analogs) но они не привязаны к пользователям.
Сделать это можно так
//файл /app/Http/Controllers/ProductsController.php public function index() { //задаем массив привязанных свойств и аналогов, //не нуждающихся в дополнительных условиях $with = ['props','analogs']; //если пользователь авторизован if (Auth::user()) { //задаем массив комментариев с дополнительным условием // отбора по id текущего авторизованного пользователя $with['comments'] = function ($query) { $query->where('user_id', '=', Auth::user()->id); }; //задаем массив обзоров с дополнительным условием // отбора по id текущего авторизованного пользователя $with['reviews'] = function ($query) { $query->where('user_id', '=', Auth::user()->id); }; } // отбираем список товаров по нужным параметрам из БД $products = Product::with($with)->get(); // передаем список полученных товаров во вьюер return view('products.index',compact('products')); }