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

171 lines
5.2 KiB
PHP

<?php
namespace KupShop\FeedGeneratorBundle\Feed;
use KupShop\FeedGeneratorBundle\Exception\XMLFeedReader\UnreadableXMLException;
use KupShop\FeedGeneratorBundle\Exception\XMLFeedReader\XMLFeedReaderException;
use KupShop\FeedGeneratorBundle\Exception\XSDParser\XSDParsingException;
use KupShop\FeedGeneratorBundle\Utils\ExternalFeedCache;
use KupShop\FeedGeneratorBundle\V8FeedGenerator;
use KupShop\FeedGeneratorBundle\Wrapper\ExternalFeedObjectWrapper;
use KupShop\FeedGeneratorBundle\XMLFeedReader;
use KupShop\FeedGeneratorBundle\XSD\Parser;
use KupShop\FeedsBundle\FeedProductList;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class ExternalFeed implements IExternalFeed
{
use ConfigurableFeedTrait;
public const TYPE_DESCRIPTION = 'feed_description_url';
public const TYPE_SOURCE = 'feed_source_url';
protected static $type = 'external';
protected static $objectType = 'external';
protected static $alias = 'Externí';
private HttpClientInterface $http;
private V8FeedGenerator $generator;
private Parser $parser;
private XMLFeedReader $feedReader;
private ?string $searchValue = null;
/** @var FeedProductList */
protected $productList; // only declared to avoid redefining whole render() method
public static function isAllowed(): bool
{
return true;
}
public function __construct(HttpClientInterface $httpClient, V8FeedGenerator $generator, ExternalFeedObjectWrapper $objectWrapper)
{
$this->http = $httpClient;
$this->generator = $generator;
$this->objectWrapper = $objectWrapper;
$this->parser = new Parser();
$this->feedReader = new XMLFeedReader();
$this->setFeedGenerator($generator);
}
private function initParser(array $feedRow): void
{
$cache = new ExternalFeedCache($feedRow);
$this->parser->loadFile($cache->getDescriptionUrl());
}
/**
* @throws UnreadableXMLException
*/
private function initReader(array $feedRow, ?array $basePath = null): void
{
$cache = new ExternalFeedCache($feedRow, $basePath);
$this->feedReader->openFile($cache->getSourceUrl());
}
/**
* Called from template, shows in data tab in feed configuration.
*/
public function fetchDataDescription(array $feedRow): array
{
if (is_string($feedRow['data'])) {
$feedRow['data'] = json_decode($feedRow['data'], true);
}
$this->initParser($feedRow);
$data = $this->parser->getFeedRootElement()->serialize();
$data['children'] = $data['children'] ?? [];
$data['children'][] = ['type' => 'xs:string', 'name' => '__search', 'minOccurs' => '0'];
usort($data['children'], function ($a, $b) {
return strcmp($a['name'], $b['name']);
});
return $data;
}
private function doSearch(string $searchQuery): ?array
{
try {
return $this->feedReader->findElementBySearchAttribute($searchQuery);
} catch (XMLFeedReaderException $e) {
}
return null;
}
/**
* @throws XSDParsingException|XMLFeedReaderException
*/
public function getData(array $feedRow, ?int $limit = null): \Generator
{
if (is_string($feedRow['data'])) {
$feedRow['data'] = json_decode($feedRow['data'], true);
}
$this->initParser($feedRow);
$feedElementPath = $this->parser->getFeedRootElementPath();
$types = $this->parser->getTypeConstraints();
$this->initReader($feedRow, $feedElementPath);
$this->feedReader->setBasePath($feedElementPath);
$this->feedReader->setTypeCorrectionMap($types);
if ($this->searchValue !== null) {
$cache = new ExternalFeedCache($feedRow, $feedElementPath);
$res = $cache->getCacheItem($this->searchValue);
if ($res !== null) {
return yield $this->prepareSingleObject($res);
}
$found = $this->doSearch($this->searchValue);
if ($found === null) {
return new \EmptyIterator();
} else {
$res = $found;
$cache->setCacheItem($this->searchValue, $res);
}
return yield $this->prepareSingleObject($res);
}
$counter = 0;
foreach ($this->feedReader->read() as $feedItem) {
if ($limit !== null && $counter >= $limit) {
break;
}
yield $this->prepareSingleObject($feedItem);
$counter++;
}
}
public function getDataNoThrow(array $feedRow, ?int $limit = null): \Generator
{
try {
$data = $this->getData($feedRow, $limit);
foreach ($data as $elem) {
yield $elem;
}
} catch (\Throwable $ignore) {
return new \EmptyIterator();
}
}
public function fetchDataDescriptionNoThrow(array $feedRow): array
{
try {
return $this->fetchDataDescription($feedRow);
} catch (\Throwable $ignore) {
return [];
}
}
public function filterByObjectID($objectID): void
{
$this->searchValue = trim($objectID);
}
}