<?php
namespace App\Controller\Catalog;
use App\Controller\BaseController;
use Doctrine\DBAL\Connection;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class CatalogController extends BaseController
{
/**
* @Route("/catalog", name="catalog", methods={"GET"})
*/
public function catalog(Request $request, Connection $db, CatalogRepository $catalog): Response
{
$activeCategory = $this->normalizeCatalogCategory((string) $request->query->get('category', 'all'));
$query = trim((string) $request->query->get('q', ''));
$filters = $this->catalogRequestFilters($request);
$result = $catalog->listCatalogProducts(24, 0, $query, $activeCategory === 'all' ? '' : $activeCategory, $filters);
$officialProducts = $catalog->officialProducts(10);
return $this->render('catalog.html.twig', [
'user' => $this->publicUser($this->getAuthorizedUser($request, $db)),
'catalog_products' => $result['products'],
'catalog_payload_json' => $catalog->payloadJson($result['products'], $result['total'], [
'official_products' => $officialProducts,
'has_more' => $result['has_more'],
'next_offset' => $result['next_offset'],
'limit' => $result['limit'],
]),
'catalog_official_products' => $officialProducts,
'catalog_total' => $result['total'],
'catalog_category_filters' => $this->catalogCategoryFilters($request, $activeCategory),
'catalog_type_filters' => $this->catalogTypeFilters(),
'catalog_genre_filters' => $catalog->popularGenreOptions(),
'catalog_active_category' => $activeCategory,
'catalog_query' => $query,
'catalog_active_sort' => $filters['sort'],
]);
}
/**
* @Route("/catalog/items", name="catalog_items", methods={"GET"})
*/
public function catalogItems(Request $request, CatalogRepository $catalog): JsonResponse
{
$activeCategory = $this->normalizeCatalogCategory((string) $request->query->get('category', 'all'));
$query = trim((string) $request->query->get('q', ''));
$limit = max(1, min(80, (int) $request->query->get('limit', 24)));
$offset = max(0, (int) $request->query->get('offset', 0));
$result = $catalog->listCatalogProducts(
$limit,
$offset,
$query,
$activeCategory === 'all' ? '' : $activeCategory,
$this->catalogRequestFilters($request)
);
$response = new JsonResponse($result);
$response->setEncodingOptions(JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
return $response;
}
/**
* @Route("/product", name="product", methods={"GET"})
*/
public function product(Request $request, Connection $db, CatalogRepository $catalog): Response
{
$slug = trim((string) $request->query->get('slug', $request->query->get('id', '')));
$product = $slug !== '' ? $catalog->findProduct($slug) : null;
if ($slug !== '' && !$product) {
throw $this->createNotFoundException('Товар не найден или сейчас не в наличии.');
}
return $this->render('product.html.twig', [
'user' => $this->publicUser($this->getAuthorizedUser($request, $db)),
'product' => $product,
]);
}
private function catalogCategoryFilters(Request $request, string $activeCategory): array
{
$definitions = [
['id' => 'all', 'label' => 'Все', 'icon' => 'catalog.svg'],
['id' => 'donate', 'label' => 'Донат', 'icon' => 'cat-donate.svg'],
['id' => 'subscriptions', 'label' => 'Подписки', 'icon' => 'cat-subscribes.svg'],
['id' => 'items', 'label' => 'Предметы', 'icon' => 'cat-items.svg'],
['id' => 'accounts', 'label' => 'Аккаунты', 'icon' => 'cat-accounts.svg'],
['id' => 'keys', 'label' => 'Ключи', 'icon' => 'cat-keys.svg'],
['id' => 'steam_gift', 'label' => 'Steam Gift', 'icon' => 'cat-currency.svg'],
['id' => 'other', 'label' => 'Другое', 'icon' => 'cat-other.svg'],
['id' => 'services', 'label' => 'Услуги', 'icon' => 'cat-services.svg'],
];
$query = $request->query->all();
unset($query['page']);
return array_map(function (array $definition) use ($query, $activeCategory): array {
$urlQuery = $query;
if ($definition['id'] === 'all') {
unset($urlQuery['category']);
} else {
$urlQuery['category'] = $definition['id'];
}
$definition['active'] = $definition['id'] === $activeCategory;
$definition['url'] = $this->generateUrl('catalog', $urlQuery);
return $definition;
}, $definitions);
}
private function catalogTypeFilters(): array
{
return [
['id' => 'steam_gift', 'label' => 'Steam Gift'],
['id' => 'keys', 'label' => 'Ключ Steam'],
['id' => 'donate', 'label' => 'Донат'],
['id' => 'subscriptions', 'label' => 'Подписки'],
['id' => 'items', 'label' => 'Предметы'],
['id' => 'accounts', 'label' => 'Аккаунты'],
['id' => 'other', 'label' => 'Другое'],
];
}
private function catalogRequestFilters(Request $request): array
{
return [
'types' => $this->requestValueList($request, 'type'),
'genres' => $this->requestValueList($request, 'genre'),
'min_price' => trim((string) $request->query->get('min_price', '')),
'max_price' => trim((string) $request->query->get('max_price', '')),
'sort' => $this->normalizeCatalogSort((string) $request->query->get('sort', 'popular')),
];
}
private function requestValueList(Request $request, string $key): array
{
$value = $request->query->get($key, '');
if (is_array($value)) {
$items = $value;
} else {
$items = preg_split('/[,\s]+/', (string) $value) ?: [];
}
return array_values(array_filter(array_unique(array_map(function ($item): string {
return strtolower(trim((string) $item));
}, $items)), function (string $item): bool {
return $item !== '';
}));
}
private function normalizeCatalogSort(string $sort): string
{
$sort = strtolower(trim($sort));
$allowed = ['popular', 'price_asc', 'price_desc', 'new', 'rating'];
return in_array($sort, $allowed, true) ? $sort : 'popular';
}
private function normalizeCatalogCategory(string $category): string
{
$category = strtolower(trim($category));
$allowed = ['all', 'donate', 'subscriptions', 'items', 'accounts', 'keys', 'steam_gift', 'other', 'services'];
return in_array($category, $allowed, true) ? $category : 'all';
}
}