Current File : //var/www/prestashop/modules/ps_facebook/classes/Provider/EventDataProvider.php |
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 3.0
* that is bundled with this package in the file LICENSE.md.
* It is also available through the world-wide-web at this URL:
* https://opensource.org/licenses/AFL-3.0
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\PrestashopFacebook\Provider;
use Cart;
use Context;
use Currency;
use Order;
use PrestaShop\Module\PrestashopFacebook\Adapter\ConfigurationAdapter;
use PrestaShop\Module\PrestashopFacebook\Adapter\ToolsAdapter;
use PrestaShop\Module\PrestashopFacebook\Repository\GoogleCategoryRepository;
use PrestaShop\Module\PrestashopFacebook\Repository\ProductRepository;
use PrestaShop\Module\Ps_facebook\Utility\CustomerInformationUtility;
use PrestaShop\Module\Ps_facebook\Utility\ProductCatalogUtility;
use PrestaShopException;
use Product;
use Ps_facebook;
class EventDataProvider
{
public const PRODUCT_TYPE = 'product';
public const CATEGORY_TYPE = 'product_group';
/**
* @var Context
*/
private $context;
private $locale;
/**
* @var ToolsAdapter
*/
private $toolsAdapter;
/**
* @var ConfigurationAdapter
*/
private $configurationAdapter;
/**
* @var ProductRepository
*/
private $productRepository;
/**
* @var ps_facebook
*/
private $module;
/**
* @var ProductAvailabilityProviderInterface
*/
private $availabilityProvider;
/**
* @var GoogleCategoryRepository
*/
private $googleCategoryRepository;
/**
* @var GoogleCategoryProvider
*/
private $googleCategoryProvider;
public function __construct(
ToolsAdapter $toolsAdapter,
ConfigurationAdapter $configurationAdapter,
ProductRepository $productRepository,
Context $context,
ps_facebook $module,
ProductAvailabilityProviderInterface $availabilityProvider,
GoogleCategoryRepository $googleCategoryRepository,
GoogleCategoryProvider $googleCategoryProvider
) {
$this->toolsAdapter = $toolsAdapter;
$this->context = $context;
$this->locale = \Tools::strtoupper($this->context->language->iso_code);
$this->configurationAdapter = $configurationAdapter;
$this->productRepository = $productRepository;
$this->module = $module;
$this->availabilityProvider = $availabilityProvider;
$this->googleCategoryRepository = $googleCategoryRepository;
$this->googleCategoryProvider = $googleCategoryProvider;
}
public function generateEventData($name, array $params)
{
switch ($name) {
case 'hookDisplayHeader':
if (true === \Tools::isSubmit('submitCustomizedData')) {
return $this->getCustomEventData();
}
if ($this->context->controller instanceof \ProductControllerCore) {
return $this->getProductPageData();
}
if ($this->context->controller instanceof \CategoryControllerCore) {
return $this->getCategoryPageData();
}
if ($this->context->controller instanceof \CmsControllerCore) {
return $this->getCMSPageData();
}
break;
case 'hookActionSearch':
return $this->getSearchEventData($params);
case 'hookActionObjectCustomerMessageAddAfter':
return $this->getContactEventData();
case 'hookDisplayOrderConfirmation':
return $this->getOrderConfirmationEvent($params);
case 'InitiateCheckout':
return $this->getInitiateCheckoutEvent();
case 'hookActionCartSave':
return $this->getAddToCartEventData();
case 'hookActionNewsletterRegistrationAfter':
return $this->getShopSubscriptionEvent($params);
case 'hookActionCustomerAccountAdd':
return $this->getCompleteRegistrationEventData();
case 'customizeProduct':
return $this->getCustomisationEventData($params);
case 'hookActionFacebookCallPixel':
return $this->getCustomEvent($params);
}
return false;
}
private function getProductPageData()
{
$type = 'ViewContent';
/** @var \ProductControllerCore $controller */
$controller = $this->context->controller;
$product = $controller->getTemplateVarProduct();
$fbProductId = ProductCatalogUtility::makeProductId(
$product['id_product'],
$product['id_product_attribute']
);
$productUrl = $this->context->link->getProductLink($product['id']);
$categoryPath = $this->googleCategoryProvider->getCategoryPaths(
$product['id_category_default'],
$this->context->language->id,
$this->context->shop->id
);
$content = [
'id' => $fbProductId,
'title' => \Tools::replaceAccentedChars($product['name']),
'category' => $categoryPath['category_path'],
'item_price' => $product['price_tax_exc'],
'brand' => (new \Manufacturer($product['id_manufacturer']))->name,
];
$customData = [
'currency' => $this->getCurrency(),
'content_ids' => [$fbProductId],
'contents' => [$content],
'content_type' => self::PRODUCT_TYPE,
'value' => $product['price_tax_exc'],
];
$category = $this->googleCategoryRepository->getGoogleCategoryIdByCategoryId(
$product['id_category_default'],
$this->context->shop->id
) ?: '';
$this->context->smarty->assign(
[
'retailer_item_id' => $fbProductId,
'product_availability' => $this->availabilityProvider->getProductAvailability(
(int) $product['id_product'],
(int) $product['id_product_attribute']
),
'item_group_id' => $category,
]
);
return [
'custom_data' => $customData,
'event_source_url' => $productUrl,
] + $this->getCommonData($type);
}
private function getCategoryPageData()
{
$type = 'ViewCategory';
/** @var \CategoryControllerCore $controller */
$controller = $this->context->controller;
$category = $controller->getCategory();
$page = $this->toolsAdapter->getValue('page');
$resultsPerPage = $this->configurationAdapter->get('PS_PRODUCTS_PER_PAGE');
$prods = $category->getProducts($this->context->language->id, $page, $resultsPerPage);
$categoryUrl = $this->context->link->getCategoryLink($category->id);
$breadcrumbs = $controller->getBreadcrumbLinks();
$breadcrumb = implode(' > ', array_column($breadcrumbs['links'], 'title'));
$contentIds = [];
if ($prods) {
foreach ($prods as $product) {
$contentIds[] = ProductCatalogUtility::makeProductId(
$product['id_product'],
$product['id_product_attribute']
);
}
}
$customData = [
'content_name' => \Tools::replaceAccentedChars($category->name) . ' ' . $this->locale,
'content_category' => \Tools::replaceAccentedChars($breadcrumb),
'content_type' => self::CATEGORY_TYPE,
'content_ids' => $contentIds ?: null,
];
return [
'custom_data' => $customData,
'event_source_url' => $categoryUrl,
] + $this->getCommonData($type);
}
private function getCMSPageData()
{
$type = 'ViewCMS';
$cms = new \CMS((int) $this->toolsAdapter->getValue('id_cms'), $this->context->language->id);
/** @var \CmsControllerCore $controller */
$controller = $this->context->controller;
$breadcrumbs = $controller->getBreadcrumbLinks();
$breadcrumb = implode(' > ', array_column($breadcrumbs['links'], 'title'));
$customData = [
'content_name' => \Tools::replaceAccentedChars($cms->meta_title) . ' ' . $this->locale,
'content_category' => \Tools::replaceAccentedChars($breadcrumb),
'content_type' => self::PRODUCT_TYPE,
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getAddToCartEventData()
{
$action = $this->toolsAdapter->getValue('action');
$quantity = $this->toolsAdapter->getValue('qty');
$idProduct = $this->toolsAdapter->getValue('id_product');
$op = $this->toolsAdapter->getValue('op');
$isDelete = $this->toolsAdapter->getValue('delete');
$idProductAttribute = $this->toolsAdapter->getValue('id_product_attribute');
$attributeGroups = $this->toolsAdapter->getValue('group');
if ($attributeGroups) {
try {
$idProductAttribute = $this->productRepository->getIdProductAttributeByIdAttributes(
$idProduct,
$attributeGroups
);
} catch (PrestaShopException $e) {
return false;
}
}
if ($action !== 'update') {
return false;
}
$type = 'AddToCart';
if ($op) {
$type = $op === 'up' ? 'IncreaseProductQuantityInCart' : 'DecreaseProductQuantityInCart';
} elseif ($isDelete) {
//todo: when removing product from cart this hook gets called twice
$type = 'RemoveProductFromCart';
}
$productName = Product::getProductName($idProduct, $idProductAttribute);
$cartId = $this->context->cookie->id_cart ?? null;
$customData = [
'content_name' => pSQL($productName),
'content_type' => self::PRODUCT_TYPE,
'content_ids' => [
ProductCatalogUtility::makeProductId(
$idProduct,
$idProductAttribute
),
],
'currency' => $this->getCurrency(),
'value' => $this->productRepository->getSalePrice($idProduct, $idProductAttribute, $cartId),
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getCompleteRegistrationEventData()
{
$type = 'CompleteRegistration';
$customData = [
'content_name' => 'authentication',
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getContactEventData()
{
return $this->getCommonData('Contact');
}
private function getCustomisationEventData($params)
{
$type = 'CombinationProduct';
$idLang = (int) $this->context->language->id;
$productId = $this->toolsAdapter->getValue('id_product');
$attributeIds = $params['attributeIds'];
$customData = $this->getCustomAttributeData($productId, $idLang, $attributeIds);
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getCustomEventData()
{
return $this->getCommonData('CustomizeProduct');
}
private function getSearchEventData($params)
{
$searchQuery = $params['searched_query'];
$quantity = $params['total'];
$type = 'Search';
$customData = [
'content_name' => 'searchQuery',
'search_string' => $searchQuery,
];
if ($quantity) {
$customData['num_items'] = $quantity;
}
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getOrderConfirmationEvent($params)
{
/** @var Order $order */
$order = $this->module->psVersionIs17 ? $params['order'] : $params['objOrder'];
$productList = [];
foreach ($order->getCartProducts() as $product) {
$productList[] = ProductCatalogUtility::makeProductId(
$product['id_product'],
$product['id_product_attribute']
);
}
$type = 'Purchase';
$customData = [
'content_name' => 'purchased',
'order_id' => $order->id,
'currency' => $this->getCurrency(),
'content_ids' => $productList,
'content_type' => self::PRODUCT_TYPE,
'value' => (float) ($order->total_paid_tax_excl),
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getInitiateCheckoutEvent()
{
$type = 'InitiateCheckout';
$cart = $this->context->cart;
$contents = $this->getProductContent($cart);
$numberOfItems = array_sum(array_column($contents, 'quantity'));
$customData = [
'contents' => $contents,
'content_type' => 'product',
'currency' => $this->getCurrency(),
'value' => $cart->getOrderTotal(false),
'num_items' => $numberOfItems,
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
private function getShopSubscriptionEvent($params)
{
$type = 'Subscribe';
$customData = [
'content_name' => pSQL($params['email']),
];
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
/**
* @param Cart $cart
*
* @return array
*/
private function getProductContent(Cart $cart)
{
$contents = [];
foreach ($cart->getProducts() as $product) {
$categoryPath = $this->googleCategoryProvider->getCategoryPaths(
$product['id_category_default'],
$this->context->language->id,
$this->context->shop->id
);
$content = [
'id' => ProductCatalogUtility::makeProductId($product['id_product'], $product['id_product_attribute']),
'quantity' => $product['quantity'],
'item_price' => $product['price'],
'title' => \Tools::replaceAccentedChars($product['name']),
'brand' => (new \Manufacturer($product['id_manufacturer']))->name,
'category' => $categoryPath['category_path'],
];
$contents[] = $content;
}
return $contents;
}
/**
* @param array $params
*
* @return array|null
*/
private function getCustomEvent($params)
{
if (!isset($params['eventName']) || !isset($params['module'])) {
return null;
}
$type = pSQL($params['eventName']);
$customData = [
'custom_properties' => [
'module' => pSQL($params['module']),
],
];
if (isset($params['id_product'])) {
$fbProductId = ProductCatalogUtility::makeProductId(
$params['id_product'],
isset($params['id_product_attribute']) ? $params['id_product_attribute'] : 0
);
$customData['content_ids']['module'] = $fbProductId;
}
return [
'custom_data' => $customData,
] + $this->getCommonData($type);
}
/**
* @param int $productId
* @param int $idLang
* @param int[] $attributeIds
*
* @return array
*
* @throws \PrestaShopException
*/
private function getCustomAttributeData($productId, $idLang, $attributeIds)
{
$attributes = [];
foreach ($attributeIds as $attributeId) {
if (class_exists('\ProductAttribute')) {
$attributes[] = (new \ProductAttribute($attributeId, $idLang))->name;
} elseif (class_exists('\AttributeCore')) {
$attributes[] = (new \AttributeCore($attributeId, $idLang))->name;
}
}
try {
$idProductAttribute = $this->productRepository->getIdProductAttributeByIdAttributes(
$productId,
$attributeIds
);
} catch (PrestaShopException $e) {
$idProductAttribute = 0;
}
return [
'content_type' => self::PRODUCT_TYPE,
'content_ids' => [
ProductCatalogUtility::makeProductId($productId, $idProductAttribute),
],
'custom_properties' => [
'custom_attributes' => $attributes,
],
];
}
/**
* Generate the array with data that are used for all events
*
* @see https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events
*
* @param string $eventType
*/
private function getCommonData($eventType)
{
$time = time();
return [
'event_type' => $eventType,
'event_time' => $time,
'user' => CustomerInformationUtility::getCustomerInformationForPixel($this->context->customer),
'eventID' => uniqid($eventType . '_' . $time . '_', true),
];
}
private function getCurrency(): string
{
if (!empty($this->context->currency->iso_code)) {
return \Tools::strtolower($this->context->currency->iso_code);
}
if (!empty($this->context->cookie->id_currency)) {
return \Tools::strtolower((new Currency($this->context->cookie->id_currency))->iso_code);
}
return \Tools::strtolower(Currency::getDefaultCurrency()->iso_code);
}
}