264 lines
7.3 KiB
PHP
264 lines
7.3 KiB
PHP
<?php
|
|
/**
|
|
* Upsell Service - Handles CUW (UpsellWP) campaign data
|
|
*/
|
|
|
|
class UpsellService
|
|
{
|
|
/**
|
|
* Build a map: product_id => related product ids from CUW (UpsellWP) campaigns.
|
|
* Uses {$wpdb->prefix}cuw_campaigns table.
|
|
*
|
|
* @return array<int, int[]>
|
|
*/
|
|
public static function buildProductMap(): array
|
|
{
|
|
if (!function_exists('get_post_type')) {
|
|
return [];
|
|
}
|
|
|
|
global $wpdb;
|
|
|
|
$table = $wpdb->prefix . 'cuw_campaigns';
|
|
|
|
// Safety: table might not exist
|
|
$exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
|
|
if ($exists !== $table) {
|
|
return [];
|
|
}
|
|
|
|
$rows = $wpdb->get_results("
|
|
SELECT id, type, filters, data
|
|
FROM {$table}
|
|
WHERE enabled = 1
|
|
ORDER BY priority DESC, id DESC
|
|
");
|
|
|
|
if (empty($rows)) {
|
|
return [];
|
|
}
|
|
|
|
$map = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$filters = json_decode((string) $row->filters, true);
|
|
if (!is_array($filters)) {
|
|
continue;
|
|
}
|
|
|
|
$filterProductLists = self::extractProductListsFromFilters($filters);
|
|
|
|
if (empty($filterProductLists)) {
|
|
continue;
|
|
}
|
|
|
|
// For each list: if a product is in that list, relate it to the other products in that list
|
|
foreach ($filterProductLists as $list) {
|
|
foreach ($list as $pid) {
|
|
$related = array_values(array_diff($list, [$pid]));
|
|
if (empty($related)) {
|
|
continue;
|
|
}
|
|
|
|
// Keep only real Woo products
|
|
$related = array_values(array_filter($related, function ($id) {
|
|
$pt = get_post_type($id);
|
|
return $pt === 'product' || $pt === 'product_variation';
|
|
}));
|
|
|
|
if (empty($related)) {
|
|
continue;
|
|
}
|
|
|
|
if (!isset($map[$pid])) {
|
|
$map[$pid] = [];
|
|
}
|
|
$map[$pid] = array_values(array_unique(array_merge($map[$pid], $related)));
|
|
}
|
|
}
|
|
}
|
|
|
|
return $map;
|
|
}
|
|
|
|
/**
|
|
* Build deals map for buy_more_save_more campaigns
|
|
*
|
|
* @return array<int, array>
|
|
*/
|
|
public static function buildDealsMap(): array
|
|
{
|
|
global $wpdb;
|
|
|
|
$table = $wpdb->prefix . 'cuw_campaigns';
|
|
|
|
$exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
|
|
if ($exists !== $table) {
|
|
return [];
|
|
}
|
|
|
|
$rows = $wpdb->get_results("
|
|
SELECT id, type, title, priority, filters, data
|
|
FROM {$table}
|
|
WHERE enabled = 1
|
|
ORDER BY priority DESC, id DESC
|
|
");
|
|
|
|
if (empty($rows)) {
|
|
return [];
|
|
}
|
|
|
|
$map = [];
|
|
|
|
foreach ($rows as $row) {
|
|
$type = (string) $row->type;
|
|
if ($type !== 'buy_more_save_more') {
|
|
continue;
|
|
}
|
|
|
|
$filters = json_decode((string) $row->filters, true);
|
|
$data = json_decode((string) $row->data, true);
|
|
|
|
if (!is_array($filters) || !is_array($data)) {
|
|
continue;
|
|
}
|
|
|
|
$product_ids = self::extractProductIdsFromFilters($filters);
|
|
|
|
if (empty($product_ids)) {
|
|
continue;
|
|
}
|
|
|
|
// Deal details from data
|
|
$cta_text = $data['template']['cta_text'] ?? '';
|
|
$display_location = $data['display_location'] ?? ($data['template']['display_location'] ?? '');
|
|
|
|
$discounts = $data['discounts'] ?? [];
|
|
if (!is_array($discounts) || empty($discounts)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($discounts as $disc) {
|
|
if (!is_array($disc)) {
|
|
continue;
|
|
}
|
|
|
|
$qty = isset($disc['qty']) ? (int) $disc['qty'] : 0;
|
|
$dType = isset($disc['type']) ? (string) $disc['type'] : '';
|
|
$value = isset($disc['value']) ? (float) $disc['value'] : 0.0;
|
|
|
|
if ($qty <= 0 || $dType === '' || $value <= 0) {
|
|
continue;
|
|
}
|
|
|
|
$deal = [
|
|
'campaign_id' => (int) $row->id,
|
|
'campaign_type' => $type,
|
|
'title' => (string) $row->title,
|
|
'priority' => (int) $row->priority,
|
|
'qty' => $qty,
|
|
'discount_type' => $dType,
|
|
'value' => $value,
|
|
'cta_text' => (string) $cta_text,
|
|
'display_location' => (string) $display_location,
|
|
];
|
|
|
|
foreach ($product_ids as $pid) {
|
|
if (!isset($map[$pid])) {
|
|
$map[$pid] = [];
|
|
}
|
|
$map[$pid][] = $deal;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $map;
|
|
}
|
|
|
|
/**
|
|
* Extract product lists from campaign filters
|
|
*
|
|
* @param array $filters
|
|
* @return array<int[]>
|
|
*/
|
|
private static function extractProductListsFromFilters(array $filters): array
|
|
{
|
|
$filterProductLists = [];
|
|
|
|
foreach ($filters as $k => $v) {
|
|
if ($k === 'relation') {
|
|
continue;
|
|
}
|
|
if (!is_array($v)) {
|
|
continue;
|
|
}
|
|
|
|
$type = $v['type'] ?? '';
|
|
$method = $v['method'] ?? '';
|
|
$values = $v['values'] ?? [];
|
|
|
|
if ($type !== 'products') {
|
|
continue;
|
|
}
|
|
if (!in_array($method, ['in_list', 'in', 'include', 'equals'], true)) {
|
|
continue;
|
|
}
|
|
if (!is_array($values)) {
|
|
continue;
|
|
}
|
|
|
|
$ids = array_values(array_unique(array_map('intval', $values)));
|
|
$ids = array_values(array_filter($ids));
|
|
|
|
if (!empty($ids)) {
|
|
$filterProductLists[] = $ids;
|
|
}
|
|
}
|
|
|
|
return $filterProductLists;
|
|
}
|
|
|
|
/**
|
|
* Extract product IDs from campaign filters
|
|
*
|
|
* @param array $filters
|
|
* @return int[]
|
|
*/
|
|
private static function extractProductIdsFromFilters(array $filters): array
|
|
{
|
|
$product_ids = [];
|
|
|
|
foreach ($filters as $k => $v) {
|
|
if ($k === 'relation') {
|
|
continue;
|
|
}
|
|
if (!is_array($v)) {
|
|
continue;
|
|
}
|
|
|
|
$f_type = $v['type'] ?? '';
|
|
$f_method = $v['method'] ?? '';
|
|
$values = $v['values'] ?? [];
|
|
|
|
if ($f_type !== 'products') {
|
|
continue;
|
|
}
|
|
if (!in_array($f_method, ['in_list', 'in', 'include', 'equals'], true)) {
|
|
continue;
|
|
}
|
|
if (!is_array($values)) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($values as $pid) {
|
|
$pid = (int) $pid;
|
|
if ($pid > 0) {
|
|
$product_ids[] = $pid;
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_values(array_unique(array_filter($product_ids)));
|
|
}
|
|
}
|