Jake Vanderwerf
2026-09-04 c16a4d78d07e81ecdd2ff9b1b9090560ab2fe488
inc/managers/KarmaManager.php
@@ -2,10 +2,11 @@
namespace JVBase\managers;
use Exception;
use JVBase\managers\queue\Queue;
use JVBase\rest\Rest;
use JVBase\rest\routes\VoteRoutes;
use WP_Error;
use WP_Post;
use WP_Term;
use WP_User;
use WP_Query;
if (!defined('ABSPATH')) {
   exit; // Exit if accessed directly
@@ -23,15 +24,18 @@
   protected string $references;
   protected CustomTable $table;
   protected static Cache $cache;
   protected Rest $route;
   private function __construct(string $key, ?string $references = null)
   {
      $this->route = VoteRoutes::getInstance();
      $this->key = $key;
      if (is_null($references)) {
         $references = match($key){
            'post'   => 'post',
            'term'   => 'term',
            'user'   => 'user',
            'post', 'posts'   => 'post',
            'term','terms', 'taxonomy' => 'term',
            'user', 'users'   => 'user',
            'response', 'responses' => 'response',
            default => false
         };
         if (!$references) {
@@ -60,12 +64,98 @@
//          break;
//    }
      if (!isset(self::$cache)) {
         self::$cache = Cache::for('user_karma')->connect('user');
         self::$cache = Cache::for('user_karma')->user();
      }
      $this->registerHooks();
   }
   protected function registerHooks():void
   {
      if ($this->references === 'post') {
         add_filter('posts_join', [$this, 'maybeJoinKarmaOrderby'], 10, 2);
         add_filter('posts_orderby', [$this, 'maybeApplyKarmaOrderby'], 10, 2);
      } elseif ($this->references === 'term') {
         add_filter('terms_clauses', [$this, 'maybeApplyKarmaTermOrderby'], 10, 3);
      }
      add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
   }
   /**
    * Supported orderby keys: 'karma_upvotes', 'karma_downvotes', 'karma_score'
    */
   protected function karmaOrderExpr(string $orderby, string $alias = 'jvb_karma'): ?string
   {
      return match ($orderby) {
         'upvotes'   => "COALESCE({$alias}.upvotes, 0)",
         'downvotes' => "COALESCE({$alias}.downvotes, 0)",
         'karmic_score'     => "COALESCE({$alias}.upvotes, 0) - COALESCE({$alias}.downvotes, 0)",
         default           => null,
      };
   }
      public function maybeJoinKarmaOrderby(string $join, WP_Query $query): string
      {
         if (!in_array($query->get('orderby'), ['upvotes', 'downvotes', 'karmic_score'])) {
            return $join;
         }
         global $wpdb;
         $table = $this->table->getFullTableName();
         $content = $query->get('post_type') ?: 'post';
         $join .= $wpdb->prepare(
            " LEFT JOIN (SELECT item_id,
            SUM(vote = 'up') as upvotes,
            SUM(vote = 'down') as downvotes
            FROM {$table} WHERE content = %s GROUP BY item_id) jvb_karma
            ON jvb_karma.item_id = {$wpdb->posts}.ID",
            $content
         );
         return $join;
      }
      public function maybeApplyKarmaOrderby(string $orderby, WP_Query $query): string
      {
         $expr = $this->karmaOrderExpr($query->get('orderby'));
         if (!$expr) {
            return $orderby;
         }
         $order = strtoupper($query->get('order')) === 'ASC' ? 'ASC' : 'DESC';
         return "{$expr} {$order}";
      }
      public function maybeApplyKarmaTermOrderby(array $clauses, array $taxonomies, array $args): array
      {
         $expr = $this->karmaOrderExpr($args['orderby'] ?? '');
         if (!$expr) {
            return $clauses;
         }
         global $wpdb;
         $table = $this->table->getFullTableName();
         $content = jvbNoBase($taxonomies[0] ?? '');
         $clauses['join'] .= $wpdb->prepare(
            " LEFT JOIN (SELECT item_id,
            SUM(vote = 'up') as upvotes,
            SUM(vote = 'down') as downvotes
            FROM {$table} WHERE content = %s GROUP BY item_id) jvb_karma
            ON jvb_karma.item_id = t.term_id",
            $content
         );
         $clauses['orderby'] = "ORDER BY {$expr}";
         $clauses['order'] = strtoupper($args['order'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
         return $clauses;
      }
   public function karmaTable():CustomTable
   {
      return $this->table;
   }
   public static function getInstance(string $key):KarmaManager|false
   {
      if (!isset(self::$instances[$key])) {
@@ -78,7 +168,7 @@
   {
      $key = sanitize_key($key);
      if (!isset(self::$instances[$key])) {
         self::$instances[$key] = new KarmaManager($key, $references);
         self::$instances[$key] = new self($key, $references);
      }
      return self::$instances[$key];
@@ -175,7 +265,7 @@
         'post'   => get_post($itemID),
         'term'   => get_term($itemID, $based),
         'user'   => get_userdata($itemID),
         'response' => JVB()->responses()->response->get(['id' => $itemID])??false,
         'response' => Responses::getInstance()->responseExists($itemID),
         default => false  //TODO if we do have custom tables, we'll have to set that up here
      };
   }
@@ -221,7 +311,7 @@
      }
      self::$cache->forget($userID);
      JVB()->queue()->queueOperation(
      Queue::getInstance()->queueOperation(
         'karmic',
         0,
         [
@@ -292,16 +382,7 @@
                  update_user_meta($item['itemID'], BASE.'karma', $karma);
                  break;
               case 'response':
                  JVB()->responses()->response->update(
                     [
                        'upvotes'   => $upvotes,
                        'downvotes' => $downvotes,
                        'karma'     => $karma
                     ],
                     [
                        'id'  => $item['itemID']
                     ]
                  );
                  Responses::getInstance()->updateKarma($item['itemID'], $upvotes, $downvotes, $karma);
                  break;
            }
            $results[$item['itemID']] = true;