2026-01-10 15:30:05 +01:00

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)));
}
}