294 lines
9.8 KiB
PHP
294 lines
9.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\ZNZBundle\Util;
|
|
|
|
use External\ZNZBundle\Exception\ZNZException;
|
|
use KupShop\ContentBundle\Util\Block;
|
|
use KupShop\KupShopBundle\Context\ContextManager;
|
|
use KupShop\KupShopBundle\Context\DomainContext;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use Query\Operator;
|
|
|
|
class ZNZBlockUpdater
|
|
{
|
|
public const TYPE_PRODUCTS = 'products';
|
|
public const TYPE_PRODUCERS = 'producers';
|
|
public const TYPE_TEMPLATES = 'templates';
|
|
|
|
public const TYPES = [
|
|
self::TYPE_PRODUCTS,
|
|
self::TYPE_PRODUCERS,
|
|
self::TYPE_TEMPLATES,
|
|
];
|
|
|
|
public function __construct(
|
|
private readonly ZNZConfiguration $configuration,
|
|
private readonly ContextManager $contextManager,
|
|
private readonly Block $block,
|
|
private readonly ZNZUtil $znzUtil,
|
|
private readonly ZNZApi $znzApi,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Odesle popis objektu z e-shopu do Heliosu.
|
|
*/
|
|
public function update(string $type, int $objectId, array $languages, bool $withTriggers = true): bool
|
|
{
|
|
$method = 'getData'.ucfirst($type);
|
|
if (!method_exists($this, $method)) {
|
|
throw new ZNZException(sprintf('Typ objektu "%s" není podporován!', $type));
|
|
}
|
|
|
|
if (!($item = $this->{$method}($objectId))) {
|
|
throw new ZNZException('Objekt nebyl nalezen.');
|
|
}
|
|
|
|
if (empty($item['id_block'])) {
|
|
throw new ZNZException('Objekt nemá nastaven žádný obsah, aktualizaci není možné provést!');
|
|
}
|
|
|
|
return $this->updateBlock(
|
|
(int) $item['id_block'],
|
|
(int) $item['id_znz'],
|
|
$type,
|
|
$languages,
|
|
$withTriggers
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Finalni odeslani do Heliosu.
|
|
*/
|
|
public function updateBlock(int $rootBlockId, int $znzId, string $type, array $languages, bool $withTriggers = true): bool
|
|
{
|
|
$languageContext = Contexts::get(LanguageContext::class);
|
|
|
|
// projdu vsechny podporovane jazyky
|
|
foreach ($languageContext->getAll() as $lang) {
|
|
// pokud jazyk nechci updatovat, tak ho preskocim
|
|
if (!in_array($lang->getId(), $languages)) {
|
|
continue;
|
|
}
|
|
|
|
[$data, $plainContent] = $this->getBlocksData($rootBlockId, $lang->getId());
|
|
|
|
$updateMethod = match ($type) {
|
|
self::TYPE_PRODUCTS => 'updateProductsBlockToHelios',
|
|
self::TYPE_PRODUCERS => 'updateProducersBlockToHelios',
|
|
self::TYPE_TEMPLATES => 'updateSeriesBlockToHelios',
|
|
};
|
|
|
|
if (!method_exists($this->znzApi, $updateMethod)) {
|
|
throw new \RuntimeException(sprintf('Invalid method name: "%s"!', $updateMethod));
|
|
}
|
|
|
|
if (!isLocalDevelopment()) {
|
|
$this->znzApi->{$updateMethod}($znzId, $lang->getLocale(), $data, $plainContent, $withTriggers);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Načte data bloků od root id ve specifikovaném jazyce.
|
|
*/
|
|
public function getBlocksData(int $rootBlockId, string $language): array
|
|
{
|
|
return $this->contextManager->activateContexts([LanguageContext::class => $language], function () use ($rootBlockId) {
|
|
$blocks = $this->getBlocks($rootBlockId);
|
|
|
|
$photosMapping = $this->getBlockPhotosMapping($blocks);
|
|
|
|
$data = [
|
|
'from' => $this->configuration->getMainWebsite(),
|
|
'photos' => $photosMapping,
|
|
'blocks' => $blocks,
|
|
];
|
|
|
|
$plainContent = $this->getBlocksPlainText($blocks);
|
|
|
|
return [$data, $plainContent];
|
|
});
|
|
}
|
|
|
|
private function getBlocksPlainText(array $blocks): string
|
|
{
|
|
$content = implode('', array_filter(array_map(fn ($x) => $x['content'], $blocks)));
|
|
$content = str_replace(['<p>', '</p>'], ['', "\n"], strip_tags($content, ['<p>']));
|
|
|
|
$content = strip_tags($content);
|
|
|
|
$content = str_replace(' ', ' ', $content);
|
|
$content = htmlentities($content);
|
|
$content = str_replace(' ', ' ', $content);
|
|
$content = html_entity_decode($content);
|
|
$content = preg_replace('/&#?[a-z0-9]{2,8};/i', '', $content);
|
|
|
|
return trim($content);
|
|
}
|
|
|
|
private function getBlockPhotosMapping(array $blocks): array
|
|
{
|
|
$photoIds = $this->recursivelyCollectBlocksPhotoIds($blocks);
|
|
|
|
$mapping = [];
|
|
|
|
$qb = sqlQueryBuilder()
|
|
->select('zph.id_znz, ph.id as id_photo, ph.source, ph.image_2')
|
|
->from('photos', 'ph')
|
|
->leftJoin('ph', 'znz_photos', 'zph', 'zph.id_photo = ph.id')
|
|
->where(Operator::inIntArray($photoIds, 'ph.id'));
|
|
|
|
$domainContext = Contexts::get(DomainContext::class);
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
$znzPhotoId = !empty($item['id_znz']) ? (int) $item['id_znz'] : null;
|
|
|
|
$mapping[$item['id_photo']] = [$znzPhotoId, 'https://'.$domainContext->getActive().'/data/photos/'.$item['source'].$item['image_2']];
|
|
}
|
|
|
|
return $mapping;
|
|
}
|
|
|
|
private function getDataProducts(int $objectId): ?array
|
|
{
|
|
$item = sqlQueryBuilder()
|
|
->select('zp.id_znz, p.id_block')
|
|
->from('blocks', 'b')
|
|
->join('b', 'products', 'p', 'p.id_block = b.id_root')
|
|
->join('p', 'znz_products', 'zp', 'zp.id_product = p.id AND zp.id_variation IS NULL')
|
|
->andWhere('p.id_block IS NOT NULL')
|
|
->andWhere(Operator::equals(['p.id' => $objectId]))
|
|
->execute()->fetchAssociative();
|
|
|
|
return $item ?: null;
|
|
}
|
|
|
|
private function getDataProducers(int $objectId): ?array
|
|
{
|
|
$item = sqlQueryBuilder()
|
|
->select('zpv.id_znz, pr.id_block')
|
|
->from('producers', 'pr')
|
|
->join('pr', 'parameters_list', 'pl', 'pl.id_parameter = :parameterId AND pl.value = pr.name')
|
|
->join('pl', 'znz_parameters_values', 'zpv', 'zpv.id_parameter = :parameterId AND zpv.id_parameters_value = pl.id')
|
|
->andWhere(Operator::equals(['pr.id' => $objectId]))
|
|
->addParameters(['parameterId' => $this->znzUtil->getParameterByKey('ZnackaOption')])
|
|
->execute()->fetchAssociative();
|
|
|
|
return $item ?: null;
|
|
}
|
|
|
|
private function getDataTemplates(int $objectId): ?array
|
|
{
|
|
$item = sqlQueryBuilder()
|
|
->select('JSON_VALUE(t.data, "$.znzOptionId") as id_znz, t.id_block')
|
|
->from('templates', 't')
|
|
->andWhere(Operator::equals(['t.id' => $objectId]))
|
|
->execute()->fetchAssociative();
|
|
|
|
if (empty($item['id_znz'])) {
|
|
return null;
|
|
}
|
|
|
|
return $item ?: null;
|
|
}
|
|
|
|
private function getBlocks(int $blockID): array
|
|
{
|
|
$translationFields = [
|
|
'name' => '',
|
|
'content' => '',
|
|
'json_content' => '[]',
|
|
];
|
|
|
|
/** @var LanguageContext $languageContext */
|
|
$languageContext = Contexts::get(LanguageContext::class);
|
|
|
|
$qb = sqlQueryBuilder()
|
|
->select('b.*', 'GROUP_CONCAT(pb.id_photo ORDER BY pb.position) as photos')
|
|
->from('blocks', 'b')
|
|
->leftJoin('b', 'photos_blocks_relation', 'pb', 'pb.id_block = b.id')
|
|
->andWhere(Operator::equals(['b.id_root' => $blockID]))
|
|
->groupBy('b.id')
|
|
->orderBy('position')
|
|
->setParameter('languageId', $languageContext->getActiveId());
|
|
|
|
if ($languageContext->translationActive()) {
|
|
foreach ($translationFields as $field => $defaultValue) {
|
|
$qb->addSelect("COALESCE(bt.{$field}, '{$defaultValue}') as {$field}");
|
|
}
|
|
|
|
$qb->leftJoin('b', 'blocks_translations', 'bt', 'bt.id_block = b.id AND bt.id_language = :languageId');
|
|
}
|
|
|
|
$blocks = $this->block->buildBlockHierarchy(
|
|
$qb->execute()->fetchAllAssociative(),
|
|
$blockID
|
|
);
|
|
|
|
foreach ($blocks as &$block) {
|
|
$jsonContent = json_decode($block['json_content'] ?: '', true) ?: [];
|
|
if (empty($block['content']) && !empty($jsonContent)) {
|
|
$block['json_content'] = '[]';
|
|
}
|
|
}
|
|
|
|
return $blocks;
|
|
}
|
|
|
|
private function recursivelyCollectBlocksPhotoIds(array $blocks): array
|
|
{
|
|
$photoIds = [];
|
|
|
|
$this->recursivelyBlocksWalk($blocks, function (array $block) use (&$photoIds) {
|
|
$result = $this->recursivelyGetPhotoIdsInBlockContent(
|
|
json_decode($block['json_content'] ?? '', true) ?: []
|
|
);
|
|
|
|
$photoIds = array_merge($photoIds, $result);
|
|
});
|
|
|
|
return array_unique($photoIds);
|
|
}
|
|
|
|
private function recursivelyGetPhotoIdsInBlockContent(array $data): array
|
|
{
|
|
$photoIds = [];
|
|
|
|
foreach ($data as &$item) {
|
|
if (!empty($item['settings']['photo']['id'])) {
|
|
$photoIds[] = $item['settings']['photo']['id'];
|
|
}
|
|
|
|
if (!empty($item['settings']['photos'])) {
|
|
foreach ($item['settings']['photos'] as $photoKey => $photoItem) {
|
|
$photoIds[] = $item['settings']['photos'][$photoKey]['photo']['id'];
|
|
}
|
|
}
|
|
|
|
if (!empty($item['children'])) {
|
|
$photoIds = array_merge($photoIds, $this->recursivelyGetPhotoIdsInBlockContent($item['children']));
|
|
}
|
|
}
|
|
|
|
return array_unique($photoIds);
|
|
}
|
|
|
|
private function recursivelyBlocksWalk(array $blocks, callable $fn): array
|
|
{
|
|
foreach ($blocks as &$block) {
|
|
$block = $fn($block);
|
|
if (!empty($block['children'])) {
|
|
$block['children'] = $this->recursivelyBlocksWalk($block['children'], $fn);
|
|
}
|
|
}
|
|
|
|
return $blocks;
|
|
}
|
|
}
|