feat: Implement upsell functionality with repository and service layers

This commit is contained in:
2026-05-02 23:30:23 +03:30
parent 4928901a08
commit 5930c1ad6f
26 changed files with 3130 additions and 126 deletions

View File

@@ -0,0 +1,137 @@
<?php
namespace Sodino\Services;
use Sodino\Repositories\EventRepository;
class TrackingService {
private $eventRepository;
private $loggedEvents = [];
public function __construct(EventRepository $eventRepository) {
$this->eventRepository = $eventRepository;
}
public function trackProductView($product_id) {
if (!$product_id) {
return;
}
$session_key = 'sodino_viewed_' . intval($product_id);
if ($this->hasLogged($session_key)) {
return;
}
$this->logEvent('product_view', [
'product_id' => $product_id,
]);
$this->markLogged($session_key);
}
public function trackAddToCart($product_id, $variation_id = null, $quantity = 1) {
if (!$product_id) {
return;
}
$this->logEvent('add_to_cart', [
'product_id' => $product_id,
'variation_id' => $variation_id,
'value' => floatval($quantity),
]);
}
public function trackCheckoutStart() {
$this->logEvent('checkout_start', []);
}
public function trackPurchase($order_id) {
if (!$order_id) {
return;
}
$order = wc_get_order($order_id);
if (!$order) {
return;
}
$total = floatval($order->get_total());
$discount = 0;
if (method_exists($order, 'get_total_discount')) {
$discount = floatval($order->get_total_discount());
} else {
foreach ($order->get_items() as $item) {
$discount += floatval($item->get_subtotal()) - floatval($item->get_total());
}
}
$this->logEvent('purchase', [
'value' => $total,
'discount_value' => max(0, $discount),
]);
}
public function recordDiscountApplied($product, $original_price, $discounted_price, $rule_id = null) {
if (!$product || $original_price <= 0 || $discounted_price >= $original_price) {
return;
}
$product_id = $product->get_id();
$variation_id = $product->is_type('variation') ? $product_id : 0;
$discount_value = round($original_price - $discounted_price, 2);
$key = 'discount_applied_' . $product_id . '_' . $rule_id . '_' . $discount_value;
if ($this->hasLogged($key)) {
return;
}
$this->logEvent('discount_applied', [
'product_id' => $product_id,
'variation_id' => $variation_id,
'rule_id' => $rule_id,
'value' => $discounted_price,
'discount_value' => $discount_value,
]);
$this->markLogged($key);
}
public function getRuleUsageCount($rule_id) {
return $this->eventRepository->getRuleUsageCount($rule_id);
}
private function logEvent($type, array $data = []) {
$event = [
'event_type' => $type,
'product_id' => isset($data['product_id']) ? intval($data['product_id']) : null,
'variation_id' => isset($data['variation_id']) ? intval($data['variation_id']) : null,
'user_id' => get_current_user_id() ?: null,
'session_id' => $this->getSessionId(),
'rule_id' => isset($data['rule_id']) ? intval($data['rule_id']) : null,
'value' => isset($data['value']) ? floatval($data['value']) : 0,
'discount_value' => isset($data['discount_value']) ? floatval($data['discount_value']) : 0,
'metadata' => isset($data['metadata']) ? wp_json_encode($data['metadata']) : null,
'created_at' => current_time('mysql'),
];
$this->eventRepository->insert($event);
}
private function getSessionId() {
if (function_exists('WC') && WC()->session) {
$session_id = WC()->session->get('sodino_session_id');
if (!$session_id) {
$session_id = uniqid('sodino_', true);
WC()->session->set('sodino_session_id', $session_id);
}
return $session_id;
}
return 'guest_' . md5($_SERVER['REMOTE_ADDR'] . '|' . $_SERVER['HTTP_USER_AGENT']);
}
private function hasLogged($key) {
return isset($this->loggedEvents[$key]);
}
private function markLogged($key) {
$this->loggedEvents[$key] = true;
}
}