Создание страницы каталога с детальным описанием на PHP
Реализация детальной страницы каталога на PHP: обзор подходов
Как организовать вывод детальной страницы товара без дублирования кода?
Наиболее эффективное решение строится на принципе MVC (Model-View-Controller). Код разделяется на три слоя: модель отвечает за работу с данными, контроллер обрабатывает запрос и передаёт данные в представление, а представление отвечает за визуализацию. Такой подход обеспечивает переиспользование кода и лёгкую поддержку.
Пример структуры файлов:
/catalog
/controllers
ProductController.php
/models
Product.php
/views
product.php
index.php (роутер)Catalog detailed php (детальная страница каталога php)
Шаг 1. Роутер (index.php)
require_once 'controllers/ProductController.php';
$id = (int)($_GET['id'] ?? 0);
$controller = new ProductController();
$controller->show($id);Шаг 2. Модель (Product.php)
class Product {
private PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function getById(int $id): ?array {
$stmt = $this->pdo->prepare('SELECT * FROM products WHERE id = ?');
$stmt->execute([$id]);
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
}
}Шаг 3. Контроллер (ProductController.php)
class ProductController {
public function show(int $id): void {
$pdo = new PDO('mysql:host=localhost;dbname=catalog', 'user', 'pass');
$model = new Product($pdo);
$product = $model->getById($id);
if (!$product) {
http_response_code(404);
require 'views/404.php';
return;
}
require 'views/product.php';
}
}Шаг 4. Представление (views/product.php)
<h1><?= htmlspecialchars($product['name']) ?></h1>
<p><?= nl2br(htmlspecialchars($product['description'])) ?></p>
<span class="price"><?= number_format($product['price'], 2) ?> руб.</span>Типичные проблемы:
- SQL-инъекции при конкатенации id в запрос (решение – использование подготовленных выражений).
- XSS-уязвимости при выводе данных без экранирования (использовать htmlspecialchars).
- Отсутствие обработки 404 (проверять существование товара).
Как быстро вывести детальную страницу, если нет фреймворка?
Самый простой вариант – вся логика в одном файле. Подходит для прототипов или очень маленьких проектов.
$id = (int)($_GET['id'] ?? 0);
$pdo = new PDO('mysql:host=localhost;dbname=catalog', 'user', 'pass');
$stmt = $pdo->prepare('SELECT * FROM products WHERE id = ?');
$stmt->execute([$id]);
$product = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$product) {
http_response_code(404);
echo 'Товар не найден';
exit;
}
?>
<h1><?= htmlspecialchars($product['name']) ?></h1>
...Недостатки:
- Смешивание логики и представления.
- Сложность поддержки при росте проекта.
Как реализовать детальную страницу в Laravel?
Laravel предоставляет готовый маршрут, контроллер, Eloquent ORM и Blade шаблоны.
// routes/web.php
Route::get('/product/{id}', [ProductController::class, 'show']);
// app/Http/Controllers/ProductController.php
public function show($id) {
$product = Product::findOrFail($id);
return view('product', compact('product'));
}
// resources/views/product.blade.php
<h1>{{ $product->name }}</h1>
<p>{{ $product->description }}</p>Ошибки:
- Использование find($id) без findOrFail – при отсутствии записи вернёт null, а не 404.
- Отсутствие валидации id (Laravel сам преобразует в int, но можно добавить Rule).
Как сделать детальную страницу с подгрузкой через AJAX?
Используется REST API, возвращающий JSON. Клиентская часть получает данные и рендерит HTML.
// api.php (роут для API)
Route::get('/api/product/{id}', function ($id) {
$product = Product::find($id);
if (!$product) {
return response()->json(['error' => 'Not found'], 404);
}
return response()->json($product);
});
// JavaScript (fetch)
fetch('/api/product/123')
.then(response => response.json())
.then(product => {
document.getElementById('name').textContent = product.name;
document.getElementById('price').textContent = product.price;
});Проблемы:
- SEO – содержимое не индексируется поисковиками. Решение: SSR или prerender.
- CSRF защита для API (если используются сессии).
Расширенные примеры реализации детальной страницы
Пример 1: Полный контроллер с валидацией и логированием
class ProductController {
private Product $model;
private Logger $logger;
public function __construct(Product $model, Logger $logger) {
$this->model = $model;
$this->logger = $logger;
}
public function show(int $id): void {
// Валидация id
if ($id <= 0) {
$this->logger->warning('Invalid product ID: ' . $id);
http_response_code(400);
require 'views/400.php';
return;
}
try {
$product = $this->model->getById($id);
if (!$product) {
$this->logger->info('Product not found: ' . $id);
http_response_code(404);
require 'views/404.php';
return;
}
// Генерация мета-тегов
$metaTitle = htmlspecialchars($product['name']) . ' - Каталог';
$metaDescription = mb_substr(strip_tags($product['description']), 0, 160);
require 'views/product.php';
} catch (\Exception $e) {
$this->logger->error('Database error: ' . $e->getMessage());
http_response_code(500);
require 'views/500.php';
}
}
}Результат: страница с корректным HTTP-статусом, мета-тегами и логированием ошибок.
Пример 2: Использование шаблонизатора Twig
// composer require twig/twig
$loader = new \Twig\Loader\FilesystemLoader('templates');
$twig = new \Twig\Environment($loader, ['cache' => 'cache']);
echo $twig->render('product.html.twig', [
'product' => $product,
'breadcrumbs' => [
['url' => '/catalog', 'title' => 'Каталог'],
['url' => '/catalog/category/' . $product['category_id'], 'title' => $product['category_name']],
['title' => $product['name']]
]
]);В шаблоне product.html.twig: хлебные крошки, мета-теги, микроразметка Schema.org.
Пример 3: Кэширование результата
$cacheKey = 'product_' . $id;
$cached = apcu_fetch($cacheKey);
if ($cached !== false) {
echo $cached;
return;
}
ob_start();
// ... отрисовка страницы
$html = ob_get_clean();
apcu_store($cacheKey, $html, 3600);
echo $html;Сокращение времени загрузки страницы при повторных запросах. Требуется APCu или Redis.
Пример 4: Обработка SEO-дублей с помощью canonical
$canonical = 'https://example.com/product/' . $product['id'];
$requestUri = $_SERVER['REQUEST_URI'];
if (parse_url($requestUri, PHP_URL_PATH) !== '/product/' . $product['id']) {
// Перенаправление на канонический URL
header('Location: ' . $canonical, true, 301);
exit;
}Предотвращает дублирование страниц с разными параметрами (например, /product/123?ref=utm).