CoolCMS API

ProductXmlExporter
in package

FinalYes

Exports the catalog in the import format.

Mirrors the import grammar so exactly that the generated file passes unchanged through the same XmlImportParser/ImportValidator/XSD trio as a supplier file (the roundtrip doubles as a format reference). Like FeedXmlGenerator: reads only through the Api managers, streams with XMLWriter into a file and writes it atomically.

Every exported section is also written back on reimport (ImportProductWriter::apply*); the export writes the current state, so a reimport of an unchanged export writes the same values.

IMPORTANT: the card <image> carries only src (the XSD productImageType has no assetId), unlike variant <images><image assetId="">. The export therefore builds an absolute URL for the card photo and always writes assetId for variant gallery images (an existing asset, no download on reimport).

At most ImportRules::MAX_IMAGES_PER_VARIANT images per variant: only the first 20 are written, and an XML comment is added.

Table of Contents

Constants

ALL_SECTIONS  : array<string|int, mixed> = [self::SECTION_CATEGORIES, self::SECTION_TEXTS,...
DEFAULT_LIMIT  : int = 500
Safe cap of cards per synchronous admin export (read-only, higher than the import "run now" limit of 200).
SECTION_ACCESSORIES  : string = 'accessories'
SECTION_CATEGORIES  : string = 'categories'
SECTION_IMAGES  : string = 'images'
SECTION_PARAMETERS  : string = 'parameters'
SECTION_PRICES  : string = 'prices'
SECTION_STOCK  : string = 'stock'
SECTION_TAGS  : string = 'tags'
SECTION_TEXTS  : string = 'texts'

Properties

$currencyManager  : CurrencyManager
$em  : EntityManagerInterface
$httpRequest  : IRequest
$languageManager  : LanguageManager
$parameterManager  : ParameterManager
$productManager  : ProductManager
$productSetManager  : ProductSetManager
$settingContext  : SettingContext
$siteBaseUrl  : SiteBaseUrl|null
$stockItemManager  : StockItemManager
$userGroupManager  : UserGroupManager
$warehouseManager  : WarehouseManager

Methods

__construct()  : mixed
export()  : array{relativePath: string, fileName: string, productCount: int, variantCount: int, bundleCount: int, truncated: bool, requestedCount: int}
absoluteAssetUrl()  : string
buildTargetPath()  : array{abs: string, relative: string, fileName: string}
flattenSetIds()  : array<int, int>
loadCatalogData()  : ProductSet>>}
loadParameterOptionCodebook()  : array<int, array{key: ?string, name: ?string}>
Option choices are child rows of the {@see Parameter} entity (`parentId`); for an option type `ProductSetParameterRel::getValue()` holds their `id` as a string. The batch loads the key (plus the readable name in the default language as a fallback when a choice has no `key`).
loadPriceCodebook()  : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}
Returns the currently valid price per axis (currency x fromAmount x userGroupId) read from the read-only view {@see ProductSetCurrentPrice} (join `product_set_prices` x `max_published_time_for_prices`, `published_time <= NOW()`): the same source of truth as the front end, no own series grouping in PHP.
loadStockCodebook()  : ItemAvailability>, warehouseCodes: array}
Returns stock availability per variant, batched through {@see StockItemManager::getAvailabilityBatch()} (the same read API as `ProductSetManager::getAvailability()`). The warehouse is `labelWarehouseId` (the first active one by priority with `available > 0`), falling back to the first `perWarehouse` row.
openWriter()  : XMLWriter
Streams the document straight into a temporary file: `XMLWriter::openUri()` does not hold the whole document in memory, unlike `openMemory()`. The caller finishes it (`endDocument`/`flush`) and does the atomic `rename()` to the final path ({@see export()}, like {@see FeedXmlGenerator::publishFeedFile()}).
resolveLanguageIds()  : array<string, int>
resolveProductIds()  : array<int, int>
Maps `productSetId` to its card (fallback `product`/`variant.product`, same as {@see FeedXmlGenerator::resolveProduct()}; the direct `ProductSet.product` is an optional denormalization).
trimDecimal()  : string
`Vat.percents` and similar decimal columns without needless trailing zeros (`21.00` becomes `21`).
writeAccessories()  : void
writeBundle()  : void
writeBundleItems()  : void
writeBundles()  : int
Writes `<bundles>` only from explicitly selected `$productSetIds` (a bundle has no `product`, so {@see resolveProductIds()} never adds it to the cards), which keeps the export-to-import roundtrip for bundles. Shares `writeTags()`/`writePrices()`/`writeImages()` with the variant; `writeParameters()`, `writeStock()` and `writeAccessories()` are not called because the bundle grammar has none of them.
writeCategories()  : void
writeCDataSafe()  : void
Writes CDATA safely. `XMLWriter::writeCData()` only wraps the content and does not split the closing sequence `]]>` inside the text, so a single description containing it (even as a textual example) would produce `<![CDATA[a]]>b]]>` and make the whole export invalid XML.
writeImages()  : void
writeLangElement()  : void
Text per language; without a `lang` attribute only for the default language of the file.
writeParameters()  : void
writePrices()  : void
writeProduct()  : void
writeSettings()  : void
writeStock()  : void
writeTags()  : void
writeVariant()  : void

