Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
FacebookProductProcessor.php
1<?php
2
4
18use Bitrix\SalesCenter\Integration\LandingManager;
19
20use function GuzzleHttp\Psr7\str;
21
23{
24 public const RETAILER_ID_PREFIX = 'bitrix24-product-';
25
27 private ?SkuTree $skuTree = null;
28
29 public function __construct(IblockInfo $iblockInfo)
30 {
31 if ($iblockInfo->canHaveSku())
32 {
33 $this->skuTree = new SkuTree($iblockInfo);
34 }
35 }
36
37 public function validate(BaseSku $sku): Result
38 {
39 $result = new Result();
40
41 if ($sku->isNew())
42 {
43 $result->addError(new Error(sprintf(
44 'New product "%s" must be saved.', $sku->getName()
45 )));
46 }
47
48 $brandProperty = $sku->getParent()->getPropertyCollection()->findByCode(BrandProperty::PROPERTY_CODE);
49 if (!$brandProperty || $brandProperty->getPropertyValueCollection()->isEmpty())
50 {
51 $result->addError(new Error(
52 sprintf('Product "%s"(%s) has no brands.', $sku->getParent()->getName(), $sku->getParent()->getId() ?? 'new'),
53 0,
54 [
55 'productId' => $sku->getParent()->getId(),
56 'field' => 'brand',
57 ]
58 ));
59 }
60
61 if ($sku->getFrontImageCollection()->isEmpty())
62 {
63 $result->addError(new Error(
64 sprintf('Product "%s"(%s) has no images.', $sku->getName(), $sku->getId() ?? 'new'),
65 0,
66 [
67 'productId' => $sku->getId(),
68 'field' => 'image',
69 ]
70 ));
71 }
72
73 if (!$sku->getPriceCollection()->hasBasePrice())
74 {
75 $result->addError(new Error(
76 sprintf('Product "%s"(%s) has no prices.', $sku->getName(), $sku->getId() ?? 'new'),
77 0,
78 [
79 'productId' => $sku->getId(),
80 'field' => 'price',
81 ]
82 ));
83 }
84
85 return $result;
86 }
87
88 public function prepare(BaseSku $sku): Result
89 {
90 $result = new Result();
91
92 if ($sku->isSimple())
93 {
94 $result->setData($this->prepareSimpleProduct($sku));
95 }
96 else
97 {
98 $result->setData($this->prepareVariation($sku));
99 }
100
101 return $result;
102 }
103
104 private function prepareSimpleProduct(BaseSku $sku): array
105 {
106 $fields = $this->getBaseFields($sku);
107
108 return [$sku->getId() => $fields];
109 }
110
111 private function prepareVariation(BaseSku $sku): array
112 {
113 $fields = $this->getBaseFields($sku);
114 $skuParent = $sku->getParent();
115
116 if ($this->skuTree && $skuParent)
117 {
118 $skuTree = $this->skuTree->load([$skuParent->getId()])[$skuParent->getId()];
119 $skuFields = $this->getSkuFields($sku, $skuTree);
120 if (!empty($skuFields))
121 {
122 $fields['data'] += $skuFields;
123 }
124 }
125
126 return [$sku->getId() => $fields];
127 }
128
129 public function getEntityRetailerId(string $id): string
130 {
131 return self::RETAILER_ID_PREFIX . $id;
132 }
133
134 public function getProductIdByRetailerId(string $retailerId): string
135 {
136 return str_replace(self::RETAILER_ID_PREFIX, '', $retailerId);
137 }
138
139 private function getProductRetailerId(BaseIblockElementEntity $entity): string
140 {
141 if ($parent = $entity->getParent())
142 {
143 return $this->getEntityRetailerId($parent->getId());
144 }
145
146 return $this->getEntityRetailerId($entity->getId());
147 }
148
149 private function getBaseFields(BaseSku $variation): array
150 {
151 return [
152 'method' => 'UPDATE',
153 'retailer_id' => self::RETAILER_ID_PREFIX . $variation->getId(),
154 'data' => [
155 'name' => $this->getProductName($variation),
156 'description' => $this->getProductDescription($variation),
157 'retailer_product_group_id' => $this->getProductRetailerId($variation),
158 'url' => $this->getProductUrl($variation),
159 'image_url' => $this->getProductImageUrl($variation),
160 // 'category' => 't-shirts',
161 // todo GTIN/MPN/brand
162 'brand' => $this->getProductBrand($variation),
163 'condition' => 'new',
164 'availability' => 'in stock',
165 'price' => $this->getProductPrice($variation),
166 'currency' => $this->getProductCurrency($variation),
167 ],
168 ];
169 }
170
171 private function getSkuFields(BaseSku $variation, array $skuTree): array
172 {
173 $offerTree = $this->getOfferTree($variation->getId(), $skuTree);
174 if (empty($offerTree) || empty($offerTree['DISPLAY_PROPERTIES']))
175 {
176 return [];
177 }
178
179 $fields = [];
180
181 [$color, $size, $custom] = $this->extractVariants($offerTree);
182
183 if (!empty($color))
184 {
185 $fields['color'] = $color;
186 }
187
188 if (!empty($size))
189 {
190 $fields['size'] = $size;
191 }
192
193 if (!empty($custom))
194 {
195 $fields['additional_variant_attributes'] = $custom;
196 }
197
198 return $fields;
199 }
200
201 private function getOfferTree(int $variationId, array $skuTree): ?array
202 {
203 foreach ($skuTree['OFFERS'] as $offer)
204 {
205 if ((int)$offer['ID'] === $variationId)
206 {
207 return $offer;
208 }
209 }
210
211 return null;
212 }
213
214 private function extractVariants(array $offerTree): array
215 {
216 $color = null;
217 $size = null;
218 $custom = [];
219
220 foreach ($offerTree['DISPLAY_PROPERTIES'] as $code => $property)
221 {
222 if ($color === null && $code === 'COLOR_REF')
223 {
224 $color = $property['DISPLAY_VALUE'];
225 }
226 elseif ($size === null && ($code === 'SIZES_CLOTHES' || $code === 'SIZES_SHOES'))
227 {
228 $size = $property['DISPLAY_VALUE'];
229 }
230 else
231 {
232 $custom[$property['NAME']] = $property['DISPLAY_VALUE'];
233 }
234 }
235
236 return [$color, $size, $custom];
237 }
238
239 private function getProductName(BaseSku $variation): string
240 {
241 return $this->truncateSentence($variation->getName(), 100);
242 }
243
244 private function getProductDescription(BaseSku $variation): string
245 {
246 $description = $variation->getField('DETAIL_TEXT');
247 if ($description)
248 {
249 if ($variation->getField('DETAIL_TEXT_TYPE') === 'html')
250 {
251 $description = HTMLToTxt($description);
252 }
253
254 return $this->truncateSentence($description, 5000);
255 }
256
257 $description = $variation->getField('PREVIEW_TEXT');
258 if ($description)
259 {
260 if ($variation->getField('PREVIEW_TEXT_TYPE') === 'html')
261 {
262 $description = HTMLToTxt($description);
263 }
264
265 return $this->truncateSentence($description, 5000);
266 }
267
268 return $variation->getName();
269 }
270
271 private function getProductImageUrl(BaseSku $product): string
272 {
274 $image = $product->getFrontImageCollection()->getFirst();
275 if ($image && $image->getSource())
276 {
277 return $this->getCurrentUri($image->getSource());
278 }
279
280 return '';
281 }
282
283 private function getCurrentUri(string $path): string
284 {
285 if (strpos($path, 'http') === 0)
286 {
287 return $path;
288 }
289
290 $server = Context::getCurrent()->getServer();
291 $request = Context::getCurrent()->getRequest();
292
293 $uri = $request->isHttps() ? 'https://' : 'http://';
294 $uri .= $server->getServerName();
295 $uri .= (
296 (int)$server->getServerPort() === 80
297 || ($server->get('HTTPS') && (int)$server->getServerPort() === 443)
298 )
299 ? ''
300 : ':' . $server->getServerPort();
301 $uri .= $path;
302
303 return (new Uri($uri))->getUri();
304 }
305
306 private function getProductPrice(BaseSku $entity): string
307 {
308 $basePrice = $entity->getPriceCollection()->findBasePrice();
309 if ($basePrice)
310 {
311 return $basePrice->getPrice() * 100;
312 }
313
314 return 0;
315 }
316
317 private function getProductCurrency(BaseSku $entity): string
318 {
319 $basePrice = $entity->getPriceCollection()->findBasePrice();
320 if ($basePrice)
321 {
322 return $basePrice->getCurrency();
323 }
324
325 return \CCrmCurrency::GetDefaultCurrencyID();
326 }
327
328 private function truncateSentence(string $sentence, int $limit): string
329 {
330 if ($limit <= 0)
331 {
332 return $sentence;
333 }
334
335 if (mb_strlen($sentence) <= $limit)
336 {
337 return $sentence;
338 }
339
340 return mb_substr($sentence, 0, mb_strrpos(mb_substr($sentence, 0, $limit + 1), ' '));
341 }
342
343 private function getProductUrl(BaseSku $variation): string
344 {
345 if (
346 !Loader::includeModule('salescenter')
347 || !Loader::includeModule('landing'))
348 {
349 return '';
350 }
351
352 $product = $variation->getParent();
353 if ($product)
354 {
355 $siteUrlInfo = LandingManager::getInstance()->getCollectionPublicUrlInfo();
356 $siteUrl = $siteUrlInfo['url'] ?? null;
357 $productDetailUrl = \Bitrix\Landing\PublicAction\Utils::getIblockURL($product->getId(), 'detail');
358
359 if ($siteUrl && $productDetailUrl)
360 {
361 return str_replace('#system_catalog', $siteUrl, $productDetailUrl);
362 }
363 }
364
365 return '';
366 }
367
368 private function getProductBrand(BaseSku $variation): string
369 {
371 $product = $variation->getParent();
372 if ($product)
373 {
374 $brandProperty = $product->getPropertyCollection()->findByCode(BrandProperty::PROPERTY_CODE);
375 if ($brandProperty)
376 {
377 $names = $this->getBrandNames($brandProperty);
378
379 return implode(', ', $names);
380 }
381 }
382
383 return '';
384 }
385
386 private function getBrandNames(Property $property): array
387 {
388 if (!Loader::includeModule('highloadblock'))
389 {
390 return [];
391 }
392
393 $userTypeSettings = $property->getSetting('USER_TYPE_SETTINGS');
394 $tableName = $userTypeSettings['TABLE_NAME'] ?? '';
395
396 if (empty($tableName))
397 {
398 return [];
399 }
400
401 $brandValues = $property->getPropertyValueCollection()->toArray();
402 if (empty($brandValues))
403 {
404 return [];
405 }
406
407 $brandValues = array_column($brandValues, null, 'VALUE');
408
409 return array_intersect_key($this->loadBrandNamesMap($tableName), $brandValues);
410 }
411
412 private function loadBrandNamesMap(string $tableName): array
413 {
414 static $nameMap = [];
415
416 if (!isset($nameMap[$tableName]))
417 {
418 $tableMap = [];
419
420 $highLoadBlock = HighloadBlockTable::getList([
421 'filter' => ['=TABLE_NAME' => $tableName],
422 ])
423 ->fetch()
424 ;
425 if (!empty($highLoadBlock))
426 {
427 $entity = HighloadBlockTable::compileEntity($highLoadBlock);
428 $entityDataClass = $entity->getDataClass();
429 $directoryData = $entityDataClass::getList();
430 while ($element = $directoryData->fetch())
431 {
432 $tableMap[$element['UF_XML_ID']] = $element['UF_NAME'];
433 }
434 }
435
436 $nameMap[$tableName] = $tableMap;
437 }
438
439 return $nameMap[$tableName];
440 }
441}
static getCurrent()
Definition context.php:241