15-й урок создания интернет-магазина на Laravel: страница категории
В процессе разработки интернет-магазина на Laravel одним из ключевых этапов является создание страницы категории товаров. Эта страница играет важную роль в навигации пользователей по сайту и помогает им быстро находить нужные товары. В данной статье будет подробно рассмотрен процесс создания и оптимизации страницы категории для интернет-магазина на Laravel.
Содержание
Подготовка проекта и настройка базы данных
Создание модели Category и миграции
Разработка контроллера CategoryController
Реализация маршрутизации для страницы категории
Создание представления для страницы категории
Стилизация страницы категории с использованием CSS
Добавление функциональности фильтрации товаров
Реализация пагинации для списка товаров
Оптимизация запросов к базе данных
Кеширование данных категории
Добавление хлебных крошек
SEO-оптимизация страницы категории
Тестирование функциональности страницы категории
Рекомендации по дальнейшему улучшению
Подготовка проекта и настройка базы данных
Перед началом работы над страницей категории необходимо убедиться, что проект Laravel настроен корректно и база данных готова к использованию. Для этого следует выполнить следующие шаги:
Создать новый проект Laravel или открыть существующий.
Настроить подключение к базе данных в файле .env.
Убедиться, что все необходимые пакеты установлены и обновлены.
После выполнения этих шагов можно приступать к созданию модели Category и соответствующей миграции.
Создание модели Category и миграции
Для работы с категориями товаров необходимо создать модель Category и соответствующую миграцию. Это можно сделать с помощью следующих команд в терминале:
php artisan make:model Category -m
Эта команда создаст файл модели Category.php в директории app/Models и файл миграции в директории database/migrations. Теперь нужно определить структуру таблицы категорий в файле миграции:
public function up() { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug')->unique(); $table->text('description')->nullable(); $table->string('image')->nullable(); $table->unsignedBigInteger('parent_id')->nullable(); $table->foreign('parent_id')->references('id')->on('categories')->onDelete('cascade'); $table->timestamps(); }); }
После создания миграции нужно выполнить ее с помощью команды:
php artisan migrate
Разработка контроллера CategoryController
Следующим шагом является создание контроллера CategoryController, который будет отвечать за обработку запросов, связанных со страницей категории. Для создания контроллера используется команда:
php artisan make:controller CategoryController
В созданном файле CategoryController.php необходимо реализовать метод show, который будет отвечать за отображение страницы категории:
public function show($slug) { $category = Category::where('slug', $slug)->firstOrFail(); $products = $category->products()->paginate(12); return view('category.show', compact('category', 'products')); }
Реализация маршрутизации для страницы категории
Для того чтобы страница категории была доступна по определенному URL, необходимо добавить соответствующий маршрут в файл routes/web.php:
Этот маршрут будет обрабатывать запросы вида /category/electronics и передавать управление методу show контроллера CategoryController.
Создание представления для страницы категории
Теперь необходимо создать представление (view) для страницы категории. Создайте файл resources/views/category/show.blade.php и добавьте в него базовую структуру HTML:
Для улучшения пользовательского опыта можно добавить функцию фильтрации товаров на странице категории. Для этого необходимо обновить метод show в CategoryController:
public function show($slug, Request $request) { $category = Category::where('slug', $slug)->firstOrFail(); $query = $category->products(); if ($request->has('min_price')) { $query->where('price', '>=', $request->min_price); } if ($request->has('max_price')) { $query->where('price', '<=', $request->max_price); } if ($request->has('sort')) { $query->orderBy($request->sort, $request->order ?? 'asc'); } $products = $query->paginate(12); return view('category.show', compact('category', 'products')); }
Теперь нужно добавить форму фильтрации в представление category/show.blade.php:
Реализация пагинации для списка товаров
Laravel предоставляет встроенную поддержку пагинации, которую мы уже использовали в методе show контроллера CategoryController. Для отображения ссылок пагинации в представлении используется метод links():
{{ $products->links() }}
Если требуется настроить внешний вид пагинации, можно опубликовать шаблоны пагинации с помощью команды:
После этого можно изменять шаблоны пагинации в директории resources/views/vendor/pagination.
Оптимизация запросов к базе данных
Для улучшения производительности страницы категории следует оптимизировать запросы к базе данных. Одним из способов является использование метода eager loading для загрузки связанных данных:
public function show($slug, Request $request) { $category = Category::where('slug', $slug)->firstOrFail(); $query = $category->products()->with('brand', 'reviews'); // ... остальной код метода $products = $query->paginate(12); return view('category.show', compact('category', 'products')); }
Это позволит избежать проблемы N+1 запросов при загрузке связанных данных для каждого товара.
Кеширование данных категории
Для дальнейшего повышения производительности можно использовать кеширование данных категории. Добавьте следующий код в метод show контроллера CategoryController:
use Illuminate\Support\Facades\Cache; public function show($slug, Request $request) { $category = Cache::remember('category_'.$slug, 3600, function () use ($slug) { return Category::where('slug', $slug)->firstOrFail(); }); // ... остальной код метода }
Этот код будет кешировать данные категории на 1 час, что уменьшит нагрузку на базу данных при частых запросах к странице категории.
Добавление хлебных крошек
Для улучшения навигации по сайту и SEO можно добавить хлебные крошки на страницу категории. Создайте частичное представление resources/views/partials/breadcrumbs.blade.php:
Затем включите это частичное представление в category/show.blade.php:
@include('partials.breadcrumbs')
SEO-оптимизация страницы категории
Для улучшения SEO-показателей страницы категории необходимо выполнить следующие шаги:
После реализации всех вышеперечисленных функций необходимо провести тщательное тестирование страницы категории. Для этого можно использовать как ручное тестирование, так и автоматизированные тесты.
Пример функционального теста для страницы категории:
use Tests\TestCase; use App\Models\Category; use App\Models\Product; class CategoryPageTest extends TestCase { public function test_category_page_displays_correct_products() { $category = Category::factory()->create(); $products = Product::factory()->count(5)->create(['category_id' => $category->id]); $response = $this->get(route('category.show', $category->slug)); $response->assertStatus(200); $response->assertSee($category->name); foreach ($products as $product) { $response->assertSee($product->name); } } public function test_category_page_filters_products_by_price() { $category = Category::factory()->create(); $cheapProduct = Product::factory()->create(['category_id' => $category->id, 'price' => 10]); $expensiveProduct = Product::factory()->create(['category_id' => $category->id, 'price' => 100]); $response = $this->get(route('category.show', [ 'slug' => $category->slug, 'min_price' => 50, ])); $response->assertStatus(200); $response->assertDontSee($cheapProduct->name); $response->assertSee($expensiveProduct->name); } }
Рекомендации по дальнейшему улучшению
После успешной реализации основной функциональности страницы категории можно рассмотреть следующие улучшения:
Добавление функции «живого» поиска товаров с использованием AJAX.
Реализация сравнения товаров внутри категории.
Внедрение системы рекомендаций похожих товаров.
Оптимизация загрузки изображений с использованием lazy loading.
Добавление возможности просмотра товаров в виде списка или сетки.
Реализация фильтрации по атрибутам товаров, специфичным для каждой категории.
Оптимизация производительности страницы категории
Для обеспечения быстрой загрузки страницы категории и улучшения пользовательского опыта следует уделить особое внимание оптимизации производительности. Рассмотрим несколько эффективных методов оптимизации.
Кеширование запросов к базе данных
Помимо кеширования данных категории, можно также кешировать результаты запросов для товаров. Это особенно полезно, если фильтры и сортировка не меняются часто:
use Illuminate\Support\Facades\Cache; public function show($slug, Request $request) { $category = Cache::remember('category_'.$slug, 3600, function () use ($slug) { return Category::where('slug', $slug)->firstOrFail(); }); $cacheKey = 'products_' . $slug . '_' . md5(json_encode($request->all())); $products = Cache::remember($cacheKey, 1800, function () use ($category, $request) { $query = $category->products()->with('brand', 'reviews'); // Apply filters and sorting // ... return $query->paginate(12); }); return view('category.show', compact('category', 'products')); }
Оптимизация загрузки изображений
Для ускорения загрузки страницы категории важно оптимизировать работу с изображениями. Можно использовать следующие методы:
Использование адаптивных изображений с помощью тега <picture>:
Оптимизация изображений при загрузке с помощью пакета intervention/image:
use Intervention\Image\Facades\Image;
public function uploadProductImage($image)
{
$filename = time() . '.' . $image->getClientOriginalExtension();
$path = public_path('images/products/' . $filename);
Image::make($image)->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
$constraint->upsize();
})->save($path, 80);
return 'images/products/' . $filename;
}
Минификация и объединение CSS и JavaScript файлов
Для уменьшения количества HTTP-запросов и объема передаваемых данных следует минифицировать и объединить CSS и JavaScript файлы. Это можно сделать с помощью Laravel Mix:
Затем в шаблоне используйте сгенерированные файлы:
Использование CDN для статических ресурсов
Для ускорения загрузки статических ресурсов (изображения, CSS, JavaScript) рекомендуется использовать CDN (Content Delivery Network). Это позволит распределить нагрузку и уменьшить время отклика для пользователей из разных регионов.
Пример настройки CDN в файле .env:
ASSET_URL=https://your-cdn-domain.com
После этого все вызовы функции asset() будут использовать указанный домен CDN.
Реализация бесконечной прокрутки
Вместо классической пагинации можно реализовать бесконечную прокрутку, которая позволит загружать новые товары по мере прокрутки страницы. Для этого потребуется использовать AJAX-запросы и JavaScript.
Пример реализации на JavaScript с использованием библиотеки Axios:
// resources/js/infinite-scroll.js import axios from 'axios'; let page = 1; const container = document.querySelector('.products-grid'); const loadingIndicator = document.createElement('div'); loadingIndicator.textContent = 'Загрузка...'; function loadMoreProducts() { if (loadingIndicator.parentNode) return; container.appendChild(loadingIndicator); axios.get(`${window.location.href}?page=${++page}`) .then(response => { const parser = new DOMParser(); const html = parser.parseFromString(response.data, 'text/html'); const newProducts = html.querySelectorAll('.product-card'); newProducts.forEach(product => { container.appendChild(product); }); container.removeChild(loadingIndicator); }) .catch(error => { console.error('Ошибка при загрузке товаров:', error); container.removeChild(loadingIndicator); }); } window.addEventListener('scroll', () => { if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) { loadMoreProducts(); } });
Не забудьте подключить этот скрипт в ваш основной JavaScript файл:
Использование подзапросов для оптимизации сложных выборок:
$products = Product::whereIn('category_id', function ($query) use ($category) { $query->select('id') ->from('categories') ->where('id', $category->id) ->orWhere('parent_id', $category->id); })->paginate(12);
Реализация AJAX-фильтрации
Для улучшения пользовательского опыта можно реализовать AJAX-фильтрацию товаров без перезагрузки страницы. Для этого необходимо создать отдельный метод в контроллере и обновить JavaScript-код на клиентской стороне.
Функция сравнения товаров может быть полезной для пользователей при выборе товаров в одной категории. Для реализации этой функции необходимо выполнить следующие шаги:
Создать таблицу для хранения товаров для сравнения:
// app/Models/CompareItem.php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class CompareItem extends Model
{
protected $fillable = ['user_id', 'session_id', 'product_id'];
public function product()
{
return $this->belongsTo(Product::class);
}
}
Добавить методы в ProductController для добавления и удаления товаров из сравнения:
// app/Http/Controllers/ProductController.php public function addToCompare(Request $request, $productId) { $sessionId = $request->session()->getId(); $userId = auth()->id();
CompareItem::updateOrCreate(
['session_id' => $sessionId, 'product_id' => $productId],
['user_id' => $userId]
);
return response()->json(['message' => 'Товар добавлен к сравнению']);
}
public function removeFromCompare(Request $request, $productId)
{
$sessionId = $request->session()->getId();
CompareItem::where('session_id', $sessionId)
->where('product_id', $productId)
->delete();
return response()->json(['message' => 'Товар удален из сравнения']);
}
public function compare(Request $request)
{
$sessionId = $request->session()->getId();
$compareItems = CompareItem::where('session_id', $sessionId)
->with('product')
->get();
return view('compare', compact('compareItems'));
}
Добавить кнопки «Добавить к сравнению» на странице категории:
// resources/views/category/show.blade.php @foreach($products as $product)
@endforeach
Реализовать JavaScript для обработки добавления товаров к сравнению:
Система рекомендаций похожих товаров может повысить конверсию и улучшить пользовательский опыт. Рассмотрим простой способ реализации такой системы на основе категорий и тегов товаров:
Добавьте метод в модель Product для получения похожих товаров:
// app/Models/Product.php public function getSimilarProducts($limit = 4) { return $this->where('category_id', $this->category_id) ->where('id', '!=', $this->id) ->inRandomOrder() ->limit($limit) ->get(); }
Добавьте вывод похожих товаров на странице товара:
// resources/views/product/show.blade.php
Похожие товары
@foreach($product->getSimilarProducts() as $similarProduct)
Оптимизировать загрузку изображений для мобильных устройств:
Использовать touch-friendly элементы управления:
// resources/views/category/show.blade.php
Фильтры
Реализация функции «быстрый просмотр»
Функция «быстрый просмотр» позволяет пользователям просматривать основную информацию о товаре без перехода на отдельную страницу. Это может улучшить пользовательский опыт и увеличить конверсию. Вот пример реализации:
Добавьте кнопку «Быстрый просмотр» к каждому товару на странице категории:
// resources/views/category/show.blade.php @foreach($products as $product)
@endforeach
Создайте маршрут и метод контроллера для получения данных товара:
Фильтрация по атрибутам товаров позволяет пользователям быстро находить нужные товары в категории. Для реализации этой функции необходимо выполнить следующие шаги:
Создать таблицы для атрибутов и значений атрибутов:
Создать модели для атрибутов и значений атрибутов:
// app/Models/Attribute.php class Attribute extends Model { protected $fillable = ['name'];
public function values()
{
return $this->hasMany(AttributeValue::class);
}
}
// app/Models/AttributeValue.php
class AttributeValue extends Model
{
protected $fillable = ['attribute_id', 'value'];
public function attribute()
{
return $this->belongsTo(Attribute::class);
}
public function products()
{
return $this->belongsToMany(Product::class);
}
}
Обновить модель Product:
// app/Models/Product.php class Product extends Model { // ...
public function attributeValues()
{
return $this->belongsToMany(AttributeValue::class);
}
}
Обновить метод show в CategoryController для учета фильтрации по атрибутам:
// app/Http/Controllers/CategoryController.php public function show($slug, Request $request) { $category = Category::where('slug', $slug)->firstOrFail(); $query = $category->products()->with('attributeValues.attribute');
// Apply price filters
// ...
// Apply attribute filters
if ($request->has('attributes')) {
foreach ($request->attributes as $attributeId => $valueIds) {
$query->whereHas('attributeValues', function ($q) use ($attributeId, $valueIds) {
$q->whereIn('attribute_values.id', $valueIds)
->where('attributes.id', $attributeId);
});
}
}
$products = $query->paginate(12);
$attributes = Attribute::with(['values' => function ($query) use ($category) {
$query->whereHas('products', function ($q) use ($category) {
$q->where('category_id', $category->id);
});
}])->get();
return view('category.show', compact('category', 'products', 'attributes'));
}
Обновить представление для отображения фильтров по атрибутам:
// resources/views/category/show.blade.php
Улучшение SEO-оптимизации страницы категории
Для улучшения позиций сайта в поисковых системах необходимо уделить особое внимание SEO-оптимизации страницы категории. Рассмотрим несколько дополнительных методов оптимизации:
Добавление метатегов Open Graph для улучшения отображения страницы при шеринге в социальных сетях:
@section('meta') @endsection
Реализация автоматической генерации meta-описаний для категорий:
// app/Models/Category.php public function getMetaDescription() { $productCount = $this->products()->count(); $brandNames = $this->products()->distinct()->pluck('brand')->take(3)->implode(', ');
return "Купите {$this->name} в нашем интернет-магазине. {$productCount} товаров от ведущих брендов: {$brandNames}. Доставка по всей России.";
}
Добавление структурированных данных для категории:
Скорость загрузки страницы является важным фактором как для пользовательского опыта, так и для SEO. Рассмотрим несколько способов оптимизации скорости загрузки страницы категории:
Использование кеширования на уровне приложения:
// app/Http/Controllers/CategoryController.php use Illuminate\Support\Facades\Cache;
public function show($slug, Request $request)
{
$cacheKey = "category_{$slug}_" . md5(json_encode($request->all()));
return Cache::remember($cacheKey, now()->addMinutes(30), function () use ($slug, $request) {
// Логика получения данных для страницы категории
// ...
return view('category.show', compact('category', 'products', 'attributes'));
});
}
Отложенная загрузка изображений с использованием Intersection Observer API:
// config/database.php 'mysql' => [ // ... 'strict' => false, // Отключение строгого режима для оптимизации некоторых запросов ],
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\DB;
public function boot()
{
DB::connection()->disableQueryLog(); // Отключение логирования запросов в production
}
Проведение нагрузочного тестирования:
// Пример использования Apache Bench для нагрузочного тестирования ab -n 1000 -c 50 http://your-site.com/category/electronics