Files
kupshop/bundles/KupShop/RecommendersBundle/Twig/Components/Recommender.php
2025-08-02 16:30:27 +02:00

234 lines
6.5 KiB
PHP

<?php
declare(strict_types=1);
namespace KupShop\RecommendersBundle\Twig\Components;
use KupShop\CatalogBundle\ProductList\ProductCollection;
use KupShop\ComponentsBundle\Attributes\Component;
use KupShop\ComponentsBundle\Attributes\Version;
use KupShop\ComponentsBundle\GTM\PostMount\GTMPlaceholdersData;
use KupShop\ComponentsBundle\Interfaces\Microdata;
use KupShop\ComponentsBundle\Twig\BaseComponent;
use KupShop\ComponentsBundle\Twig\DataProvider\LiveComponentRenderTrait;
use KupShop\ComponentsBundle\Twig\DataProvider\MicrodataProvider;
use KupShop\KupShopBundle\Context\CacheContext;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\StringUtil;
use KupShop\RecommendersBundle\Util\RecommendersUtil;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Service\Attribute\Required;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\Attribute\LiveAction;
use Symfony\UX\LiveComponent\Attribute\LiveListener;
use Symfony\UX\LiveComponent\Attribute\LiveProp;
use Symfony\UX\LiveComponent\DefaultActionTrait;
use Symfony\UX\TwigComponent\Attribute\PostMount;
use Symfony\UX\TwigComponent\Attribute\PreMount;
#[AsLiveComponent(template: '@Recommenders/components/Recommender/Recommender.1.html.twig', method: 'get')]
#[Component(1, [
new Version(1),
])]
class Recommender extends BaseComponent implements Microdata
{
use DefaultActionTrait;
use LiveComponentRenderTrait;
use GTMPlaceholdersData;
#[Required]
public \KupShop\ComponentsBundle\GTM\Events\Products $gtmClass;
#[LiveProp]
public bool $isCarousel = true;
#[LiveProp]
public bool $isSquareItemCarousel = false;
#[LiveProp]
public bool $showBtn = false;
#[LiveProp]
public ?int $limit = null;
#[LiveProp]
public ?int $defaultLimit = null;
#[LiveProp]
public bool $isHidden = true;
#[LiveProp]
public string $toggleText;
#[LiveProp]
public string $label;
#[LiveProp(writable: true)]
public array $data = [];
#[LiveProp]
public string $title = '';
#[LiveProp(writable: true)]
public bool $lazy = true;
#[LiveProp]
public bool $container = true;
#[LiveProp]
public string $title_class = 'h4';
#[LiveProp]
public string $additional_class = '';
#[LiveProp]
public bool $useOuterscopeIdentifier = true;
protected array $recommender;
public ?string $propsCacheKey = null;
protected ProductCollection $productList;
public function __construct(protected RecommendersUtil $recommendersUtil, private readonly TranslatorInterface $translator)
{
$this->toggleText = $this->translator->trans('recommender.show', [], 'products');
}
#[PostMount]
public function postMount(): void
{
if ($this->limit) {
$this->defaultLimit = $this->limit;
}
}
public function __invoke(Request $request): ?Response
{
$cache = $this->getRecommenderTTL();
if ($cache > 0) {
return $this->createResponse($request, $cache);
}
return null;
}
#[PreMount]
public function preMount(array $data): array
{
if ($id_product = ($data['id_product'] ?? null)) {
$data['data']['product'] = $id_product;
unset($data['id_product']);
}
if ($id_category = ($data['id_category'] ?? null)) {
$data['data']['section'] = (array) $id_category;
unset($data['id_category']);
}
$data['propsCacheKey'] ??= serialize($data);
return $data;
}
public function getRecommender(): array
{
if (!isset($this->recommender)) {
$this->recommender = $this->recommendersUtil->getRecommenderByLabel($this->label) ?: [];
}
return $this->recommender;
}
public function getDataRecommender()
{
return json_encode([
'label' => $this->label,
'data' => $this->data,
]);
}
public function getCount(): ?int
{
if ($this->lazy) {
return null;
}
return $this->getProductList()->count();
}
public function getProductList(): ?ProductCollection
{
if ($this->lazy) {
return null;
}
if (!isset($this->productList)) {
$data = ['label' => $this->label, 'data' => $this->data];
$this->productList = $this->recommendersUtil->getRecommendersProducts($data);
}
return $this->productList;
}
#[LiveListener('recommenderLoad')]
public function load()
{
$this->lazy = false;
}
public function initMicrodata(): array
{
// Aby to mělo svůj scope
return [MicrodataProvider::RECOMMENDER => []];
}
#[LiveAction]
public function toggleAction(): void
{
if ($this->isHidden) {
$this->toggleText = $this->translator->trans('recommender.hide', [], 'products');
$this->isHidden = false;
$this->limit = 100;
} else {
$this->toggleText = $this->translator->trans('recommender.show', [], 'products');
$this->isHidden = true;
$this->limit = $this->defaultLimit;
}
}
public function getGTMPlaceholders(): array
{
return ['item_list_name' => 'recommender', 'item_list_id' => $this->getRecommender()['id'] ?? ''];
}
public function getTrackingData(): string
{
if ($this->getProductList()) {
return htmlentities(json_encode($this->gtmClass->getPushData($this), JSON_NUMERIC_CHECK | JSON_HEX_APOS));
}
return '';
}
private function getRecommenderTTL(): int
{
$data = json_decode($this->getRecommender()['data'] ?? '', true);
return intval($data['cache'] ?? 3600);
}
public function getCacheKey(): string
{
if ($this->lazy || !$this->propsCacheKey) {
return '_skip';
}
$cacheKey = [
Contexts::get(CacheContext::class)->getKey([
CacheContext::TYPE_TEXT,
CacheContext::TYPE_PRICE,
CacheContext::TYPE_AVAILABILITY,
]),
static::class,
$this->version,
$this->propsCacheKey,
];
return StringUtil::slugify(implode('_', $cacheKey));
}
public function getCacheTTL(): int
{
return $this->lazy || !$this->propsCacheKey ? 0 : $this->getRecommenderTTL();
}
}