This commit is contained in:
Alisa
2026-06-07 21:14:30 +03:00
parent 76bf5d8191
commit 66e931e0d6
14 changed files with 1079 additions and 12 deletions
+138 -1
View File
@@ -18,6 +18,7 @@ final class HomeController
$categories = [];
$catalogProducts = [];
$popularProducts = [];
$homeCatalogSections = [];
$settings = [];
$notice = null;
@@ -25,10 +26,65 @@ final class HomeController
Database::pdo()->query('SELECT 1');
$dbStatus = 'connected';
$categories = (new CategoryRepository())->tree();
$categoryRepository = new CategoryRepository();
$categories = $categoryRepository->tree();
$productRepository = new ProductRepository();
$catalogProducts = $productRepository->catalogPreview(24);
$popularProducts = $productRepository->popularPreview(10);
$homeCatalogSections[] = [
'key' => 'promos',
'title' => 'Акции',
'url' => '/catalog',
'count' => 10,
'kind' => 'promo',
'products' => $productRepository->promoPreview(10),
];
$homeCatalogSections[] = [
'key' => 'new',
'title' => 'Новинки',
'url' => '/catalog',
'count' => 50,
'kind' => 'new',
'products' => $productRepository->newPreview(10),
];
foreach ([
['slugs' => ['ikra'], 'title' => 'Икра'],
['slugs' => ['ryba'], 'title' => 'Рыба'],
['slugs' => ['moreprodukty'], 'title' => 'Морепродукты'],
['slugs' => ['myaso'], 'title' => 'Мясо'],
['slugs' => ['polufabrikaty'], 'title' => 'Полуфабрикаты'],
['slugs' => ['syry'], 'title' => 'Сыры'],
['slugs' => ['frukty-ovoschi-yagody-griby'], 'title' => 'Фрукты, овощи, ягоды, грибы'],
['slugs' => ['mramornaya-govyadina'], 'title' => 'Мраморная говядина'],
['slugs' => ['kopchenaya-ryba', 'kopchenaya-riba-1'], 'title' => 'Копченая рыба'],
['slugs' => ['vyalenaya-ryba', 'vylaenaya-riba'], 'title' => 'Вяленая рыба'],
['slugs' => ['malosolnaya-ryba', 'malosolnaya-riba'], 'title' => 'Малосольная рыба'],
] as $sectionConfig) {
$categoryMatch = $this->homeCategoryShowcase(
$categoryRepository,
$productRepository,
$categories,
$sectionConfig['slugs'],
$sectionConfig['title']
);
if ($categoryMatch === null) {
continue;
}
$category = $categoryMatch['category'];
$showcase = $categoryMatch['showcase'];
$slug = (string) ($sectionConfig['slugs'][0] ?? $category['slug']);
$homeCatalogSections[] = [
'key' => $slug,
'title' => $sectionConfig['title'],
'url' => '/catalog/' . $category['slug'],
'count' => $showcase['total'],
'kind' => 'category',
'products' => $showcase['products'],
];
}
$settings = (new SettingRepository())->allKeyed();
} catch (Throwable $exception) {
$dbStatus = app_env('APP_DEBUG', 'false') === 'true'
@@ -49,9 +105,90 @@ final class HomeController
'categories' => $categories,
'catalogProducts' => $catalogProducts,
'popularProducts' => $popularProducts,
'homeCatalogSections' => $homeCatalogSections,
'settings' => $settings,
'notice' => $notice,
'buildVersion' => '2026-06-04-02',
]);
}
/**
* @param array<int, array<string, mixed>> $categoryTree
* @param array<int, string> $slugs
* @return array{category: array<string, mixed>, showcase: array{products: array<int, array<string, mixed>>, total: int}}|null
*/
private function homeCategoryShowcase(
CategoryRepository $categoryRepository,
ProductRepository $productRepository,
array $categoryTree,
array $slugs,
string $title
): ?array {
$candidates = [];
$seenIds = [];
foreach ($slugs as $slug) {
$category = $categoryRepository->findBySlug($slug);
if ($category !== null) {
$categoryId = (int) $category['id'];
$seenIds[$categoryId] = true;
$candidates[] = $category;
}
}
foreach ($this->findCategoriesByName($categoryTree, $title) as $category) {
$categoryId = (int) $category['id'];
if (isset($seenIds[$categoryId])) {
continue;
}
$seenIds[$categoryId] = true;
$candidates[] = $category;
}
$fallback = null;
foreach ($candidates as $category) {
$showcase = $productRepository->categoryRetailShowcase((int) $category['id'], 10);
$match = [
'category' => $category,
'showcase' => $showcase,
];
if ($showcase['products'] !== []) {
return $match;
}
$fallback ??= $match;
}
return $fallback;
}
/**
* @param array<int, array<string, mixed>> $categories
* @return array<int, array<string, mixed>>
*/
private function findCategoriesByName(array $categories, string $name): array
{
$matches = [];
$normalizedName = $this->normalizeCategoryName($name);
foreach ($categories as $category) {
if ($this->normalizeCategoryName((string) ($category['name'] ?? '')) === $normalizedName) {
$matches[] = $category;
}
$matches = array_merge(
$matches,
$this->findCategoriesByName($category['children'] ?? [], $name)
);
}
return $matches;
}
private function normalizeCategoryName(string $name): string
{
return trim(str_replace('ё', 'е', mb_strtolower($name)));
}
}
@@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
namespace App\Controllers;
use App\Core\Database;
use App\Repositories\SettingRepository;
use Throwable;
final class WishlistRequestController
{
public function store(): void
{
$customerName = trim((string) ($_POST['customer_name'] ?? ''));
$phone = trim((string) ($_POST['phone'] ?? ''));
$email = trim((string) ($_POST['email'] ?? ''));
$requestText = trim((string) ($_POST['request_text'] ?? ''));
if ($customerName === '' || $requestText === '' || ($phone === '' && $email === '')) {
$this->redirect('error');
}
try {
$uploadedFiles = $this->storeUploadedFiles();
$fullRequestText = $this->buildRequestText($requestText, $uploadedFiles);
$statement = Database::pdo()->prepare(
'INSERT INTO price_requests
(customer_name, phone, email, company_name, request_text, file_path, status, client_ip, user_agent)
VALUES
(:customer_name, :phone, :email, :company_name, :request_text, :file_path, :status, :client_ip, :user_agent)'
);
$statement->execute([
'customer_name' => $customerName,
'phone' => $phone !== '' ? $phone : null,
'email' => $email !== '' ? $email : null,
'company_name' => 'Запрос позиции',
'request_text' => $fullRequestText,
'file_path' => $uploadedFiles[0] ?? null,
'status' => 'new',
'client_ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'user_agent' => substr((string) ($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255),
]);
$requestId = (int) Database::pdo()->lastInsertId();
$this->sendNotification($requestId, $customerName, $phone, $email, $fullRequestText);
} catch (Throwable $exception) {
$this->redirect('error');
}
$this->redirect('sent');
}
/**
* @return array<int, string>
*/
private function storeUploadedFiles(): array
{
if (!isset($_FILES['wishlist_files']) || !is_array($_FILES['wishlist_files']['name'])) {
return [];
}
$stored = [];
$uploadDir = base_path('storage/wishlist_requests/' . date('Y/m'));
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0775, true);
}
foreach ($_FILES['wishlist_files']['name'] as $index => $originalName) {
$error = (int) ($_FILES['wishlist_files']['error'][$index] ?? UPLOAD_ERR_NO_FILE);
if ($error === UPLOAD_ERR_NO_FILE || $error !== UPLOAD_ERR_OK) {
continue;
}
$tmpName = (string) ($_FILES['wishlist_files']['tmp_name'][$index] ?? '');
if ($tmpName === '' || !is_uploaded_file($tmpName)) {
continue;
}
$extension = strtolower(pathinfo((string) $originalName, PATHINFO_EXTENSION));
$safeExtension = preg_replace('/[^a-z0-9]/', '', $extension) ?: 'file';
$fileName = date('Ymd-His') . '-' . bin2hex(random_bytes(4)) . '.' . $safeExtension;
$targetPath = $uploadDir . '/' . $fileName;
if (move_uploaded_file($tmpName, $targetPath)) {
$stored[] = str_replace('\\', '/', substr($targetPath, strlen(base_path()) + 1));
}
}
return $stored;
}
/**
* @param array<int, string> $uploadedFiles
*/
private function buildRequestText(string $requestText, array $uploadedFiles): string
{
$lines = [
'Тип заявки: поиск позиции / виш-лист.',
'',
'Что нужно найти:',
$requestText,
];
if ($uploadedFiles !== []) {
$lines[] = '';
$lines[] = 'Файлы:';
foreach ($uploadedFiles as $path) {
$lines[] = '- ' . $path;
}
}
return implode("\n", $lines);
}
private function sendNotification(int $requestId, string $customerName, string $phone, string $email, string $requestText): void
{
$settings = (new SettingRepository())->allKeyed();
$to = trim((string) (($settings['price_request_email'] ?? '') ?: ($settings['admin_email'] ?? '') ?: 'zakaz@rybstock.ru'));
if ($to === '') {
return;
}
$subject = 'Запрос позиции #' . $requestId . ' - ' . $customerName;
$body = implode("\n", [
'Новый запрос на поиск позиции.',
'',
'Номер заявки: ' . $requestId,
'Имя: ' . $customerName,
'Телефон: ' . ($phone !== '' ? $phone : 'не указан'),
'Email: ' . ($email !== '' ? $email : 'не указан'),
'',
$requestText,
]);
$headers = [
'Content-Type: text/plain; charset=UTF-8',
'From: Рыбсток <no-reply@rybstock.ru>',
];
@mail($to, $subject, $body, implode("\r\n", $headers));
}
private function redirect(string $status): never
{
header('Location: /?wishlist_request=' . rawurlencode($status) . '#wishlist-request', true, 303);
exit;
}
}