Constants

ALL_SECTIONS

public array<string|int, mixed> ALL_SECTIONS = [self::SECTION_CATEGORIES, self::SECTION_TEXTS, self::SECTION_PARAMETERS, self::SECTION_TAGS, self::SECTION_PRICES, self::SECTION_STOCK, self::SECTION_IMAGES, self::SECTION_ACCESSORIES]

DEFAULT_LIMIT

Safe cap of cards per synchronous admin export (read-only, higher than the import "run now" limit of 200).

public int DEFAULT_LIMIT = 500

Properties

Methods

__construct()

public __construct(EntityManagerInterface $em, ProductManager $productManager, ProductSetManager $productSetManager, ParameterManager $parameterManager, LanguageManager $languageManager, CurrencyManager $currencyManager, UserGroupManager $userGroupManager, StockItemManager $stockItemManager, WarehouseManager $warehouseManager, SettingContext $settingContext, IRequest $httpRequest[, SiteBaseUrl|null $siteBaseUrl = null ]) : mixed
Parameters
$em : EntityManagerInterface
$productManager : ProductManager
$productSetManager : ProductSetManager
$parameterManager : ParameterManager
$languageManager : LanguageManager
$currencyManager : CurrencyManager
$userGroupManager : UserGroupManager
$stockItemManager : StockItemManager
$warehouseManager : WarehouseManager
$settingContext : SettingContext
$httpRequest : IRequest
$siteBaseUrl : SiteBaseUrl|null = null

export()

public export(array<int, int> $productIds, array<int, int> $productSetIds, array<int, string> $languages, bool $includeIds, array<int, string> $sections[, int $limit = self::DEFAULT_LIMIT ]) : array{relativePath: string, fileName: string, productCount: int, variantCount: int, bundleCount: int, truncated: bool, requestedCount: int}
Parameters
$productIds : array<int, int>

explicit cards

$productSetIds : array<int, int>

sets, mapped to their card

$languages : array<int, string>

language aliases; empty means all configured

$includeIds : bool
$sections : array<int, string>

allowed sections (ALL_SECTIONS); empty means all

$limit : int = self::DEFAULT_LIMIT
Return values
array{relativePath: string, fileName: string, productCount: int, variantCount: int, bundleCount: int, truncated: bool, requestedCount: int}

absoluteAssetUrl()

private absoluteAssetUrl(object $asset, string $baseUrl) : string
Parameters
$asset : object
$baseUrl : string
Return values
string

buildTargetPath()

private buildTargetPath() : array{abs: string, relative: string, fileName: string}
Return values
array{abs: string, relative: string, fileName: string}

flattenSetIds()

private flattenSetIds(array<int, array<int, ProductSet>> $setsByProduct) : array<int, int>
Parameters
$setsByProduct : array<int, array<int, ProductSet>>
Return values
array<int, int>

loadCatalogData()

private loadCatalogData(array<int, int> $productIds, array<int, string> $enabledSections) : ProductSet>>}
Parameters
$productIds : array<int, int>
$enabledSections : array<int, string>
Return values
ProductSet>>}

loadParameterOptionCodebook()

