Files
2025-08-02 16:30:27 +02:00

508 lines
16 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\CatalogElasticBundle\View;
use KupShop\CatalogBundle\CategorySort;
use KupShop\CatalogBundle\Entity\Producer;
use KupShop\CatalogBundle\Entity\Section;
use KupShop\CatalogBundle\Enum\Ordering;
use KupShop\CatalogBundle\ProductList\DynamicFilterAttributes;
use KupShop\CatalogBundle\Repository\ProducersRepository;
use KupShop\CatalogBundle\Repository\SectionsRepository;
use KupShop\CatalogBundle\Util\FilterUtil;
use KupShop\CatalogBundle\Util\IndexedFilterUtil;
use KupShop\CatalogBundle\Util\SectionUtil;
use KupShop\CatalogBundle\Util\SectionViewUtil;
use KupShop\CatalogBundle\View\CategoryRequestHandler;
use KupShop\ComponentsBundle\Twig\DataProvider\SectionDataProvider;
use KupShop\ComponentsBundle\View\ComponentsViewInterface;
use KupShop\ComponentsBundle\View\ComponentsViewTrait;
use KupShop\IndexedFilterBundle\Util\FilterUrlGenerator;
use KupShop\KupShopBundle\Util\StringUtil;
use KupShop\KupShopBundle\Views\View;
use Query\Operator;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class SectionView extends View implements CategoryRequestHandler, ComponentsViewInterface
{
use ComponentsViewTrait;
protected $template = 'view/section.html.twig';
protected string $entrypoint = 'section';
protected ?int $categoryId = null;
protected ?int $producerId = null;
protected ?int $parameterValueId = null;
protected ?array $filterData = null;
protected \FilterParams $filterParams;
protected ?array $indexedFilterData = null;
protected Ordering $ordering;
protected int $page = 1;
private Section $section; // use getSection() to get the section
protected ?Producer $producer = null;
protected array $indexed_filter;
protected DynamicFilterAttributes $dynamicFilterAttributes;
private array $breadcrumbs;
public function __construct(
protected readonly IndexedFilterUtil $indexedFilterUtil,
protected readonly SectionViewUtil $categoryUtil,
protected readonly SectionDataProvider $sectionDataProvider,
protected readonly ProducersRepository $producersRepository,
protected readonly SectionsRepository $sectionsRepository,
protected readonly FilterUrlGenerator $filterUrlGenerator,
protected readonly SectionUtil $sectionUtil,
) {
$this->proxyCacheEnabled = findModule(\Modules::PROXY_CACHE, 'category');
}
public function getBodyVariables(): array
{
$body = parent::getBodyVariables();
$body['id_category'] = $this->categoryId;
$body['id_producer'] = $this->producerId;
$body['baseFilterParams'] = $this->createBaseFilterParams();
$body['filterParams'] = $this->filterParams = $this->createFilterParams();
$body['indexedFilter'] = $this->getIndexedFilter($this->filterParams);
$body['page'] = $this->page;
$body['ordering'] = $this->getOrdering();
$body['categorySort'] = match ($this->categoryId) {
null => null,
default => CategorySort::fromCategory($this->getSection(), $this->producerId),
};
$body['dynamicFilterConfiguration'] = $this->getDynamicFilterConfiguration();
$body['fetchAllFilters'] = $this->getFetchAllFilters();
return $body;
}
public function setProducerId($producerId)
{
$this->producerId = (int) $producerId;
$this->producer = $this->producersRepository->getProducerEntity($this->producerId);
if (!$this->producer) {
throw new NotFoundHttpException("Producer {$producerId} not found!");
}
$this->sectionDataProvider->addProducer($this->producer);
}
public function setParameterValueId($parameterValueId)
{
$this->parameterValueId = $parameterValueId;
}
public function setCategoryId($categoryId)
{
$this->categoryId = (int) $categoryId;
}
protected function getSection()
{
if (isset($this->section)) {
return $this->section;
}
// Fetch with parents - for breadcrumb navigation
$section = $this->sectionsRepository->findById($this->categoryId);
if (!$section) {
throw new NotFoundHttpException("Section {$this->categoryId} not found");
}
$this->sectionDataProvider->addSection($section);
return $this->section = $section;
}
public function getCorrectUrl(): ?string
{
if (!$this->categoryId && !$this->producerId) {
return path('kupshop_catalog_catalog_category');
}
$path = [];
if ($this->producerId) {
$producerPath = path('kupshop_catalog_catalog_producer', [
'id_producer' => $this->producerId,
'url_producer' => StringUtil::slugify($this->producer->getName()),
]);
$path[] = trim($producerPath, '/');
}
if ($this->categoryId > 0) {
$section = $this->getSection();
$sectionPath = ($this->producerId > 0 ? $section->getAutogeneratedUrl() : $section->getUrl());
$path[] = trim($sectionPath, '/');
}
// (LegacyUrlGenerator) automaticky přidá locale prefix
return path('page', ['path' => implode('/', $path).'/']);
}
protected function getCurrentUrl(Request $request): string
{
$currentUrl = parent::getCurrentUrl($request);
return $this->sectionUtil->removeFilterFromSectionUrl($request, $currentUrl);
}
public function getBreadcrumbsNew(): array
{
if (isset($this->breadcrumbs)) {
return $this->breadcrumbs;
}
$producer = $this->producer;
$section = $this->getSection();
$catProducer = $producer?->getId() ?? '';
$catProducerTitle = $producer?->getName() ?? '';
if ($section->getId() > 0) {
$breadcrumbs = [];
foreach ($section->getParents() as $parent) {
if ($parent->getId() === $section->getId()) {
// add active section
$breadcrumbs[] = [
'text' => $section->getNameShort() ?: $section->getName(),
'ID' => $parent->getId(),
'link' => path('page', ['path' => $section->getUrl()]),
];
continue;
}
$breadcrumbs[] = [
'text' => $parent->getNameShort() ?: $parent->getName(),
'link' => path('page', ['path' => $parent->getUrl()]),
'ID' => $parent->getId(),
];
}
if (!empty($catProducerTitle)) {
$home = sprintf(translate('getSections', 'functions')['catalogProducer'], $catProducerTitle);
array_unshift($breadcrumbs, [
'text' => $home,
'link' => createScriptURL([
'URL' => 'launch.php',
's' => 'category',
'IDpd' => $catProducer,
'producerTitle' => $catProducerTitle,
'TITLE' => $home,
]),
'ID' => '0',
]);
}
array_unshift($breadcrumbs, [
'text' => translate('getSections', 'functions')['home'],
'link' => createScriptURL([
'URL' => 'launch.php',
's' => 'index',
]),
]);
return $this->breadcrumbs = $breadcrumbs;
}
$breadcrumbs = [];
if ($catProducer) {
$home = sprintf(translate('getSections', 'functions')['catalogProducer'], $catProducerTitle);
} else {
$home = translate('getSections', 'functions')['catalog'];
}
if (!empty($home)) {
array_unshift($breadcrumbs, [
'text' => $home,
]);
}
array_unshift($breadcrumbs, [
'text' => translate('getSections', 'functions')['home'],
'link' => createScriptURL([
'URL' => 'launch.php',
's' => 'index',
]),
]);
return $breadcrumbs;
}
public function getWpjToolbar()
{
$wpjToolbar = [];
if ($this->categoryId > 0) {
$params = ['ID' => $this->categoryId];
if ($this->producerId > 0) {
$params['id_producer'] = $this->producerId;
}
$wpjToolbar = [
'url' => getAdminUrl('sections', $params),
'title' => 'Upravit sekci',
'translateUrl' => getAdminUrl('list', [
'type' => 'translateSections',
'iframe' => 1,
'objectIDs' => $this->categoryId,
]),
'translateTitle' => 'Překlad sekce',
];
} elseif ($this->producerId > 0) {
$wpjToolbar = [
'url' => getAdminUrl('producers', ['ID' => $this->producerId]),
'title' => 'Upravit výrobce',
'translateUrl' => getAdminUrl('list', [
'type' => 'translateProducers',
'iframe' => 1,
'objectIDs' => $this->producerId,
]),
'translateTitle' => 'Překlad výrobce',
];
}
$wpjToolbar['indexed_filter'] = $this->getIndexedFilterAdminUrl();
return array_merge(parent::getWpjToolbar(), $wpjToolbar);
}
public function getIndexedFilterAdminUrl()
{
$url = false;
if ($indexed_filter = $this->indexed_filter ?? null) {
if (!empty($indexed_filter['id'])) {
$params = ['ID' => $indexed_filter['id']];
} else {
$params = $indexed_filter + ['title' => $this->getTitle()];
}
$url = getAdminUrl('IndexedFiltersContent', $params);
}
return $url;
}
protected function getIndexedFilter(\FilterParams $filterParams)
{
if (!isset($this->indexed_filter)) {
$this->indexed_filter = $this->indexedFilterUtil->getIndexedFilter(
$this->categoryId,
$this->producerId,
$filterParams,
) ?: [];
}
return $this->indexed_filter;
}
public function getMetaTitle()
{
if (!empty($this->indexed_filter['meta_title'])) {
return $this->indexed_filter['meta_title'];
}
return $this->categoryUtil->createTitle(
$this->getSection(),
$this->producer,
$this->createFilterParams(),
$this->getDynamicFilterConfiguration(),
);
}
public function getTitle(): string
{
return $this->getMetaTitle();
}
public function getMetaDescription(): string
{
if (!empty($this->indexed_filter['meta_description'])) {
return $this->indexed_filter['meta_description'];
}
return $this->categoryUtil->createMetaDescription($this->getSection(), $this->producer);
}
public function createFilterParams(): \FilterParams
{
$filterParams = $this->createBaseFilterParams();
$filterData = FilterUtil::mergeIndexedAndDynamicFilterData(
$this->getDynamicFilterConfiguration(),
$this->filterData ?? [],
$this->indexedFilterData ?? [],
);
if (!empty($filterData)) {
$filter = new \Filter($filterParams);
$filter->setFilterData($filterData);
}
return $filterParams;
}
private function createBaseFilterParams(): \FilterParams
{
$filterParams = \FilterParams::createDefault();
if ($this->categoryId !== null) {
if ($this->categoryId === -1) {
$filterParams->setSearch($this->filterData['search'] ?? '');
}
if ($this->categoryId === 0 || $this->getSection()->displayProductsFromSubsections()) {
$filterParams->setSectionsFilter([$this->categoryId]);
} else {
$filterParams->setSections([$this->categoryId]);
}
}
if ($this->producerId) {
$filterParams->setProducers([$this->producerId]);
}
if ($this->parameterValueId) {
$parameterId = sqlQueryBuilder()
->select('id_parameter')
->from('parameters_list')
->andWhere(Operator::equals(['id' => $this->parameterValueId]))
->execute()
->fetchOne();
$filterParams->setParameter($parameterId, [$this->parameterValueId]);
}
return $filterParams;
}
private function getOrdering(): ?Ordering
{
if (!isset($this->ordering)) {
if (isset($this->categoryId)) {
return null;
}
if ($this->producer?->getOrderby()) {
return $this->ordering = new Ordering($this->producer->getOrderby(), $this->producer->getOrderdir());
}
}
return $this->ordering;
}
public function setOrdering(Ordering $ordering): void
{
$this->ordering = $ordering;
}
public function setPage(int $page): void
{
$this->page = $page;
}
public function setFilterData(array $filterData): self
{
$this->filterData = $filterData;
return $this;
}
public function setParsedURLData($indexedFilterData)
{
$this->indexedFilterData = $indexedFilterData;
}
public function getDynamicFilterConfiguration(): DynamicFilterAttributes
{
return $this->dynamicFilterAttributes ??= $this->categoryUtil->getDynamicFilterConfiguration($this->getSection(), $this->producerId);
}
protected function getFetchAllFilters(): bool
{
$allFilters = FilterUtil::mergeIndexedAndDynamicFilterData(
$this->getDynamicFilterConfiguration(),
$this->filterData ?? [],
$this->indexedFilterData ?? [],
);
return !empty($allFilters);
}
public function setCampaign($campaign)
{
throw new NotFoundHttpException('Campaigns not supported!');
}
public function getCanonicalUrl(Request $request)
{
if ($this->producerId) {
$this->filterUrlGenerator->setIgnoreProducer(true);
}
if ($this->categoryId <= 0) {
$path = $request->getPathInfo();
} else {
$this->filterUrlGenerator->setSectionId($this->categoryId);
$this->filterUrlGenerator->setFilterParams($this->filterParams);
$this->filterUrlGenerator->setCategoryUrl($this->getCorrectUrl());
$indexedUrl = $this->filterUrlGenerator->generateIndexedUrl();
$path = $request->getSchemeAndHttpHost().$indexedUrl['url'];
}
// Hack? Maybe no ...
if (!$this->filterData && $page = $request->query->get('page')) {
$page = intval($page);
if ($page > 1) {
$path .= '?page='.$page;
}
}
return $path;
}
public function getCategoryId(): ?int
{
return $this->categoryId;
}
public function getProducerId(): ?int
{
return $this->producerId;
}
public function getTemplate(): string
{
return $this->getSection()->getTemplate() ?: $this->template;
}
public function getTemplates(): iterable
{
// List all shop templates in section
if (is_dir(__DIR__.'/../../../../../shop/twig/view/section')) {
foreach (new \FilesystemIterator(__DIR__.'/../../../../../shop/twig/view/section') as $file) {
yield 'view/section/'.$file->getFilename();
}
}
yield from [
$this->template,
'view/search.html.twig',
'block.productList.html.twig',
];
}
}