Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
facebookconversion.php
1<?php
2
4
17
19{
20 public const ID = 'id';
21 public const IDS = 'ids';
22 public const NAME = 'name';
23 public const GROUP = 'group';
24 public const PRICE = 'price';
25 public const QUANTITY = 'quantity';
26 public const PRODUCTS_GROUP_AND_QUANTITY = 'productsGroupAndQuantity';
27 public const NAME_AND_PROPERTIES = 'nameAndProperties';
28 public const SOCIAL_NETWORK = 'socialNetwork';
29 public const EMAIL = 'email';
30
31 public static function onAddToCartHandler(int $id, array $productData): void
32 {
33 if (!self::isAllowedRegion())
34 {
35 return;
36 }
37
38 $request = \Bitrix\Main\Context::getCurrent()->getRequest();
39 $action = $request->get('action');
40 if ($action !== 'ADD2BASKET' && $action !== 'BUY')
41 {
42 return;
43 }
44
45 if (!self::checkClass())
46 {
47 return;
48 }
49
50 $params = self::getFacebookConversionParams(Facebook\Event::EVENT_ADD_TO_CART);
51 if (!$params)
52 {
53 return;
54 }
55
56 if ($params[self::ID] !== 'Y')
57 {
58 return;
59 }
60
61 $customDataParams = [
62 'content_type' => 'product',
63 ];
64
65 if ($params[self::NAME] === 'Y')
66 {
67 $customDataParams['content_name'] = $productData['NAME'];
68 }
69 if ($params[self::GROUP] === 'Y')
70 {
71 $customDataParams['content_category'] = self::getProductDeepestSection((int)$productData['PRODUCT_ID']);
72 }
73 if ($params[self::PRICE] === 'Y')
74 {
75 $customDataParams['value'] = $productData['PRICE'];
76 $customDataParams['currency'] = $productData['CURRENCY'];
77 }
78 if ($params[self::QUANTITY] === 'Y')
79 {
80 $customDataParams['contents'] = [
81 [
82 'product_id' => $productData['PRODUCT_ID'],
83 'quantity' => $productData['QUANTITY'],
84 ]
85 ];
86 }
87 else
88 {
89 $customDataParams['content_ids'] = [$productData['PRODUCT_ID']];
90 }
91
92 self::fireEvent(Facebook\Event::EVENT_ADD_TO_CART, $customDataParams);
93 }
94
95 public static function onOrderCreatedHandler(Order $order): void
96 {
97 if (!self::isAllowedRegion())
98 {
99 return;
100 }
101
102 $application = \Bitrix\Main\Application::getInstance();
103 $session = $application ? $application->getSession() : null;
104 $isInitiateCheckoutSent =
105 $session
106 ? $session->has('FACEBOOK_CONVERSION_INITIATE_CHECKOUT_SENT_' . SITE_ID)
107 : false
108 ;
109 if (\Bitrix\Main\Context::getCurrent()->getRequest()->isAjaxRequest())
110 {
111 if (
112 $isInitiateCheckoutSent
113 && \Bitrix\Main\Context::getCurrent()->getRequest()->get('action') === 'saveOrderAjax'
114 )
115 {
116 $session->remove('FACEBOOK_CONVERSION_INITIATE_CHECKOUT_SENT_' . SITE_ID);
117 }
118
119 return;
120 }
121 if ($isInitiateCheckoutSent)
122 {
123 return;
124 }
125 $session->set('FACEBOOK_CONVERSION_INITIATE_CHECKOUT_SENT_' . SITE_ID, true);
126
127 if (!self::checkClass())
128 {
129 return;
130 }
131
132 $params = self::getFacebookConversionParams(Facebook\Event::EVENT_INITIATE_CHECKOUT);
133 if (!$params)
134 {
135 return;
136 }
137
138 if ($params[self::IDS] !== 'Y')
139 {
140 return;
141 }
142
143 $customDataParams = self::getCustomDataParamsForOrderEvent($order, $params);
144
145 if ($params[self::QUANTITY] === 'Y')
146 {
147 $totalQuantity = 0;
149 foreach ($order->getBasket()->getBasketItems() as $basketItem)
150 {
151 $totalQuantity += $basketItem->getQuantity();
152 }
153 $customDataParams['num_items'] = (int)$totalQuantity;
154 }
155
156 self::fireEvent(Facebook\Event::EVENT_INITIATE_CHECKOUT, $customDataParams);
157 }
158
159 public static function onOrderSavedHandler(\Bitrix\Main\Event $event): void
160 {
161 if (!self::isAllowedRegion())
162 {
163 return;
164 }
165
166 if (\Bitrix\Main\Context::getCurrent()->getRequest()->get('action') !== 'saveOrderAjax')
167 {
168 return;
169 }
170
172 $order = $event->getParameter('ENTITY');
173 if (!$order)
174 {
175 return;
176 }
177
178 if (!self::checkClass())
179 {
180 return;
181 }
182
183 $params = self::getFacebookConversionParams(Facebook\Event::EVENT_ADD_PAYMENT);
184 if (!$params)
185 {
186 return;
187 }
188
189 if ($params[self::IDS] !== 'Y')
190 {
191 return;
192 }
193
194 $customDataParams = self::getCustomDataParamsForOrderEvent($order, $params);
195
196 self::fireEvent(Facebook\Event::EVENT_ADD_PAYMENT, $customDataParams);
197 }
198
199 public static function getCustomDataParamsForOrderEvent(Order $order, array $params): array
200 {
201 $customDataParams = [
202 'content_type' => 'product',
203 ];
204
205 if ($params[self::PRODUCTS_GROUP_AND_QUANTITY] === 'Y')
206 {
207 $products = [];
208 foreach ($order->getBasket()->getBasketItems() as $basketItem)
209 {
211 $products[] = [
212 'product_id' => $basketItem->getProductId(),
213 'category' => self::getProductDeepestSection((int)$basketItem->getProductId()),
214 'quantity' => $basketItem->getQuantity(),
215 ];
216 }
217 $customDataParams['contents'] = $products;
218 }
219 else
220 {
221 $productIds = [];
222 foreach ($order->getBasket()->getBasketItems() as $basketItem)
223 {
225 $productIds[] = $basketItem->getProductId();
226 }
227 $customDataParams['content_ids'] = $productIds;
228 }
229
230 if ($params[self::PRICE] === 'Y')
231 {
232 $customDataParams['value'] = $order->getPrice();
233 $customDataParams['currency'] = $order->getCurrency();
234 }
235
236 return $customDataParams;
237 }
238
239 public static function onFeedbackFormContactHandler(\Bitrix\Main\Event $event): void
240 {
241 if (!self::isAllowedRegion())
242 {
243 return;
244 }
245
246 $email = $event->getParameter('AUTHOR_EMAIL') ?? '';
247 self::fireContactEvent(self::EMAIL, (string)$email);
248 }
249
250 public static function onContactHandler($contactBy): void
251 {
252 if (!self::isAllowedRegion())
253 {
254 return;
255 }
256
257 $type = null;
258 $value = null;
259 if (is_array($contactBy))
260 {
261 $type = $contactBy['type'] ?? '';
262 $value = $contactBy['value'] ?? '';
263 }
264 else
265 {
266 $type = self::EMAIL;
267 $value = $contactBy;
268 }
269
270 self::fireContactEvent((string)$type, (string)$value);
271 }
272
273 private static function fireContactEvent(string $type, string $value): void
274 {
275 if (!self::checkClass())
276 {
277 return;
278 }
279
280 $params = self::getFacebookConversionParams(Facebook\Event::EVENT_CONTACT);
281 if (!$params)
282 {
283 return;
284 }
285
286 if (!isset($params[$type]) || $params[$type] !== 'Y')
287 {
288 return;
289 }
290
291 $customDataParams['content_name'] = $value;
292
293 self::fireEvent(Facebook\Event::EVENT_CONTACT, $customDataParams);
294 }
295
296 public static function onCustomizeProductHandler(int $offerId): void
297 {
298 if (!self::isAllowedRegion())
299 {
300 return;
301 }
302
303 if (!self::checkClass())
304 {
305 return;
306 }
307
308 $params = self::getFacebookConversionParams(Facebook\Event::EVENT_DONATE);
309 if (!$params)
310 {
311 return;
312 }
313
314 if ($params[self::ID] !== 'Y')
315 {
316 return;
317 }
318
319 $customDataParams = [
320 'content_type' => 'product',
321 'content_ids' => [$offerId],
322 ];
323
324 if ($params[self::NAME_AND_PROPERTIES] === 'Y')
325 {
326 $skuPropertiesTextValue = self::getSkuNameAndPropertiesTextValue($offerId);
327 if ($skuPropertiesTextValue)
328 {
329 $customDataParams['content_name'] = $skuPropertiesTextValue;
330 }
331 }
332
333 self::fireEvent(Facebook\Event::EVENT_DONATE, $customDataParams);
334 }
335
336 private static function getSkuNameAndPropertiesTextValue(int $offerId): ?string
337 {
339 $skuEntity = ServiceContainer::getRepositoryFacade()->loadVariation($offerId);
340 if (!$skuEntity)
341 {
342 return null;
343 }
344
345 $skuTree = self::getProductSkuTree($skuEntity);
346 if (!$skuTree)
347 {
348 return null;
349 }
350
351 $selectedValues = $skuTree['SELECTED_VALUES'];
352 $offersProp = $skuTree['OFFERS_PROP'];
353
354 $skuProperties = [];
355 foreach ($offersProp as $property)
356 {
357 $selectedValueId = $selectedValues[$property['ID']];
358 if ($selectedValueId === 0)
359 {
360 continue;
361 }
362
363 $filteredValues = array_filter(
364 $property['VALUES'],
365 static function($valuesElement) use($selectedValueId) {
366 return $valuesElement['ID'] === $selectedValueId;
367 }
368 );
369 $skuProperties[] = $filteredValues[array_key_first($filteredValues)]['NAME'];
370 }
371
372 return $skuEntity->getName() . ' ' . implode(', ', $skuProperties);
373 }
374
375 private static function getProductSkuTree(BaseSku $skuEntity): ?array
376 {
378 $skuTreeComponent = ServiceContainer::make('sku.tree', ['iblockId' => $skuEntity->getIblockId()]);
379 if (!$skuTreeComponent)
380 {
381 return null;
382 }
383
385 $productEntity = $skuEntity->getParent();
386 if (!$productEntity)
387 {
388 return null;
389 }
390 $productId = $productEntity->getId();
391
392 $skuIds = array_column($productEntity->getSkuCollection()->toArray(), 'ID');
393 $productsSkuTree = $skuTreeComponent->loadWithSelectedOffers(
394 [$productId => $skuIds]
395 );
396
397 return $productsSkuTree[$productId][$skuEntity->getId()];
398 }
399
400 private static function fireEvent(string $eventName, array $customDataParams): void
401 {
402 $conversion = self::getConversionEntity($eventName, $customDataParams);
403
404 Application::getInstance()->addBackgroundJob(
405 function() use ($conversion, $eventName) {
406 try
407 {
408 $isEventSent = $conversion->fireEvents();
409 if ($isEventSent)
410 {
411 $analyticsEvent = new Analytics\Events\Event(
412 Analytics\Events\Event::FACEBOOK_CONVERSION_EVENT_FIRED,
413 [
414 'EVENT_NAME' => $eventName,
415 ]
416 );
417
418 $provider = new Analytics\Events\Provider($analyticsEvent);
419 (new Analytics\Storage($provider))->save();
420 }
421 }
422 catch (\Throwable $throwable)
423 {
424 }
425 }
426 );
427 }
428
429 private static function getConversionEntity(
430 string $eventName,
431 array $customDataParams
432 ): ?Facebook\Conversion
433 {
434 $service = self::getService();
435 if (!$service)
436 {
437 return null;
438 }
439
440 $customData = new Facebook\CustomData($customDataParams);
441 $userData = new Facebook\UserData([
442 'client_ip_address' => $_SERVER['REMOTE_ADDR'],
443 'client_user_agent' => $_SERVER['HTTP_USER_AGENT'],
444 ]);
445 $event = new Facebook\Event([
446 'event_name' => $eventName,
447 'custom_data' => $customData,
448 'user_data' => $userData,
449 'event_source_url' => self::getSiteUrl(),
450 ]);
451 $conversion = new Facebook\Conversion($service);
452 $conversion->addEvent($event);
453
454 return $conversion;
455 }
456
457 private static function getSiteUrl(): string
458 {
459 static $url = null;
460 if ($url !== null)
461 {
462 return $url;
463 }
464
465 $request = \Bitrix\Main\Context::getCurrent()->getRequest();
466 $protocol = $request->isHttps() ? 'https://' : 'http://';
467 $url = $protocol . $request->getHttpHost();
468
469 $site = \Bitrix\Main\SiteTable::getList([
470 'select' => ['LID', 'DIR'],
471 'filter' => [
472 'LID' => SITE_ID,
473 ],
474 'limit' => 1,
475 'cache' => ['ttl' => 86400],
476 ])->fetch();
477 if ($site)
478 {
479 $url .= $site['DIR'];
480 }
481
482 return $url;
483 }
484
485 private static function getProductDeepestSection(int $productId):? string
486 {
488 $variation = ServiceContainer::getRepositoryFacade()->loadVariation($productId);
489 if (!$variation)
490 {
491 return null;
492 }
493
495 $product = $variation->getParent();
496 if (!$product)
497 {
498 return null;
499 }
500
501 $sectionIds = $product->getSectionCollection()->getValues();
502 if (!empty($sectionIds))
503 {
504 $sectionData = \CIBlockSection::GetList(
505 ['DEPTH_LEVEL' => 'DESC'],
506 ['ID' => $sectionIds],
507 false,
508 ['ID', 'NAME']
509 )->Fetch();
510 if ($sectionData)
511 {
512 return $sectionData['NAME'];
513 }
514 }
515
516 return null;
517 }
518
519 private static function checkClass(): bool
520 {
521 return self::checkModules() && self::getService();
522 }
523
524 private static function checkModules(): bool
525 {
526 return
527 Loader::includeModule('seo')
528 && Loader::includeModule('socialservices')
529 && Loader::includeModule('sale')
530 ;
531 }
532
533 private static function getService(): ?Service
534 {
535 return ServiceLocator::getInstance()->get('seo.business.service') ?: null;
536 }
537
538 private static function getFacebookConversionParamsData(string $eventName):? array
539 {
540 $facebookConversionParamsData = FacebookConversionParamsTable::getList([
541 'filter' => [
542 '=EVENT_NAME' => $eventName,
543 '=LID' => SITE_ID,
544 '=ENABLED' => 'Y',
545 ],
546 ])->fetch();
547
548 return $facebookConversionParamsData ?: null;
549 }
550
551 private static function getFacebookConversionParams(string $eventName): ?array
552 {
553 $facebookConversionParamsData = self::getFacebookConversionParamsData($eventName);
554 if (!$facebookConversionParamsData || $facebookConversionParamsData['ENABLED'] !== 'Y')
555 {
556 return null;
557 }
558
559 $params = unserialize($facebookConversionParamsData['PARAMS'], ['allowed_classes' => false]);
560 if (!$params)
561 {
562 return null;
563 }
564
565 return $params;
566 }
567
568 private static function isAllowedRegion(): bool
569 {
570 $region = \Bitrix\Main\Application::getInstance()->getLicense()->getRegion();
571
572 return $region !== null && $region !== 'ru';
573 }
574
575 public static function isEventEnabled(string $eventName): bool
576 {
577 if (!self::isAllowedRegion())
578 {
579 return false;
580 }
581
582 static $isEventEnabled;
583 if (isset($isEventEnabled[$eventName]))
584 {
585 return $isEventEnabled[$eventName];
586 }
587
588 $facebookConversionParams = self::getFacebookConversionParamsData($eventName);
589 $isEventEnabled[$eventName] =
590 isset($facebookConversionParams['ENABLED']) && $facebookConversionParams['ENABLED'] === 'Y'
591 ;
592
593 return $isEventEnabled[$eventName];
594 }
595
596 public static function OnSiteDeleteHandler(string $lid): void
597 {
598 $facebookConversionParamsResult = FacebookConversionParamsTable::getList([
599 'filter' => [
600 '=LID' => $lid,
601 ],
602 ]);
603 while ($facebookConversionParams = $facebookConversionParamsResult->fetch())
604 {
605 FacebookConversionParamsTable::delete($facebookConversionParams['ID']);
606 }
607 }
608}
static getCurrent()
Definition context.php:241
static onAddToCartHandler(int $id, array $productData)
static onFeedbackFormContactHandler(\Bitrix\Main\Event $event)