Разработка каталога товаров и категорий
Вывод списка категорий и товаров, реализация пагинации и детальной страницы продукта. Фокус на Project-Based Learning.
В этом уроке мы применим подход Project-Based Learning для создания 'сердца' нашего интернет-магазина — каталога товаров. Пользователь должен иметь возможность просматривать все товары, фильтровать их по категориям и переходить на детальную страницу продукта. Проблема, с которой сталкиваются при больших объемах данных, заключается в том, что загрузка тысяч товаров на одну страницу убьет производительность сервера и браузера. Для решения этой проблемы используется Пагинация (разбивка на страницы). Laravel делает этот процесс невероятно простым благодаря встроенному методу paginate() в Eloquent. Кроме того, мы столкнемся с проблемой 'N+1 запросов'. Если мы выведем 20 товаров и для каждого будем запрашивать его категорию в цикле Blade, фреймворк выполнит 1 запрос на товары и 20 запросов на категории. Мы решим это с помощью 'Жадной загрузки' (Eager Loading), используя метод with('category').
namespace App\Http\Controllers;
use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\View\View;
class ProductController extends Controller
{
public function index(Request $request): View
{
// Жадная загрузка категории и пагинация по 12 товаров на страницу
$query = Product::with('category');
// Если передана категория в URL, фильтруем по ней
if ($request->has('category_id')) {
$query->where('category_id', $request->category_id);
}
$products = $query->paginate(12);
return view('products.index', compact('products'));
}
public function show(Product $product): View
{
// Laravel автоматически найдет товар по ID благодаря Route Model Binding
return view('products.show', compact('product'));
}
}
Разберем код контроллера. В методе index мы используем Product::with('category')->paginate(12). Метод with указывает фреймворку заранее извлечь все связанные категории одним дополнительным SQL-запросом (Eager Loading), что радикально снижает нагрузку на базу данных. Метод paginate(12) берет только 12 записей, соответствующих текущей странице (передается через GET-параметр ?page=2), и возвращает специальный объект LengthAwarePaginator. Этот объект содержит не только сами товары, но и метаданные: общее количество страниц, текущую страницу и методы для генерации HTML-ссылок пагинации. В методе show мы используем магию Laravel под названием 'Route Model Binding'. Обратите внимание: мы передаем в аргументы Product $product. Laravel видит, что мы ожидаем модель, берет ID из URL (например, /products/5) и автоматически выполняет Product::findOrFail(5). Если товар не найден, он сам вернет ошибку 404. Это избавляет нас от написания лишнего шаблонного кода.
{{-- Файл: resources/views/products/index.blade.php --}}
@extends('layouts.app')
@section('content')
<h1>Каталог товаров</h1>
<div class="product-grid">
@foreach($products as $product)
<div class="product-card">
<h2>{{ $product->name }}</h2>
<p>Категория: {{ $product->category->name }}</p>
<p>Цена: ${{ number_format($product->price, 2) }}</p>
<a href="{{ route('products.show', $product) }}">Подробнее</a>
</div>
@endforeach
</div>
{{-- Вывод кнопок пагинации --}}
<div class="pagination-wrapper">
{{ $products->links() }}
</div>
@endsection
В шаблоне Blade мы используем директиву @foreach для итерации по массиву товаров. Внутри цикла мы обращаемся к $product->category->name. Благодаря жадной загрузке, которую мы настроили в контроллере, это действие не вызывает новых запросов к БД. Самое интересное находится внизу шаблона: {{ $products->links() }}. Всего одна строчка кода генерирует полноценную, стилизованную (по умолчанию под Tailwind CSS или Bootstrap, в зависимости от настроек) панель навигации по страницам со стрелочками 'Вперед' и 'Назад'. Это отличный пример того, как фреймворк берет на себя рутинную работу, позволяя разработчику сфокусироваться на бизнес-логике проекта.