Files
kupshop/bundles/KupShop/CatalogBundle/Util/SectionViewUtil.php
2025-08-02 16:30:27 +02:00

444 lines
17 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\CatalogBundle\Util;
use KupShop\CatalogBundle\Entity\Category;
use KupShop\CatalogBundle\Entity\Producer;
use KupShop\CatalogBundle\Entity\Section;
use KupShop\CatalogBundle\ProductList\DynamicFilterAttributes;
use KupShop\CatalogBundle\Repository\SectionsRepository;
use KupShop\I18nBundle\Translations\BlocksTranslation;
use KupShop\I18nBundle\Translations\ParametersListTranslation;
use KupShop\I18nBundle\Translations\ProducersTranslation;
use KupShop\I18nBundle\Translations\VariationsValuesTranslation;
use KupShop\KupShopBundle\Config;
use KupShop\KupShopBundle\Util\StringUtil;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class SectionViewUtil
{
public function __construct(
protected readonly ActiveCategory $activeCategory,
protected readonly IndexedFilterUtil $indexedFilterUtil,
protected readonly FilterUtil $filterUtil,
) {
}
public function getListShowType(): mixed
{
$show = \Settings::getDefault()->cat_show_style;
if (!empty($_COOKIE['cat_show'])) {
$show = $_COOKIE['cat_show'];
}
if (!empty($_GET['show']) && preg_match('/^[[:digit:]]{1}$/i', $_GET['show'])) {
$show = $_GET['show'];
SetCookies('cat_show', $show);
}
return $show;
}
/**
* @return array{noOnPage: int, page: int}
*/
public function getPagerOptions(Request $request): array
{
$dbcfg = \Settings::getDefault();
$noOnPage = $dbcfg['cat_show_products'];
if (is_numeric(getVal('filter_onpage'))) {
$noOnPage = getVal('filter_onpage');
SetCookies('filter_onpage', $noOnPage);
} elseif (!empty($_COOKIE['filter_onpage'])) {
$noOnPage = intval($_COOKIE['filter_onpage']);
}
$page = (int) max(intval($request->get('page', default: 1)), 1);
$noOnPage = (int) $noOnPage;
return compact('noOnPage', 'page');
}
/**
* @deprecated use {@see SectionsRepository}
*/
public function getCategory(int|array $categoryId, ?int $campaign = null): Category
{
$cfg = Config::get();
global $catPath;
$catPath = [];
if (is_array($categoryId)) {
$catPath = array_unique($categoryId);
$categoryId = $catPath[0];
$catPath = array_reverse($catPath);
}
$categoryId = (string) $categoryId;
if (!empty($categoryId)) {
$cat = $this->activeCategory->getSectionById($categoryId);
if (!$cat) {
throw new NotFoundHttpException('Section not found!');
}
if (($cat->getFigure() == 'N') && !getAdminUser()) {
// skryta sekce - 404 (pro admina nezobrazovat 404, pokud je sekce skryta)
throw new NotFoundHttpException('Section not found!');
}
}
$this->activeCategory->setCategoryId($categoryId);
if (!is_array($categoryId) && empty($catPath)) {
$catPath = $this->activeCategory->getOpenedTreePath();
}
$this->activeCategory->validate([$categoryId]);
$category = null;
if (preg_match("/^-?[[:digit:]]+({$cfg['Products']['UrlFlags']})?$/i", $categoryId)) {
if (!is_numeric($categoryId)) {
$campaign = substr($categoryId, -1);
$categoryId = substr($categoryId, 0, -1);
}
if ($categoryId >= 0) {
// Tohle je demence, ale Honza tvrdí, že to je třeba kvůli Seznam remarketingu, bohužel :-(
$cqb = sqlQueryBuilder()
->select('s.*, fs.category_text as feed_seznam_category')
->from('sections', 's')
->leftJoin('s', 'kupshop_shared.feed_seznam', 'fs', 's.feed_seznam = fs.id')
->where(Operator::equals(['s.id' => $categoryId]));
$cqb->andWhere(
Translation::coalesceTranslatedFields(
\KupShop\I18nBundle\Translations\SectionsTranslation::class,
function ($columns) {
$columns['data'] = 'data_translated';
return $columns;
}
)
);
$category = $cqb->execute()
->fetch();
if ($category) {
$category['param'] = '';
$category['parents'] = $catPath;
$sectionData = array_replace_recursive(json_decode($category['data'] ?? '{}', true) ?: [], json_decode($category['data_translated'] ?? '{}', true) ?: []);
$category['data'] = $sectionData;
$category['date_updated'] = $category['date_updated'] ? new \DateTime($category['date_updated']) : null;
} else {
throw new NotFoundHttpException('Section not found!');
}
} else {
$category = [
'id' => 0,
'name' => '',
'descr' => '',
'behaviour' => 0,
'param' => '',
'lead_figure' => false,
'lead_text' => '',
'lead_products' => '',
'orderby' => 'title',
'orderdir' => 'ASC',
'producers_filter' => true,
'producers_to_title' => true,
'producers_indexing' => false,
];
}
$category['name_orig'] = $category['name'];
$category['campaign'] = $campaign;
$category = new Category($category);
} else {
redirection('REFERER');
}
return $category;
}
public function getDynamicFilterConfiguration(Section $section, ?int $producerId): DynamicFilterAttributes
{
if (FilterUtil::useOrderableFilters()) {
$sectionFilters = $this->filterUtil->getSectionFilters(
$section,
$producerId,
);
return DynamicFilterAttributes::fromOrderedFilters($sectionFilters);
}
$parameters = [];
if (findModule('producers') || findModule('products_sections')) {
$qb = sqlQueryBuilder()
->select('pa.*')
->from('parameters', 'pa')
->leftJoin('pa', 'parameters_sections', 'ps', 'ps.id_section=:id_section AND ps.id_parameter=pa.id')
->setParameter('id_section', $section->getId())
->where(Operator::orX(
findModule('products_sections') ? Operator::andX(\Query\Parameter::inSections([$section->getId()]), Operator::equals(['ps.filter' => 'Y'])) : null,
findModule('producers') ? \Query\Parameter::inProducers([$producerId]) : null))
->groupBy('pa.id')
->orderBy('ps.weight')
->addOrderBy('pa.position');
if (findModule(\Modules::INDEXED_FILTER)) {
$qb->addSelect('ps.to_title, ps.indexing');
}
$qb->andWhere(Translation::coalesceTranslatedFields(\KupShop\I18nBundle\Translations\ParametersTranslation::class, ['name', 'descr']));
$parameters = sqlFetchAll($qb->execute(), 'id');
}
$variations = [];
if (findModule('products_variations')) {
$qb = sqlQueryBuilder()
->select('pvcl.id, pvcl.label as name')
->from('products_variations_choices_labels', 'pvcl')
->where(\Query\Variation::labelsInSections([$section->getId()]))
->orderBy('pvs.weight');
if (findModule(\Modules::INDEXED_FILTER)) {
$qb->addSelect('to_title, indexing');
}
if (findModule(\Modules::CONVERTORS)) {
$qb->addSelect('convertor');
}
$qb->andWhere(Translation::coalesceTranslatedFields(\KupShop\I18nBundle\Translations\VariationsLabelsTranslation::class, ['label' => 'name']));
$variations = sqlFetchAll($qb->execute(), 'id');
}
$producers = true;
if (findModule(\Modules::PRODUCERS)) {
$producers = $section->getProducersFilter() == 'Y';
if ($producers && findModule(\Modules::INDEXED_FILTER)) {
$producers = [
'to_title' => $section->getProducersToTitle() === 'Y',
'indexing' => $section->getProducersIndexing() === 'Y',
];
}
}
$labels = findModule(\Modules::LABELS) && $section->getLabelsFilter() === 'Y';
return new DynamicFilterAttributes(
parameters: $parameters,
variations: $variations,
producers: $producers,
labels: $labels,
);
}
public function getTitleParts(?Section $section, ?Producer $producer, bool $useSeo = true): array
{
$parts = [];
if ($producer) {
if ($useSeo && $producerMeta = $producer->getMetaTitle()) {
$parts['producer'] = $producerMeta;
} elseif ($producerName = $producer->getName()) {
$parts['producer'] = $producerName;
}
}
if ($section) {
if ($section->getId() !== 0 || empty($parts['producer'])) {
if ($useSeo && $categoryMetaTitle = $section->getMetaTitle()) {
$parts = array_merge(['category' => $categoryMetaTitle], $parts);
} elseif ($categoryName = $section->getName()) {
$parts = array_merge(['category' => $categoryName], $parts);
}
}
}
return $parts;
}
public function getTitle(?Section $section, ?Producer $producer, string $separator = ' - ', bool $useSeo = true): string
{
$parts = $this->getTitleParts($section, $producer, $useSeo);
return join($separator, array_filter($parts));
}
protected function getBlockContent(int $rootId): ?string
{
$block = sqlQueryBuilder()->from('blocks', 'b')
->where(Operator::equals(['id_root' => $rootId]))
->andWhere(Translation::coalesceTranslatedFields(BlocksTranslation::class, ['content']))
->execute()->fetchOne();
if ($block) {
return strip_tags($block);
}
return null;
}
public function getMetaDescriptionParts(?Section $section, ?Producer $producer): array
{
$parts = [];
if ($categoryMeta = $section?->getMetaDescription()) {
$parts['category'] = $categoryMeta;
}
if ($producerMeta = $producer?->getMetaDescription()) {
$parts['producer'] = $producerMeta;
}
if (!isset($parts['category']) && ($sectionBlock = $section?->getIdBlock())) {
if ($block = $this->getBlockContent($sectionBlock)) {
$parts['category_block'] = $block;
}
}
if (!isset($parts['producer']) && ($idProducerBlock = $producer?->getIdBlock())) {
if ($block = $this->getBlockContent($idProducerBlock)) {
$parts['producer_block'] = $block;
}
}
return $parts;
}
public function createMetaDescription(?Section $section, ?Producer $producer, string $separator = ', '): string
{
if ($parts = $this->getMetaDescriptionParts($section, $producer)) {
return StringUtil::cutToWords(join($separator, $parts), 160);
}
return $this->getTitle($section, $producer).' - '.\Settings::getDefault()->shop_description;
}
public function getDynamicTitleParts(DynamicFilterAttributes $dynamicFilter, \FilterParams $params, bool $includeProducer = true): array
{
if (!findModule(\Modules::INDEXED_FILTER)) {
return [];
}
$variationNames = [];
$parameterNames = [];
$producerNames = [];
// Variation names
foreach ($params->getVariations() as $labelId => $values) {
if (isset($dynamicFilter['variations'][$labelId])
&& $dynamicFilter['variations'][$labelId]['to_title'] == 'Y') {
foreach ($values as $value) {
$data = sqlQueryBuilder()->select('pvcv.id')
->from('products_variations_choices_values', 'pvcv')
->where(Operator::equals(['pvcv.id' => $value]))
->andWhere(Translation::coalesceTranslatedFields(VariationsValuesTranslation::class, ['value', 'title']))
->execute()->fetch();
if ($data) {
$variationNames[] = (!empty($data['title']) ? $data['title'] : $data['value']);
}
}
}
}
// Parameter names
foreach ($params->getParameters() as $id => $parameter) {
if (isset($dynamicFilter['parameters'][$id])
&& $dynamicFilter['parameters'][$id]['to_title'] == 'Y') {
if ($parameter['type'] == \Filter::PARAM_LIST) {
foreach ($parameter['values'] as $parameterListId) {
$data = sqlQueryBuilder()->select('pl.id')
->from('parameters_list', 'pl')->where(Operator::equals(['pl.id' => $parameterListId]))
->andWhere(Translation::coalesceTranslatedFields(ParametersListTranslation::class, ['value', 'title']))
->execute()->fetch();
if ($data) {
$parameterNames[$id][] = (!empty($data['title']) ? $data['title'] : $data['value']);
}
}
}
}
}
if ($includeProducer
&& ($dynamicFilter['producers'] ?? false)
&& ($dynamicFilter['producers']['to_title'] ?? false) == 'Y'
) {
foreach ($params->getProducers() as $producer) {
$data = sqlQueryBuilder()->select('pr.id')
->from('producers', 'pr')->where(Operator::equals(['pr.id' => $producer]))
->andWhere(Translation::joinTranslatedFields(ProducersTranslation::class, function (QueryBuilder $qb, $columnName, $translatedField) {}, ['name', 'title']))
->execute()->fetch();
if ($data) {
$producerNames[] = (!empty($data['title']) ? $data['title'] : $data['name']);
}
}
}
foreach ($params->getVariationsSizeConvertion() as $labelId => $convertor) {
if (($dynamicFilter['variations'][$labelId]['to_title'] ?? false) == 'Y') {
foreach ($convertor['value'] as $paramId) {
$data = sqlQueryBuilder()->select('pl.id')
->from('parameters_list', 'pl')->where(Operator::equals(['pl.id' => $paramId]))
->andWhere(Translation::coalesceTranslatedFields(ParametersListTranslation::class, ['value', 'title']))
->execute()->fetch();
if ($data) {
$variationNames[] = (!empty($data['title']) ? $data['title'] : $data['value']);
}
}
}
}
return compact('variationNames', 'parameterNames', 'producerNames');
}
public function createDynamicTitle(DynamicFilterAttributes $dynamicFilter, \FilterParams $params, bool $includeProducer = true): string
{
list(
'variationNames' => $variationNames,
'parameterNames' => $parameterNames,
'producerNames' => $producerNames,
) = $this->getDynamicTitleParts($dynamicFilter, $params, $includeProducer);
$parametersTitle = '';
foreach ($parameterNames as $values) {
$parametersTitle .= ' '.join(', ', $values);
}
$title = '';
$title .= !empty($producerNames) ? ' '.join(', ', $producerNames) : '';
$title .= $parametersTitle;
$title .= !empty($variationNames) ? ' '.join(', ', $variationNames) : '';
return $title;
}
public function createTitle(
?Section $section,
?Producer $producer,
\FilterParams $filterParams,
DynamicFilterAttributes $filterConfig,
bool $useSeo = true,
): string {
$categoryMeta = $this->getTitle($section, $producer, useSeo: $useSeo);
$categoryDynamic = $this->createDynamicTitle(
dynamicFilter: $filterConfig,
params: $filterParams,
includeProducer: $producer !== null,
);
return $categoryMeta.$categoryDynamic;
}
}