Option choices are child rows of the {@see Parameter} entity (`parentId`); for an option type `ProductSetParameterRel::getValue()` holds their `id` as a string. The batch loads the key (plus the readable name in the default language as a fallback when a choice has no `key`).

private loadParameterOptionCodebook(array<int, array<int, ProductSet>> $setsByProduct, int|null $defaultLanguageId) : array<int, array{key: ?string, name: ?string}>

Option and range types are told apart without duplicating the numeric constants of ParameterType (they live only in ImportValidator): value2 !== null means range, otherwise parameter->getHasOptions() means option, otherwise text, the same signal as the validator.

Parameters
$setsByProduct : array<int, array<int, ProductSet>>
$defaultLanguageId : int|null
Return values
array<int, array{key: ?string, name: ?string}> —

choice id => key/name

loadPriceCodebook()

Returns the currently valid price per axis (currency x fromAmount x userGroupId) read from the read-only view {@see ProductSetCurrentPrice} (join `product_set_prices` x `max_published_time_for_prices`, `published_time <= NOW()`): the same source of truth as the front end, no own series grouping in PHP.

private loadPriceCodebook(array<int, array<int, ProductSet>> $setsByProduct) : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}
Parameters
$setsByProduct : array<int, array<int, ProductSet>>
Return values
ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}

loadStockCodebook()

Returns stock availability per variant, batched through {@see StockItemManager::getAvailabilityBatch()} (the same read API as `ProductSetManager::getAvailability()`). The warehouse is `labelWarehouseId` (the first active one by priority with `available > 0`), falling back to the first `perWarehouse` row.

private loadStockCodebook(array<int, array<int, ProductSet>> $setsByProduct) : ItemAvailability>, warehouseCodes: array}
Parameters
$setsByProduct : array<int, array<int, ProductSet>>
Return values
ItemAvailability>, warehouseCodes: array}

openWriter()

Streams the document straight into a temporary file: `XMLWriter::openUri()` does not hold the whole document in memory, unlike `openMemory()`. The caller finishes it (`endDocument`/`flush`) and does the atomic `rename()` to the final path ({@see export()}, like {@see FeedXmlGenerator::publishFeedFile()}).

private openWriter(string $tmpPath) : XMLWriter
Parameters
$tmpPath : string
Return values
XMLWriter

resolveLanguageIds()

private resolveLanguageIds(array<int, string> $languages) : array<string, int>
Parameters
$languages : array<int, string>
Return values
array<string, int> —

alias => id in request order (the first one is the default language of the file)

resolveProductIds()

Maps `productSetId` to its card (fallback `product`/`variant.product`, same as {@see FeedXmlGenerator::resolveProduct()}; the direct `ProductSet.product` is an optional denormalization).

private resolveProductIds(array<int, int> $productIds, array<int, int> $productSetIds) : array<int, int>

Returns a sorted, deduplicated list of card ids.

Parameters
$productIds : array<int, int>
$productSetIds : array<int, int>
Return values
array<int, int>

trimDecimal()

`Vat.percents` and similar decimal columns without needless trailing zeros (`21.00` becomes `21`).

private trimDecimal(string $value) : string
Parameters
$value : string
Return values
string

writeBundle()

private writeBundle(XMLWriter $writer, ProductSet $bundle, array<string, int> $languageIds, string $defaultLangAlias, bool $includeIds, array<int, string> $enabledSections, ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null $priceCodebook) : void
Parameters
$writer : XMLWriter
$bundle : ProductSet
$languageIds : array<string, int>
$defaultLangAlias : string
$includeIds : bool
$enabledSections : array<int, string>
$priceCodebook : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null

writeBundles()

Writes `<bundles>` only from explicitly selected `$productSetIds` (a bundle has no `product`, so {@see resolveProductIds()} never adds it to the cards), which keeps the export-to-import roundtrip for bundles. Shares `writeTags()`/`writePrices()`/`writeImages()` with the variant; `writeParameters()`, `writeStock()` and `writeAccessories()` are not called because the bundle grammar has none of them.

