├── composer.json ├── readme.md └── src ├── CommerceML.php ├── Exceptions └── CommerceMLException.php └── Models ├── Category.php ├── Model.php ├── PriceType.php ├── Product.php └── Property.php /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "artemsway/commerceml", 3 | "homepage": "https://github.com/artemsway/commerceml", 4 | "description": "Package for parsing CommerceML2 files.", 5 | "keywords": ["commerceml", "1c"], 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "homepage": "https://github.com/ArtemsWay", 10 | "name": "Artem Miroshnichenko", 11 | "email": "artemsway@gmail.com" 12 | } 13 | ], 14 | "autoload": { 15 | "psr-4": { 16 | "ArtemsWay\\CommerceML\\": "src" 17 | } 18 | }, 19 | "minimum-stability": "dev", 20 | "require": {} 21 | } 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Парсер CommerceML2 2 | ============== 3 | 4 | Библиотека для парсинга CommerceML2 файлов. 5 | В основу была взята библиотека [zenwalker/commerceml](https://github.com/zenwalker/php-commerceml), 6 | за что разработчику [zenwolker](https://zenwalker.me) отдельное спасибо. 7 | ### Установка 8 | 9 | 1. Обновите ваш composer.json файл. 10 | 11 | ```json 12 | "repositories": [ 13 | { 14 | "type": "vcs", 15 | "url": "https://github.com/ArtemsWay/commerceml" 16 | } 17 | ], 18 | "require": { 19 | 20 | "artemsway/commerceml": "dev-master" 21 | }, 22 | ``` 23 | 24 | 2. Выполните команду ``` composer update ```. 25 | 26 | ### Использование 27 | 28 | ```php 29 | use ArtemsWay\CommerceML\CommerceML; 30 | 31 | class ExampleController extends Controller { 32 | 33 | public function exampleAction() { 34 | $importFile = storage_path('import0_1.xml'); 35 | $offersFile = storage_path('offers0_1.xml'); 36 | 37 | $reader = new CommerceML($importFile, $offersFile); 38 | $data = $reader->getData(); 39 | } 40 | 41 | } 42 | ``` 43 | 44 | ### Выходные данные ``` var_dump($data) ``` 45 | 46 | ```php 47 | array:6 [▼ 48 | "importTime" => "2016-03-01T01:03:19" 49 | "onlyChanges" => false 50 | "categories" => array:218 [▶] 51 | "properties" => array:608 [▶] 52 | "priceTypes" => array:1 [▶] 53 | "products" => array:4535 [▶] 54 | ] 55 | ``` -------------------------------------------------------------------------------- /src/CommerceML.php: -------------------------------------------------------------------------------- 1 | importXml = $this->loadXml($import); 79 | $this->offersXml = $this->loadXml($offers); 80 | 81 | $this->parse(); 82 | } 83 | 84 | /** 85 | * Load XML from file. 86 | * 87 | * @param string $path 88 | * @throws CommerceMLException 89 | * 90 | * @return \SimpleXMLElement 91 | */ 92 | private function loadXml($path) 93 | { 94 | if (!is_file($path)) { 95 | throw new CommerceMLException("Wrong file path: {$path}."); 96 | } 97 | 98 | libxml_use_internal_errors(true); 99 | 100 | $importXml = simplexml_load_file($path); 101 | 102 | if ($error = libxml_get_last_error()) { 103 | throw new CommerceMLException("Simple xml load file error: {$error->message}."); 104 | } 105 | 106 | if (!$importXml) { 107 | throw new CommerceMLException("File was not loaded: {$importXml}."); 108 | } 109 | 110 | return $importXml; 111 | } 112 | 113 | /** 114 | * Parsing xml files. 115 | * 116 | * @return void 117 | */ 118 | private function parse() 119 | { 120 | $this->parseImportTime(); 121 | $this->parseOnlyChanges(); 122 | 123 | $this->parseCategories(); 124 | $this->parseProperties(); 125 | 126 | $this->parsePriceTypes(); 127 | 128 | $this->parseProducts(); 129 | } 130 | 131 | /** 132 | * Parse import time. 133 | * 134 | * @throws CommerceMLException 135 | * @return void 136 | */ 137 | private function parseImportTime() 138 | { 139 | $importTime = $this->importXml['ДатаФормирования']; 140 | 141 | if (!$importTime) { 142 | throw new CommerceMLException('Attribute was not set: ДатаФормировния.'); 143 | } 144 | 145 | $this->importTime = (string)$importTime; 146 | } 147 | 148 | /** 149 | * Parse contains only changes. 150 | * 151 | * @throws CommerceMLException 152 | * @return void 153 | */ 154 | private function parseOnlyChanges() 155 | { 156 | $onlyChanges = $this->importXml->Каталог['СодержитТолькоИзменения']; 157 | 158 | if (!$onlyChanges) { 159 | throw new CommerceMLException('Attribute was not set: "СодержитТолькоИзменения".'); 160 | } 161 | 162 | $this->onlyChanges = (string)$onlyChanges; 163 | } 164 | 165 | /** 166 | * Parse categories. 167 | * 168 | * @param \SimpleXMLElement|null $xmlCategories 169 | * @param \SimpleXMLElement|null $parent 170 | * 171 | * @throws CommerceMLException 172 | * @return void 173 | */ 174 | private function parseCategories($xmlCategories = null, $parent = null) 175 | { 176 | if (is_null($xmlCategories)) { 177 | if (!$xmlCategories = $this->importXml->Классификатор->Группы) { 178 | throw new CommerceMLException('Categories not found.'); 179 | } 180 | } 181 | 182 | foreach ($xmlCategories->Группа as $xmlCategory) { 183 | $category = new Category($xmlCategory); 184 | 185 | if (!is_null($parent)) { 186 | $parent->addChild($category); 187 | } 188 | 189 | $this->categories[$category->id] = $category; 190 | 191 | if ($xmlCategory->Группы) { 192 | $this->parseCategories($xmlCategory->Группы, $category); 193 | } 194 | } 195 | } 196 | 197 | /** 198 | * Parse properties. 199 | * 200 | * @return void 201 | */ 202 | private function parseProperties() 203 | { 204 | if ($this->importXml->Классификатор->Свойства) { 205 | foreach ($this->importXml->Классификатор->Свойства->Свойство as $xmlProperty) { 206 | $property = new Property($xmlProperty); 207 | $this->properties[$property->id] = $property; 208 | } 209 | 210 | } 211 | } 212 | 213 | /** 214 | * Parse price types. 215 | * 216 | * @return void 217 | */ 218 | private function parsePriceTypes() 219 | { 220 | if ($this->offersXml->ПакетПредложений->ТипыЦен) { 221 | foreach ($this->offersXml->ПакетПредложений->ТипыЦен->ТипЦены as $xmlPriceType) { 222 | $priceType = new PriceType($xmlPriceType); 223 | $this->priceTypes[$priceType->id] = $priceType; 224 | } 225 | } 226 | } 227 | 228 | /** 229 | * Parse products. 230 | */ 231 | private function parseProducts() 232 | { 233 | $buffer = [ 234 | 'products' => [] 235 | ]; 236 | 237 | $products = $this->importXml->Каталог->Товары; 238 | $offers = $this->offersXml->ПакетПредложений->Предложения; 239 | 240 | if (!$products) throw new CommerceMLException('Products not found.'); 241 | if (!$offers) throw new CommerceMLException('Offers not found.'); 242 | 243 | // Parse products in import.xml. 244 | foreach ($products->Товар as $product) { 245 | $productId = (string)$product->Ид; 246 | $buffer['products'][$productId]['import'] = $product; 247 | } 248 | 249 | // Parse offers in offers.xml. 250 | foreach ($offers->Предложение as $offer) { 251 | $productId = (string)$offer->Ид; 252 | $buffer['products'][$productId]['offer'] = $offer; 253 | } 254 | 255 | // Merge import and offer to one product. 256 | foreach ($buffer['products'] as $item) { 257 | $import = isset($item['import']) ? $item['import'] : null; 258 | $offer = isset($item['offer']) ? $item['offer'] : null; 259 | 260 | if (is_null($import) || is_null($offer)) { 261 | continue; 262 | } 263 | 264 | $product = new Product($import, $offer); 265 | 266 | $this->products[$product->id] = $product; 267 | 268 | // Associate properties with the category. 269 | if (count($properties = $product->properties)) { 270 | $this->addPropertiesToCategory( 271 | $product->category, 272 | array_keys($properties) 273 | ); 274 | } 275 | } 276 | } 277 | 278 | /** 279 | * Add properties to the category. 280 | * 281 | * @param string $id 282 | * @param array $properties 283 | * 284 | * @return void 285 | */ 286 | private function addPropertiesToCategory($id, $properties) 287 | { 288 | if (isset($this->categories[$id])) { 289 | $properties = array_merge($this->categories[$id]->properties, $properties); 290 | $this->categories[$id]->properties = array_unique($properties); 291 | } 292 | } 293 | 294 | /** 295 | * Get import time. 296 | * 297 | * @return string 298 | */ 299 | public function getImportTime() 300 | { 301 | return $this->importTime; 302 | } 303 | 304 | /** 305 | * Check if file contains changes only. 306 | * 307 | * @return bool 308 | */ 309 | public function getOnlyChanges() 310 | { 311 | return $this->onlyChanges == 'true'; 312 | } 313 | 314 | /** 315 | * Get categories. 316 | * 317 | * @return array|Category[] 318 | */ 319 | public function getCategories() 320 | { 321 | return $this->categories; 322 | } 323 | 324 | /** 325 | * Get properties. 326 | * 327 | * @return array|Property[] 328 | */ 329 | public function getProperties() 330 | { 331 | return $this->properties; 332 | } 333 | 334 | /** 335 | * Get price types. 336 | * 337 | * @return array|PriceType[] 338 | */ 339 | public function getPriceTypes() 340 | { 341 | return $this->priceTypes; 342 | } 343 | 344 | /** 345 | * Get products. 346 | * 347 | * @return array|Product[] 348 | */ 349 | public function getProducts() 350 | { 351 | return $this->products; 352 | } 353 | 354 | /** 355 | * Get all parsed data. 356 | * 357 | * @return array 358 | */ 359 | public function getData() 360 | { 361 | return [ 362 | "importTime" => $this->getImportTime(), 363 | "onlyChanges" => $this->getOnlyChanges(), 364 | "categories" => $this->getCategories(), 365 | "properties" => $this->getProperties(), 366 | "priceTypes" => $this->getPriceTypes(), 367 | "products" => $this->getProducts(), 368 | ]; 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /src/Exceptions/CommerceMLException.php: -------------------------------------------------------------------------------- 1 | loadImport($importXml); 35 | } 36 | 37 | /** 38 | * Load category data from import.xml. 39 | * 40 | * @param \SimpleXMLElement $xml 41 | * @return void 42 | */ 43 | public function loadImport($xml) 44 | { 45 | $this->id = (string)$xml->Ид; 46 | 47 | $this->name = (string)$xml->Наименование; 48 | } 49 | 50 | /** 51 | * Add children category. 52 | * 53 | * @param Category $category 54 | * @return void 55 | */ 56 | public function addChild($category) 57 | { 58 | $category->parent = $this->id; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Models/Model.php: -------------------------------------------------------------------------------- 1 | {$key}; 24 | } 25 | 26 | return null; 27 | } 28 | 29 | /** 30 | * Set object value. 31 | * 32 | * @param string $key 33 | * @param mixed $val 34 | * @return bool 35 | */ 36 | public function set($key, $val) 37 | { 38 | if (property_exists($this, $key)) { 39 | $this->{$key} = $val; 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Models/PriceType.php: -------------------------------------------------------------------------------- 1 | loadImport($xmlPriceType); 28 | } 29 | 30 | /** 31 | * @param SimpleXMLElement [$xmlPriceType] 32 | * @return void 33 | */ 34 | private function loadImport($xmlPriceType) 35 | { 36 | $this->id = (string)$xmlPriceType->Ид; 37 | 38 | $this->type = (string)$xmlPriceType->Наименование; 39 | 40 | $this->currency = (string)$xmlPriceType->Валюта; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Models/Product.php: -------------------------------------------------------------------------------- 1 | name = ''; 76 | $this->quantity = 0; 77 | $this->description = ''; 78 | 79 | $this->loadImport($importXml); 80 | $this->loadOffers($offersXml); 81 | } 82 | 83 | /** 84 | * Load primary data from import.xml. 85 | * 86 | * @param \SimpleXMLElement $xml 87 | * 88 | * @throws CommerceMLException 89 | * @return void 90 | */ 91 | public function loadImport($xml) 92 | { 93 | $this->id = trim($xml->Ид); 94 | 95 | $this->name = trim($xml->Наименование); 96 | $this->description = trim($xml->Описание); 97 | 98 | $this->sku = trim($xml->Артикул); 99 | $this->unit = trim($xml->БазоваяЕдиница); 100 | 101 | if (!$xml->Группы) { 102 | throw new CommerceMLException("The product has no category: {$this->id}"); 103 | } 104 | 105 | $this->category = (string)$xml->Группы->Ид; 106 | 107 | if ($xml->ЗначенияРеквизитов) { 108 | foreach ($xml->ЗначенияРеквизитов->ЗначениеРеквизита as $value) { 109 | $name = (string)$value->Наименование; 110 | $this->requisites[$name] = (string)$value->Значение; 111 | } 112 | } 113 | 114 | if ($xml->Картинка) { 115 | $weight = 0; 116 | foreach ($xml->Картинка as $image) { 117 | array_push($this->images, [ 118 | 'path' => (string)$image, 119 | 'weight' => $weight++ 120 | ]); 121 | } 122 | } 123 | 124 | if ($xml->ЗначенияСвойств) { 125 | foreach ($xml->ЗначенияСвойств->ЗначенияСвойства as $prop) { 126 | 127 | $id = (string)$prop->Ид; 128 | $value = (string)$prop->Значение; 129 | 130 | if ($value) { 131 | $this->properties[$id] = $value; 132 | } 133 | } 134 | } 135 | } 136 | 137 | /** 138 | * Load primary data form offers.xml. 139 | * 140 | * @param \SimpleXMLElement $xml 141 | * 142 | * @return void 143 | */ 144 | public function loadOffers($xml) 145 | { 146 | if ($xml->Количество) { 147 | $this->quantity = (int)$xml->Количество; 148 | } 149 | 150 | if ($xml->Цены) { 151 | foreach ($xml->Цены->Цена as $price) { 152 | $id = (string)$price->ИдТипаЦены; 153 | 154 | $this->price[$id] = [ 155 | 'type' => $id, 156 | 'currency' => (string)$price->Валюта, 157 | 'value' => (float)$price->ЦенаЗаЕдиницу 158 | ]; 159 | } 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/Models/Property.php: -------------------------------------------------------------------------------- 1 | loadImport($importXml); 36 | } 37 | } 38 | 39 | /** 40 | * @param \SimpleXMLElement $xml 41 | * @return void 42 | */ 43 | public function loadImport($xml) 44 | { 45 | $this->id = (string)$xml->Ид; 46 | 47 | $this->name = (string)$xml->Наименование; 48 | 49 | $this->type = $this->getType((string)$xml->ТипЗначений); 50 | 51 | if ($this->type === 'voc' && $xml->ВариантыЗначений) { 52 | foreach ($xml->ВариантыЗначений->Справочник as $value) { 53 | $id = (string)$value->ИдЗначения; 54 | $this->values[$id] = (string)$value->Значение; 55 | } 56 | } 57 | } 58 | 59 | public function getType($xmlType) 60 | { 61 | switch ($xmlType) { 62 | case 'Справочник': 63 | $type = 'voc'; 64 | break; 65 | case 'Строка': 66 | $type = 'str'; 67 | break; 68 | case 'Число': 69 | $type = 'int'; 70 | break; 71 | default: 72 | throw new CommerceMLException("Unknown property type: {$xmlType}."); 73 | } 74 | 75 | return $type; 76 | } 77 | } --------------------------------------------------------------------------------