mirror of
https://github.com/avecms/AVE.cms.git
synced 2026-08-01 08:45:44 +00:00
93 lines
7.3 KiB
PHP
93 lines
7.3 KiB
PHP
<?php
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------------------
|
|
| AVE.cms
|
|
|--------------------------------------------------------------------------------------
|
|
| @package AVE.cms
|
|
| @file system/App/Frontend/Feeds/Generator.php
|
|
| @author AVE.cms <support@ave-cms.ru>
|
|
| @copyright 2007-2026 (c) AVE.cms
|
|
| @link https://ave-cms.ru
|
|
| @version 3.3
|
|
*/
|
|
|
|
namespace App\Frontend\Feeds;
|
|
|
|
defined('BASEPATH') || die('Direct access to this location is not allowed.');
|
|
|
|
use App\Content\Fields\FieldValueCodec;
|
|
use DB;
|
|
|
|
class Generator
|
|
{
|
|
protected $repository;
|
|
public function __construct() { $this->repository = new Repository(); }
|
|
|
|
public function generate(array $feed, $limit = 0, $record = true)
|
|
{
|
|
$started = microtime(true); $skipped = 0;
|
|
try {
|
|
$products = $this->repository->products($feed, $limit);
|
|
$categories = $feed['profile'] === 'yml_offers' || $feed['profile'] === 'price' ? array() : $this->repository->outputCategories($feed);
|
|
$writer = new \XMLWriter(); $writer->openMemory(); $writer->setIndent(true); $writer->startDocument('1.0', 'UTF-8');
|
|
$writer->startElement('yml_catalog'); $writer->writeAttribute('date', date('Y-m-d H:i'));
|
|
$writer->startElement('shop'); $writer->writeElement('name', $feed['site_name']); $writer->writeElement('company', $feed['company_name']); $writer->writeElement('url', rtrim($feed['base_url'], '/'));
|
|
$writer->startElement('currencies'); $writer->startElement('currency'); $writer->writeAttribute('id', $feed['currency']); $writer->writeAttribute('rate', '1'); $writer->endElement(); $writer->endElement();
|
|
if ($categories) { $writer->startElement('categories'); foreach ($categories as $category) { $writer->startElement('category'); $writer->writeAttribute('id', (string) $category['id']); if (!empty($category['parent_id'])) { $writer->writeAttribute('parentId', (string) $category['parent_id']); } $writer->text($category['name']); $writer->endElement(); } $writer->endElement(); }
|
|
$writer->startElement('offers'); $written = 0;
|
|
foreach ($products as $product) { if (!$this->writeOffer($writer, $feed, $product)) { $skipped++; continue; } $written++; }
|
|
$writer->endElement(); $writer->endElement(); $writer->endElement(); $writer->endDocument(); $xml = $writer->outputMemory();
|
|
if ($record) { $this->record($feed['id'], 'success', $written, $skipped, strlen($xml), $started, ''); }
|
|
return array('xml' => $xml, 'offers' => $written, 'skipped' => $skipped, 'products' => $products);
|
|
} catch (\Throwable $e) {
|
|
if ($record) { $this->record($feed['id'], 'error', 0, $skipped, 0, $started, $e->getMessage()); }
|
|
throw $e;
|
|
}
|
|
}
|
|
|
|
protected function writeOffer(\XMLWriter $writer, array $feed, array $product)
|
|
{
|
|
$map = $feed['mappings']; $price = $this->value(isset($map['price']) ? $map['price'] : 'index:price', $product, $feed);
|
|
if ((float) $price < (float) $feed['min_price']) { return false; }
|
|
$writer->startElement('offer'); $writer->writeAttribute('id', (string) $product['product_id']); $writer->writeAttribute('available', (float) $product['stock'] > 0 ? 'true' : 'false');
|
|
$this->element($writer, 'url', $this->value(isset($map['url']) ? $map['url'] : 'document:url', $product, $feed));
|
|
$this->element($writer, 'price', $price); $old = $this->value(isset($map['oldprice']) ? $map['oldprice'] : 'index:old_price', $product, $feed); if ((float) $old > 0) { $this->element($writer, 'oldprice', $old); }
|
|
$this->element($writer, 'currencyId', $feed['currency']);
|
|
if ($feed['profile'] !== 'yml_offers' && $feed['profile'] !== 'price' && !empty($product['feed_category_id'])) { $this->element($writer, 'categoryId', $product['feed_category_id']); }
|
|
if ($feed['profile'] !== 'price') {
|
|
foreach ($this->pictures($this->value(isset($map['pictures']) ? $map['pictures'] : 'index:images', $product, $feed)) as $picture) { $this->element($writer, 'picture', $this->absolute($picture, $feed)); }
|
|
$this->element($writer, 'name', $this->value(isset($map['name']) ? $map['name'] : 'index:title', $product, $feed));
|
|
$vendor = $this->value(isset($map['vendor']) ? $map['vendor'] : 'static:' . $feed['site_name'], $product, $feed); if ($vendor !== '') { $this->element($writer, 'vendor', $vendor); }
|
|
$this->element($writer, 'vendorCode', $this->value(isset($map['vendorCode']) ? $map['vendorCode'] : 'index:article', $product, $feed));
|
|
$description = $this->value(isset($map['description']) ? $map['description'] : '', $product, $feed); if ($description !== '') { $writer->startElement('description'); $writer->writeCdata($this->clean($description)); $writer->endElement(); }
|
|
foreach ($feed['params'] as $param) { $fieldId = isset($param['field_id']) ? (int) $param['field_id'] : 0; $value = $fieldId && isset($product['fields'][$fieldId]) ? $this->clean($product['fields'][$fieldId]) : ''; if ($value === '' && !empty($param['skip_empty'])) { continue; } $writer->startElement('param'); $writer->writeAttribute('name', isset($param['name']) ? $param['name'] : 'Поле ' . $fieldId); if (!empty($param['unit'])) { $writer->writeAttribute('unit', $param['unit']); } $writer->text($value); $writer->endElement(); }
|
|
} else { $this->element($writer, 'name', $this->value(isset($map['name']) ? $map['name'] : 'index:title', $product, $feed)); $this->element($writer, 'vendorCode', $this->value(isset($map['vendorCode']) ? $map['vendorCode'] : 'index:article', $product, $feed)); }
|
|
$writer->endElement(); return true;
|
|
}
|
|
|
|
protected function value($source, array $product, array $feed)
|
|
{
|
|
$source = (string) $source;
|
|
if (strpos($source, 'static:') === 0) { return substr($source, 7); }
|
|
if (strpos($source, 'field:') === 0) { $id = (int) substr($source, 6); return isset($product['fields'][$id]) ? $product['fields'][$id] : ''; }
|
|
if ($source === 'document:url') { return rtrim($feed['base_url'], '/') . '/' . ltrim($product['alias'], '/'); }
|
|
if ($source === 'index:images') { return $product['images_json']; }
|
|
if (strpos($source, 'index:') === 0) { $key = substr($source, 6); return isset($product[$key]) ? $product[$key] : ''; }
|
|
return '';
|
|
}
|
|
|
|
protected function pictures($value)
|
|
{
|
|
$decoded = FieldValueCodec::decodeStructured($value, null);
|
|
if (!is_array($decoded)) { $decoded = $value !== '' ? array($value) : array(); }
|
|
$result = array(); array_walk_recursive($decoded, function ($item) use (&$result) { $item = explode('|', (string) $item, 2)[0]; if ($item !== '') { $result[] = $item; } });
|
|
return array_slice(array_values(array_unique($result)), 0, 10);
|
|
}
|
|
|
|
protected function absolute($path, array $feed) { return preg_match('#^https?://#i', (string) $path) ? $path : rtrim($feed['base_url'], '/') . '/' . ltrim($path, '/'); }
|
|
protected function clean($value) { return trim(html_entity_decode(strip_tags((string) $value), ENT_QUOTES, 'UTF-8')); }
|
|
protected function element(\XMLWriter $writer, $name, $value) { if ((string) $value === '') { return; } $writer->writeElement($name, (string) $value); }
|
|
protected function record($feedId, $status, $offers, $skipped, $bytes, $started, $message) { DB::Insert(Schema::table('runs'), array('feed_id'=>(int)$feedId,'status'=>$status,'offers_count'=>(int)$offers,'skipped_count'=>(int)$skipped,'bytes'=>(int)$bytes,'duration_ms'=>(int)round((microtime(true)-$started)*1000),'message'=>(string)$message,'created_at'=>time())); }
|
|
}
|