private writeBundles(XMLWriter $writer, array<int, int> $productSetIds, array<string, int> $languageIds, string $defaultLangAlias, bool $includeIds, array<int, string> $enabledSections) : int
Parameters
$writer : XMLWriter
$productSetIds : array<int, int>
$languageIds : array<string, int>
$defaultLangAlias : string
$includeIds : bool
$enabledSections : array<int, string>
Return values
int

writeCategories()

private writeCategories(XMLWriter $writer, Product $product, int|null $defaultLanguageId) : void
Parameters
$writer : XMLWriter
$product : Product
$defaultLanguageId : int|null

writeCDataSafe()

Writes CDATA safely. `XMLWriter::writeCData()` only wraps the content and does not split the closing sequence `]]>` inside the text, so a single description containing it (even as a textual example) would produce `<![CDATA[a]]>b]]>` and make the whole export invalid XML.

private static writeCDataSafe(XMLWriter $writer, string $value) : void

The text is therefore split at the ]]> boundary into several CDATA sections so that ]] ends one and > starts the next; the parser joins them back. The sequence is neither escaped nor removed, the description stays identical character by character.

a]]>b -> <![CDATA[a]]]]><![CDATA[>b]]> -> read as a]]>b

Parameters
$writer : XMLWriter
$value : string

writeImages()

private writeImages(XMLWriter $writer, ProductSet $set, array<string, int> $languageIds) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$languageIds : array<string, int>

writeLangElement()

Text per language; without a `lang` attribute only for the default language of the file.

private writeLangElement(XMLWriter $writer, string $tag, string $alias, string $defaultLangAlias, string $value, bool $cdata) : void
Parameters
$writer : XMLWriter
$tag : string
$alias : string
$defaultLangAlias : string
$value : string
$cdata : bool

writeParameters()

private writeParameters(XMLWriter $writer, ProductSet $set, array<int, array{key: ?string, name: ?string}> $optionCodebook) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$optionCodebook : array<int, array{key: ?string, name: ?string}>

writePrices()

private writePrices(XMLWriter $writer, ProductSet $set, ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int} $priceCodebook) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$priceCodebook : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}

writeProduct()

private writeProduct(XMLWriter $writer, Product $product, array<int, ProductSet> $sets, array<string, int> $languageIds, string $defaultLangAlias, bool $includeIds, array<int, string> $enabledSections, ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null $priceCodebook, ItemAvailability>, warehouseCodes: array}|null $stockCodebook, array<int, array{key: ?string, name: ?string}>|null $optionCodebook, string $baseUrl) : void
Parameters
$writer : XMLWriter
$product : Product
$sets : array<int, ProductSet>
$languageIds : array<string, int>
$defaultLangAlias : string
$includeIds : bool
$enabledSections : array<int, string>
$priceCodebook : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null
$stockCodebook : ItemAvailability>, warehouseCodes: array}|null
$optionCodebook : array<int, array{key: ?string, name: ?string}>|null
$baseUrl : string

writeSettings()

private writeSettings(XMLWriter $writer, string $defaultLangAlias) : void
Parameters
$writer : XMLWriter
$defaultLangAlias : string

writeStock()

private writeStock(XMLWriter $writer, ProductSet $set, ItemAvailability>, warehouseCodes: array} $stockCodebook) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$stockCodebook : ItemAvailability>, warehouseCodes: array}

writeTags()

private writeTags(XMLWriter $writer, ProductSet $set, int|null $defaultLanguageId) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$defaultLanguageId : int|null

writeVariant()

private writeVariant(XMLWriter $writer, ProductSet $set, array<string, int> $languageIds, string $defaultLangAlias, bool $includeIds, array<int, string> $enabledSections, ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null $priceCodebook, ItemAvailability>, warehouseCodes: array}|null $stockCodebook, array<int, array{key: ?string, name: ?string}>|null $optionCodebook) : void
Parameters
$writer : XMLWriter
$set : ProductSet
$languageIds : array<string, int>
$defaultLangAlias : string
$includeIds : bool
$enabledSections : array<int, string>
$priceCodebook : ProductSetCurrentPrice>>, currencies: array, baseUserGroupId: int}|null
$stockCodebook : ItemAvailability>, warehouseCodes: array}|null
$optionCodebook : array<int, array{key: ?string, name: ?string}>|null
On this page

Search results