Files
kupshop/bundles/External/PompoBundle/Util/PompoGraphQLUpdater.php
2025-08-02 16:30:27 +02:00

444 lines
14 KiB
PHP

<?php
declare(strict_types=1);
namespace External\PompoBundle\Util;
use KupShop\CatalogBundle\ProductList\ProductList;
use KupShop\ContentBundle\Util\Block;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\KupShopBundle\Util\Functional\Mapping;
use KupShop\KupShopBundle\Util\StringUtil;
use Query\Operator;
use WpjShop\GraphQL\Client;
use WpjShop\GraphQL\DataObjects\Product\CollectionItem;
use WpjShop\GraphQL\DataObjects\Product\LinkItem;
use WpjShop\GraphQL\DataObjects\Product\ParameterValue;
use WpjShop\GraphQL\DataObjects\Product\ProductParameter;
use WpjShop\GraphQL\DataObjects\Product\RelatedItem;
use WpjShop\GraphQL\Enums\ProductVisibility;
/**
* Slouží pro komunikaci mezi `prod/pompo` a `prod/nejhracka`. Z pompa se na nejhracku přenáší informace o produktu.
*/
class PompoGraphQLUpdater
{
private const GRAPHQL_URL = 'https://www.nejhracka.cz/admin/graphql';
private const GRAPHQL_ACCESS_TOKEN = '201ac0b08889d173f99cfc7230f3f327';
private ?Client $client = null;
private ProductList $productList;
private Block $block;
private ContextManager $contextManager;
private array $parametersCache = [];
private array $sectionsListCache = [];
private array $producersListCache = [];
public function __construct(ProductList $productList, Block $block, ContextManager $contextManager)
{
$this->productList = $productList;
$this->block = $block;
$this->contextManager = $contextManager;
}
public function updateEditableContent(int $id, array $data): bool
{
return $this->getClient()->editableContent->update($id, $data)['result'] ?? false;
}
public function updateProduct(int $id, array $data): array
{
return $this->getClient()->product
->addSelection(['descriptionPlus' => ['id']])
->update($id, $data) ?? [];
}
public function updateProductLinks(int $externalProductId, \Product $product): void
{
$links = [];
foreach ($product->links ?? [] as $link) {
$links[] = new LinkItem(
$link['title'],
$link['link'],
$link['type']
);
}
$this->client->product->updateLinks($externalProductId, $links);
}
public function updateProductRelated(int $externalProductId, \Product $product): void
{
$relatedProducts = [];
foreach ($product->products_related ?? [] as $item) {
if (empty($item->code)) {
continue;
}
if ($relatedExternalId = $this->getProductIdByCode($item->code)) {
$relatedProducts[] = new RelatedItem($relatedExternalId);
}
}
$this->client->product->updateRelated($externalProductId, $relatedProducts);
}
public function updateProductCollection(int $externalProductId, \Product $product): void
{
$collectionProducts = [];
foreach ($product->collections['own'] ?? [] as $item) {
if (empty($item->code)) {
continue;
}
if ($relatedExternalId = $this->getProductIdByCode($item->code)) {
$collectionProducts[] = new CollectionItem($relatedExternalId);
}
}
$this->client->product->updateCollections($externalProductId, $collectionProducts);
}
public function updateProductTranslations(int $externalProductId, int $externalEditableContentId, string $language, \Product $product): void
{
$translation = sqlQueryBuilder()
->select('*')
->from('products_translations')
->where(Operator::equals(['id_product' => $product->id, 'id_language' => $language]))
->execute()->fetchAssociative();
$productData = [];
if (!empty($translation['title'])) {
$productData['title'] = $translation['title'];
}
if (!empty($translation['short_descr'])) {
$productData['description'] = $translation['short_descr'];
}
if (!empty($translation['long_descr'])) {
$productData['longDescription'] = $translation['long_descr'];
}
if (!empty($translation['figure'])) {
$productData['visibility'] = $this->getProductVisibility($translation['figure']);
}
if (!empty($translation['meta_title']) || !empty($translation['meta_description']) || !empty($translation['meta_keywords'])) {
$seoData = [];
if (!empty($translation['meta_title'])) {
$seoData['title'] = $translation['meta_title'];
}
if (!empty($translation['meta_description'])) {
$seoData['description'] = $translation['meta_description'];
}
if (!empty($translation['meta_keywords'])) {
$seoData['keywords'] = $translation['meta_keywords'];
}
$productData['seo'] = $seoData;
}
if (!empty($productData)) {
$this->getClient()->product->translate($externalProductId, $language, $productData);
}
// prelozit popis+
if (!empty($product->id_block)) {
$defaultBlocks = $this->getBlocks((int) $product->id_block);
$translatedBlocks = $this->getBlocks((int) $product->id_block, $language);
if (json_encode($defaultBlocks) !== json_encode($translatedBlocks)) {
$this->getClient()->editableContent->translate(
$externalEditableContentId,
$language,
$this->createEditableContentFromBlocks($translatedBlocks)
);
}
}
}
public function updateProductParameters(int $externalProductId, \Product $product): void
{
$parameterUpdates = [];
foreach ($this->getParameters() as $externalParameter) {
$parameterUpdates[$externalParameter['id']] = [];
}
foreach ($product->param as $parameter) {
// pokud nenajdu parametr, tak ho vytvorim
if (!($externalParameterId = ($this->getParameterByName($parameter['name'])['id'] ?? false))) {
// nejdriv rozhodnu typ parametru
switch ($parameter['value_type']) {
case 'float':
$type = 'NUMBER';
break;
case 'char':
$type = 'TEXT';
break;
default:
$type = 'LIST';
}
// vytvorim parametr
$externalParameterId = $this->createParameter($parameter['name'],
$type,
empty($parameter['unit']) ? null : $parameter['unit']
)['id'];
}
$parameterUpdates[$externalParameterId] = array_map(fn ($x) => $x['value'], $parameter['values']);
}
// aktualizuju parametry na nejhracce
$productParameters = [];
foreach ($parameterUpdates as $externalParameterId => $values) {
$parameterType = $this->getParameterType($externalParameterId);
$productParameters[] = new ProductParameter(
$externalProductId,
$externalParameterId,
array_map(fn ($x) => new ParameterValue($x, $parameterType), $values)
);
}
$this->getClient()->product->updateParameterBulk($productParameters);
}
public function createParameter(string $name, string $type, ?string $unit = null): array
{
$result = $this->getClient()->parameter->create(
[
'name' => $name,
'type' => $type,
'unit' => $unit,
]
)['parameterCreate'];
// Pridam parametr do cache
return $this->parametersCache[] = $result;
}
public function getSections(bool $force = false): array
{
if (!$force && $this->sectionsListCache) {
return $this->sectionsListCache;
}
return $this->sectionsListCache = $this->getClient()->section->setSelection(
[
'id',
'name',
'visible',
'url',
'isVirtual',
'parent' => [
'id',
'name',
'parent' => [
'id',
'name',
'parent' => [
'id',
'name',
'parent' => [
'id',
'name',
'parent' => [
'id',
'name',
'parent' => [
'id',
'name',
],
],
],
],
],
],
]
)->list()['items'] ?? [];
}
public function getSectionIdByPath(array $path): ?int
{
static $sectionsByKey = [];
$createKey = function (array $path) {
return StringUtil::slugify(implode('/', $path));
};
if (empty($sectionsByKey)) {
$sections = $this->getSections();
foreach ($sections as $section) {
$remotePath = $this->getRemoteSectionPath($section);
$sectionsByKey[$createKey($remotePath)] = $section['id'];
}
}
$key = $createKey($path);
return $sectionsByKey[$key] ?? null;
}
public function getRemoteSectionPath(array $section): array
{
$path = [];
$currentSection = $section;
while ($currentSection) {
$path[$currentSection['id']] = $currentSection['name'];
$currentSection = $currentSection['parent'];
}
return array_reverse($path, true);
}
public function getProducers(): array
{
if (!empty($this->producersListCache)) {
return $this->producersListCache;
}
return $this->producersListCache = $this->client->producer->list()['items'] ?? [];
}
public function getParameters(bool $force = false): array
{
if (!$force && !empty($this->parametersCache)) {
return $this->parametersCache;
}
return $this->parametersCache = $this->getClient()->parameter->list()['items'] ?? [];
}
public function getParameterType(int $parameterId): string
{
static $parameterTypes;
if (!$parameterTypes) {
$parameterTypes = Mapping::mapKeys($this->getParameters(), fn ($k, $v) => [$v['id'], $v['type']]);
}
return $parameterTypes[$parameterId] ?? 'LIST';
}
public function getParameterByName(string $name): ?array
{
$nameLower = mb_strtolower($name);
foreach ($this->getParameters() as $parameter) {
if (mb_strtolower($parameter['name']) === $nameLower) {
return $parameter;
}
}
return null;
}
public function getProducerIdByName(string $name): ?int
{
$nameLower = mb_strtolower($name);
foreach ($this->getProducers() as $producer) {
if (mb_strtolower($producer['name']) === $nameLower) {
return $producer['id'];
}
}
return null;
}
public function getProductIdByCode(string $code): ?int
{
return $this->getClient()->product->getByCode($code)['id'] ?? null;
}
public function getProductVisibility(string $visibility): string
{
switch ($visibility) {
case 'Y':
return ProductVisibility::VISIBLE;
case 'O':
return ProductVisibility::CLOSED;
default:
return ProductVisibility::HIDDEN;
}
}
public function getBlocks(int $rootBlockId, ?string $language = null): ?array
{
$language = $language ?: Contexts::get(LanguageContext::class)->getDefaultId();
return $this->contextManager->activateContexts([LanguageContext::class => $language], function () use ($rootBlockId) {
return $this->block->getBlocks($rootBlockId);
});
}
public function getProduct(int $productId, ?string $language = null): ?\Product
{
$productList = clone $this->productList;
$productList->andSpec(Operator::equals(['p.id' => $productId]));
$productList->fetchParameters();
$productList->fetchSections();
$productList->fetchLinks();
$productList->fetchCollections();
$productList->fetchProductsRelated();
$productList->fetchProducers();
$language = $language ?: Contexts::get(LanguageContext::class)->getDefaultId();
$products = $this->contextManager->activateContexts([LanguageContext::class => $language], function () use ($productList) {
return $productList->getProducts();
});
if (empty($products[$productId])) {
return null;
}
return $products[$productId];
}
public function createEditableContentFromBlocks(array $blocks): array
{
$data = [
'areas' => [],
'overwrite' => true,
];
foreach ($blocks as $block) {
$data['areas'][] = [
'id' => null,
'name' => $block['name'],
'data' => $block['json_content'] ?: '[]',
];
}
return $data;
}
public function getClient(): Client
{
if (!$this->client) {
$this->client = new Client(
self::GRAPHQL_URL,
self::GRAPHQL_ACCESS_TOKEN
);
}
return $this->client;
}
}