From 18e7e415339ccc9adf8ade562f93abef606da16d Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 26 Aug 2026 22:24:18 +0000
Subject: [PATCH] =Cache::connects throughout codebase refactored

---
 inc/rest/routes/TermRoutes.php |  941 ++++++++++++++++++++--------------------------------------
 1 files changed, 333 insertions(+), 608 deletions(-)

diff --git a/inc/rest/routes/TermRoutes.php b/inc/rest/routes/TermRoutes.php
index da8f52c..38772cb 100644
--- a/inc/rest/routes/TermRoutes.php
+++ b/inc/rest/routes/TermRoutes.php
@@ -1,10 +1,15 @@
 <?php
 namespace JVBase\rest\routes;
 
-use JVBase\JVB;
-use JVBase\rest\RestRouteManager;
+use JVBase\Instance;
+use JVBase\managers\Cache;
+use JVBase\managers\Errors;
 use JVBase\managers\TaxonomyRelationships;
+use JVBase\registrar\Registrar;
+use JVBase\rest\Rest;
 use JVBase\managers\UserTermsManager;
+use JVBase\rest\Route;
+use JVBase\base\Site;
 use WP_REST_Request;
 use WP_REST_Response;
 use Exception;
@@ -12,19 +17,22 @@
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
 }
-class TermRoutes extends RestRouteManager
+class TermRoutes extends Rest
 {
+	use Instance;
     protected object $term_index_manager;
     protected int $per_page;
 
     public function __construct()
     {
-        $this->cache_name = 'terms';
+        $this->cacheName = 'terms';
         parent::__construct();
-//		$this->cache->invalidateGroup('terms');
+		$this->cache = Cache::for('terms', 'term');
+
+
         $this->per_page = 20;
+
         add_action('edited_term', [$this, 'deleteTermPath']);
-        add_action('wp_login', [$this, 'clearUserTaxonomyCache'], 10, 2);
     }
 
     /**
@@ -33,60 +41,31 @@
      */
     public function registerRoutes():void
     {
-        register_rest_route($this->namespace, '/terms', [
-            [
-                'methods'   => 'GET',
-                'callback'  => [$this, 'handleTermSelectionRequest'],
-                'permission_callback'   => [$this, 'checkPermission'],
-                'args' => [
-                    'page'  => [
-                        'required'  => true,
-                        'type'      => 'integer',
-                        'default'   => 1,
-                    ],
-                    'taxonomy' => [
-                        'required' => true,
-                        'type' => 'string'
-                    ],
-                    'search' => [
-                        'required' => false,
-                        'type' => 'string'
-                    ],
-                    'parent' => [
-                        'required' => false,
-                        'type' => 'integer',
-                        'default'   => 0
-                    ]
-                ]
-            ],
-            [
-                'methods'   => 'POST',
-                'callback' => [$this, 'createTermRequest'],
-                'permission_callback' => [$this, 'checkPermission'],
-                'args' => [
-                    'taxonomy' => [
-                        'required' => true,
-                        'type' => 'string'
-                    ],
-                    'name' => [
-                        'required' => true,
-                        'type' => 'string'
-                    ],
-                    'parent' => [
-                        'required' => false,
-                        'type' => 'integer',
-                        'default' => 0
-                    ]
-                ]
-            ]
-        ]);
+		Route::for('terms')
+			->get([$this, 'handleTermSelectionRequest'])
+			->auth('public')
+			->rateLimit()
+			->args([
+				'page'		=> 'int|required',
+				'taxonomy'	=> 'string|required',
+				'search'	=> 'string',
+				'parent'	=> 'int'
+			])
+			->post([$this, 'createTermRequest'])
+			->auth('isVerified')
+			->rateLimit(30)
+			->args([
+				'taxonomy'	=> 'string|required',
+				'name'		=> 'string|required',
+				'parent'	=> 'int|default:0',
+			])
+			->register();
 
-        //TODO: Just wrap this up to the normal GET request
-        register_rest_route($this->namespace, '/terms/check', [
-            'methods' => 'GET',
-            'callback' => [$this, 'getTermDetails'],
-            'permission_callback' => [$this, 'checkPermission'],
-        ]);
+		Route::for('terms/check')
+			->get([$this,'getTermDetails'])
+			->auth('public')
+			->rateLimit()
+			->register();
     }
 
     /**
@@ -100,22 +79,19 @@
         $term = get_term($term_id);
 
         if (is_wp_error($term)) {
-            return new WP_REST_Response([
-                'success'   => false,
-                'message'   => $term
-            ]);
+			return $this->error('No term found');
         }
 
         $data = [
             'id' => $term->term_id,
-            'name' => $term->name,
+            'name' => html_entity_decode($term->name),
             'slug' => $term->slug,
             'taxonomy' => $term->taxonomy,
         ];
 
         // Add relationship data if requested
         if ($request->get_param('include_relationships')) {
-            $relationship_manager = new TaxonomyRelationships();
+            $relationship_manager = TaxonomyRelationships::getInstance();
             $related_taxonomies = $request->get_param('related_taxonomies') ?: ['jvb_style', 'jvb_theme', 'jvb_city'];
 
             $data['relationships'] = [];
@@ -130,7 +106,7 @@
                         $term = get_term($rel->related_term_id, $rel->related_taxonomy);
                         return [
                             'id' => $rel->related_term_id,
-                            'name' => $term ? $term->name : 'Unknown',
+                            'name' => $term ? html_entity_decode($term->name) : 'Unknown',
                             'count' => $rel->relationship_count
                         ];
                     }, $relationships);
@@ -138,7 +114,7 @@
             }
         }
 
-        return new WP_REST_Response($data);
+        return $this->success($data);
     }
 
     /**
@@ -146,22 +122,27 @@
      *
      * @return WP_REST_Response
      */
-    public function getTermDetails(WP_REST_Request $request):WP_REST_Response
-    {
-        $data = $request->get_params();
-        $terms = [];
-        foreach ($data as $tax => $IDs) {
-            $args = [
-                'taxonomy'  => BASE.$tax,
-                'include'   => $IDs
-            ];
+	public function getTermDetails(WP_REST_Request $request): WP_REST_Response
+	{
+		$data = $request->get_params();
 
-            $terms[$tax] = $this->formatTerms($args, BASE.$tax);
-        }
-        return new WP_REST_Response([
-            'items' => $terms,
-        ]);
-    }
+		$terms = $this->cache->remember(
+			['termDetails' => $data],
+			function() use ($data) {
+				$result = [];
+				foreach ($data as $tax => $IDs) {
+					$args = [
+						'taxonomy' => BASE . $tax,
+						'include' => $IDs
+					];
+					$result[$tax] = $this->formatTerms($args, BASE . $tax);
+				}
+				return $result;
+			}
+		);
+
+		return $this->success(['items' => $terms]);
+	}
 
     /**
      * @param WP_REST_Request $request
@@ -171,67 +152,72 @@
     public function handleTermSelectionRequest(WP_REST_Request $request):WP_REST_Response
     {
 		$data = $request->get_params();
-		$taxonomy = jvbCheckBase($data['taxonomy']);
+		$taxonomy = sanitize_text_field($data['taxonomy'])??'';
+		if (empty($taxonomy)) {
+			return $this->success(['message' => 'No taxonomy selected']);
+		}
+
+		// Handle batch request (multiple taxonomies)
+		if (str_contains($taxonomy, ',')) {
+			return $this->handleBatchTermRequest($taxonomy, $data, $request);
+		}
+		$taxonomy = jvbCheckBase($taxonomy);
 
 		if (array_key_exists('termIDs', $data)) {
 			$args = [
 				'taxonomy'	=> $taxonomy,
 				'include'	=> $data['termIDs'],
-				'hide_empty'	=> false,
+				'hide_empty'	=> true,
 			];
-			$key = $this->cache->generateKey($args);
-			$cached = $this->cache->get($key);
-			if ($cached) {
-				return new WP_REST_Response($cached);
-			}
+			$data = $this->cache->remember(
+				$args,
+				function() use ($args, $taxonomy) {
+					return $this->formatTerms($args, $taxonomy);
+				}
+			);
 
-			$formatted = $this->formatTerms($args, $taxonomy);
 			$response = [
-				'items'	=> $formatted
+				'items'	=> $data
 			];
-			$this->cache->set($key, $response);
-			return new WP_REST_Response($response);
+
+			return $this->success($response);
 		}
 		if (array_key_exists('content', $data)) {
 			// If content_type is provided, use the specialized endpoint
 			$content_type = $request->get_param('content');
-			global $feed_types;
-			if (taxIsJVBContentTax($content_type)) {
-				return $this->getTermsForContentType($request);
+			$registrar = Registrar::getInstance($content_type);
+			if ($registrar->hasFeature('is_content')) {
+				//TODO: Not sure what we're trying for here. Doesn't seem to be used.
+				//TODO: Likely for taxonomy content types, like tattoo shops
+				return $this->emptyResult();
 			}
 		}
 
-        $taxonomy = BASE.$request->get_param('taxonomy');
+        $taxonomy = jvbCheckBase($request->get_param('taxonomy'));
+		if (!taxonomy_exists($taxonomy)) {
+			return $this->emptyResult();
+		}
         $search = $request->get_param('search');
-        $parent = (int)$request->get_param('parent');
-        $page = max(1, (int)$request->get_param('page'));
-        $per_page = max(20, (int)$request->get_param('per_page'));
 
-        if (!taxonomy_exists($taxonomy)) {
-            return new WP_REST_Response([
-                'items' => [],
-                'pagination' => [
-                    'page' => 1,
-                    'per_page' => $per_page,
-                    'total_pages' => 0,
-                    'total_terms' => 0,
-                    'has_more' => false
-                ]
-            ]);
-        }
+		$parent = (int)$data['parent']??0;
+		$page = max(1, (int)($data['page']??1));
+		$per_page = 25;
+
 
         $tax_obj = get_taxonomy($taxonomy);
         $is_hierarchical = $tax_obj->hierarchical;
 
         // If searching, handle differently
         if (!empty($search)) {
-            return $this->handleTermSearch($request);
+			error_log('Handling search...');
+
+			return $this->handleTermSearch($request);
         }
 
         // Get terms for current level with child count
         $args = [
             'taxonomy' => $taxonomy,
-            'hide_empty' => false,
+            'hide_empty' => true,
             'parent' => $parent,
             'number' => $per_page,
 			'orderby'=> 'name',
@@ -243,21 +229,12 @@
         if ($request->get_param('main_context') && in_array(jvbCheckBase(json_decode($request->get_param('main_context'), true)['context']), jvbUserTypes())) {
 
             $main_context = json_decode($request->get_param('main_context'), true);
-            $userID = get_post_meta($main_context['id'], BASE.'link', true);
-            $manager = new UserTermsManager();
-            $related = $manager->getUserTermIDs($userID, $taxonomy);
+            $userID = get_post_meta($main_context['id'], BASE.'profile_link', true);
+            $manager = UserTermsManager::getInstance();
+            $related = $manager->fetchUserTerms($userID, $taxonomy);
 
             if (empty($related)) {
-                return new WP_REST_Response([
-                    'items' => [],
-                    'pagination' => [
-                        'page' => 1,
-                        'per_page' => $per_page,
-                        'total_pages' => 0,
-                        'total_terms' => 0,
-                        'has_more' => false
-                    ]
-                ]);
+				return $this->emptyResult();
             }
 
             $args['include'] = $related;
@@ -266,20 +243,11 @@
             $main_context = json_decode($request->get_param('main_context'), true);
             $thisTaxonomy = str_replace('taxonomy:', '', $main_context['context']);
             $ID = (int)$main_context['id'];
-            $manager = new TaxonomyRelationships();
+            $manager = TaxonomyRelationships::getInstance();
             $related = $manager->getRelatedTerms($ID, BASE.$request->get_param('taxonomy'));
 
             if (empty($related)) {
-                return new WP_REST_Response([
-                    'items' => [],
-                    'pagination' => [
-                        'page' => 1,
-                        'per_page' => $per_page,
-                        'total_pages' => 0,
-                        'total_terms' => 0,
-                        'has_more' => false
-                    ]
-                ]);
+				return $this->emptyResult();
             }
             $args['tax_query'] = [
                 'taxonomy'  => $taxonomy,
@@ -292,7 +260,7 @@
             $match = $request->get_param('match') ?? 'any';
             $context = json_decode($request['context'], true);
 
-            $relationshipManager = new TaxonomyRelationships();
+            $relationshipManager = TaxonomyRelationships::getInstance();
             // Prepare array to collect term IDs that match the context
             $related_term_ids = [];
 
@@ -328,178 +296,201 @@
                 $args['include'] = $related_term_ids;
             } else {
                 // No related terms found, return empty result
-                return new WP_REST_Response([
-                    'items' => [],
-                    'pagination' => [
-                        'page' => 1,
-                        'per_page' => $per_page,
-                        'total_pages' => 0,
-                        'total_terms' => 0,
-                        'has_more' => false
-                    ]
-                ]);
+				return $this->emptyResult();
             }
         }
 
+		$response = $this->cache->remember(
+			$args,
+			function() use ($args, $taxonomy, $parent, $per_page, $page, $is_hierarchical) {
+				$formatted_terms = $this->formatTerms($args, $taxonomy);
+				// Get total count for pagination
+				$total_terms = wp_count_terms([
+					'taxonomy' => $taxonomy,
+					'hide_empty' => false,
+					'parent' => $parent
+				]);
 
+				// Calculate pagination
+				$total_pages = ceil($total_terms / $per_page);
+				$has_more = $page < $total_pages;
 
-        $key = $this->cache->generateKey($args);
-        $cache = $this->cache->get($key);
-		$cache = false;
-        if ($cache) {
-            return $cache;
-        }
+				return [
+					'items' => $formatted_terms,
+					'is_hierarchical' => $is_hierarchical,
+					'pagination' => [
+						'page' => $page,
+						'per_page' => $per_page,
+						'total_pages' => $total_pages,
+						'total_terms' => (int)$total_terms
+					],
+					'has_more' => $has_more
+				];
+			}
+		);
 
-        $formatted_terms = $this->formatTerms($args, $taxonomy);
-        // Get total count for pagination
-        $total_terms = wp_count_terms([
-            'taxonomy' => $taxonomy,
-            'hide_empty' => false,
-            'parent' => $parent
-        ]);
-
-        // Calculate pagination
-        $total_pages = ceil($total_terms / $per_page);
-        $has_more = $page < $total_pages;
-
-        $response = [
-            'items' => $formatted_terms,
-            'is_hierarchical' => $is_hierarchical,
-            'pagination' => [
-                'page' => $page,
-                'per_page' => $per_page,
-                'total_pages' => $total_pages,
-                'total_terms' => (int)$total_terms,
-                'has_more' => $has_more
-            ]
-        ];
-
-        $this->cache->set($key, $response);
-        return new WP_REST_Response($response);
+		return $this->success($response);
     }
 
+	protected function handleBatchTermRequest(string $taxonomy, array $data, WP_REST_Request $request):WP_REST_Response
+	{
+		$taxonomies = array_map('trim', explode(',', $taxonomy));
+		$all_terms = [];
+		$parent = (int)$data['parent']??0;
+		$page = max(1, (int)($data['page']??1));
+		$per_page = 25;
+		$mainArgs = [
+			'hide_empty'=> false,
+			'parent'	=> $parent,
+			'number'	=> $per_page,
+			'orderby'	=> 'name',
+			'offset'	=> ($page -1) * $per_page,
+		];
+
+		foreach ($taxonomies as $taxonomy) {
+			if (!taxonomy_exists(BASE.$taxonomy)) {
+				continue;
+			}
+			$args = $mainArgs;
+			$args['taxonomy'] = BASE.$taxonomy;
+
+			$all_terms = array_merge($all_terms, $this->formatTerms($args, $taxonomy));
+		}
+
+		$response = [
+			'items'	=> $all_terms,
+			'pagination'=> [
+				'page' => $page,
+				'per_page'=> $per_page
+			],
+			'has_more' => true,
+		];
+
+		return $this->success($response);
+	}
+
+
     /**
      * @param array $args
      * @param string $taxonomy
      *
      * @return array
      */
-    protected function formatTerms(array $args, string $taxonomy):array
-    {
-        $terms = get_terms($args);
+	protected function formatTerms(array $args, string $taxonomy): array
+	{
+		return $this->cache->remember(
+			$args,
+			function() use ($args, $taxonomy) {
+				$terms = get_terms($args);
 
-        if (is_wp_error($terms)) {
-            return [];
-        }
+				if (is_wp_error($terms)) {
+					return [];
+				}
+
+				$formatted_terms = [];
+				foreach ($terms as $term) {
+					$formatted_terms[] = $this->formatSingleTerm($term, $taxonomy);
+				}
+
+				return $formatted_terms;
+			}
+		);
 
 
+	}
 
-        $formatted_terms = [];
-        foreach ($terms as $term) {
-            // Check for children explicitly
-            $children_args = [
-                'taxonomy' => $taxonomy,
-                'parent' => $term->term_id,
-                'fields' => 'count',
-                'hide_empty' => false
-            ];
-            $count = wp_count_terms($children_args);
-            $has_children = !is_wp_error($count) && $count > 0;
+	/**
+	 * Format a single term with caching
+	 *
+	 * @param object $term WP_Term object
+	 * @param string $taxonomy Full taxonomy name
+	 *
+	 * @return array Formatted term data
+	 */
+	protected function formatSingleTerm(object $term, string $taxonomy): array
+	{
+		$cache_key = "{$term->term_id}_{$taxonomy}";
 
-            $formatted_terms[] = [
-                'id' => $term->term_id,
-                'name' => $term->name,
-				'slug'	=> $term->slug,
-                'parent' => $term->parent,
-                'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy),
-                'hasChildren' => $has_children,
-				'taxonomy'	=> $term->taxonomy,
-				'count'		=> $term->count,
-            ];
-        }
+		return $this->cache->remember($cache_key, function() use ($term, $taxonomy) {
+			$data = [
+				'id' => $term->term_id,
+				'name' => html_entity_decode($term->name),
+				'slug' => $term->slug,
+				'parent' => $term->parent,
+				'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy),
+				'taxonomy' => jvbNoBase($term->taxonomy),
+				'count' => $term->count,
+			];
 
-        return $formatted_terms;
-    }
+			$children_args = [
+				'taxonomy' => $taxonomy,
+				'parent' => $term->term_id,
+				'fields' => 'count',
+				'hide_empty' => false
+			];
+			$count = wp_count_terms($children_args);
+			$data['hasChildren'] = !is_wp_error($count) && $count > 0;
+
+
+			return $data;
+		});
+	}
 
     /**
      * @param WP_REST_Request $request
      *
      * @return WP_REST_Response
      */
-    public function handleTermSearch(WP_REST_Request $request):WP_REST_Response
-    {
-        $taxonomy = BASE.$request->get_param('taxonomy');
-        $search = $request->get_param('search');
-        $page = $request->get_param('page') ?? 1;
-        $per_page = $request->get_param('per_page') ?? 20;
+	public function handleTermSearch(WP_REST_Request $request): WP_REST_Response
+	{
+		$taxonomy = BASE . $request->get_param('taxonomy');
+		$search = $request->get_param('search');
+		$page = $request->get_param('page') ?? 1;
+		$per_page = $request->get_param('per_page') ?? 20;
 
-        // When searching, we want to search across all terms regardless of hierarchy
-        $args = [
-            'taxonomy' => $taxonomy,
-            'hide_empty' => false,
-            'search' => $search,
-            'search_columns' => ['name', 'slug'],
-            'fields' => 'all',
-            'number' => $per_page,
-            'offset' => ($page - 1) * $per_page,
-        ];
+		$args = [
+			'taxonomy' => $taxonomy,
+			'hide_empty' => true,
+			'search' => $search,
+			'search_columns' => ['name', 'slug'],
+			'fields' => 'all',
+			'number' => $per_page,
+			'offset' => ($page - 1) * $per_page,
+		];
 
-        $key = $this->cache->generateKey($args);
-        $cache = $this->cache->get($key);
-        if ($cache) {
-            return new WP_REST_Response($cache);
-        }
+		$data = $this->cache->remember(
+			$args,
+			function() use ($args, $taxonomy, $page, $per_page) {
+				$terms = get_terms($args);
 
-        $terms = get_terms($args);
+				if (is_wp_error($terms)) {
+					return $this->emptyResult($page, $per_page);
+				}
 
-        if (is_wp_error($terms)) {
-            return new WP_REST_Response([
-                'items' => [],
-                'pagination' => [
-                    'page' => 0,
-                    'per_page' => 20,
-                    'total_pages' => 0,
-                    'total_terms' => 0,
-                    'has_more' => false
-                ]
-            ]);
-        }
+				$formatted_terms = array_map(
+					fn($term) => $this->formatSingleTerm($term, $taxonomy),
+					$terms
+				);
 
-        // Get total count for pagination
-        $count_args = array_merge($args, ['fields' => 'count']);
-        $total_terms = wp_count_terms($count_args);
+				$count_args = array_merge($args, ['fields' => 'count']);
+				$total_terms = wp_count_terms($count_args);
+				$total_pages = ceil($total_terms / $per_page);
 
-        $formatted_terms = [];
-        foreach ($terms as $term) {
-            $formatted_terms[$term->term_id] = [
-                'id'            => $term->term_id,
-                'name'          => $term->name,
-                'parent'        => $term->parent,
-                'path'          => $this->getTermPath($term->term_id, $term->name, $taxonomy),
-                'hasChildren'   => (bool)$term->has_children,
-                'count'         => (int)$term->count
-            ];
-        }
+				return [
+					'items' => $formatted_terms,
+					'pagination' => [
+						'page' => (int)$page,
+						'per_page' => (int)$per_page,
+						'total_pages' => $total_pages,
+						'total_terms' => (int)$total_terms
+					],
+					'has_more' => $page < $total_pages
+				];
+			}
+		);
 
-        // Calculate pagination info
-        $total_pages = ceil($total_terms / $per_page);
-        $has_more = $page < $total_pages;
-
-        $response = [
-            'items' => $formatted_terms,
-            'pagination' => [
-                'page' => (int)$page,
-                'per_page' => (int)$per_page,
-                'total_pages' => $total_pages,
-                'total_terms' => (int)$total_terms,
-                'has_more' => $has_more
-            ]
-        ];
-
-        $this->cache->set($key, $response);
-
-        return new WP_REST_Response($response);
-    }
+		return $this->success($data);
+	}
 
     /**
      * @param int $termID
@@ -540,314 +531,46 @@
         delete_term_meta($termID, BASE.'term_path');
     }
 
-    /**
-     * @param string $username
-     * @param object $user
-     *
-     * @return void
-     */
-    public function clearUserTaxonomyCache(string $username, object $user):void
-    {
-        global $wpdb;
-
-        // Get all transients for this user
-        $like_pattern = $wpdb->esc_like(BASE.'user_' . $user->ID) . '_%';
-
-        $transients = $wpdb->get_col($wpdb->prepare("
-            SELECT option_name
-            FROM {$wpdb->options}
-            WHERE option_name LIKE %s
-            AND option_name LIKE %s
-        ", '_transient_' . $like_pattern, '%'));
-
-        // Delete all found transients
-        foreach ($transients as $transient) {
-            delete_transient(str_replace('_transient_', '', $transient));
-        }
-    }
 
     /**
      * @param WP_REST_Request $request
      *
      * @return WP_REST_Response
      */
-    public function getTermsForContentType(WP_REST_Request $request):WP_REST_Response
-    {
-        $manager = new TaxonomyRelationships();
-        $content_type = BASE . $request->get_param('content');
-        $taxonomy = BASE . $request->get_param('taxonomy');
-        $search = $request->get_param('search');
-        $page = max(1, intval($request->get_param('page')));
-        $per_page = $request->get_param('per_page') ?: 20;
+	public function createTermRequest(WP_REST_Request $request): WP_REST_Response
+	{
+		$user_id = get_current_user_id();
+		$taxonomy = $request->get_param('taxonomy');
+		$name = sanitize_text_field($request->get_param('name'));
+		$parent = (int)$request->get_param('parent') ?: 0;
 
-        // Create cache key
-        $cache_key = "terms_for_{$content_type}_{$taxonomy}_" . md5("{$search}_{$page}_{$per_page}");
+		try {
+			$existing = term_exists($name, jvbCheckBase($taxonomy), $parent);
 
-        $cache_key = $this->cache->generateKey([
-            'terms_for'     => $content_type,
-            'taxonomy'      => $taxonomy,
-            'search'        => $search,
-            'page'          => $page,
-            'per_page'      => $per_page
-        ]);
-        $cache = $this->cache->get($cache_key);
-        // Try cache first
-        if ($cache !== false) {
-            return new WP_REST_Response($cache);
-        }
+			if ($existing) {
+				$term = get_term($existing['term_id'], jvbCheckBase($taxonomy));
+				return $this->success(['message' => 'Term already exists', 'term' => [
+					'id' => $term->term_id,
+					'name' => html_entity_decode($term->name),
+					'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy)
+				]]);
+			}
 
-        try {
-            global $wpdb;
-
-            // Starting the query building
-            $query_args = [];
-            $terms_query = "
-                SELECT DISTINCT t.term_id, t.name, t.slug, tt.count, tt.taxonomy,
-                       (SELECT COUNT(DISTINCT tm.meta_key) FROM {$wpdb->termmeta} tm WHERE tm.term_id = t.term_id) AS meta_count
-                FROM {$wpdb->terms} t
-                JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
-                JOIN {$manager->getTableName()} tr ON (
-                    (tr.primary_taxonomy = %s AND tr.primary_term_id = t.term_id)
-                    OR (tr.related_taxonomy = %s AND tr.related_term_id = t.term_id)
-                )
-            ";
-
-            $query_args[] = $taxonomy;
-            $query_args[] = $taxonomy;
-
-            // Add content type filter through posts
-            $query_where = " WHERE tt.taxonomy = %s";
-            $query_args[] = $taxonomy;
-
-            // Add search condition if provided
-            if ($search) {
-                $query_where .= " AND (t.name LIKE %s OR t.slug LIKE %s)";
-                $search_like = '%' . $wpdb->esc_like($search) . '%';
-                $query_args[] = $search_like;
-                $query_args[] = $search_like;
-            }
-
-            // Add relationship strength ordering
-            $query_order = " GROUP BY t.term_id";
-            $query_order .= " ORDER BY COUNT(tr.id) DESC, tt.count DESC";
-
-            // Add pagination
-            $query_limit = " LIMIT %d OFFSET %d";
-            $query_args[] = $per_page;
-            $query_args[] = ($page - 1) * $per_page;
-
-            // Combine the query
-            $final_query = $query_where . $query_order . $query_limit;
-
-            // Get terms
-            $terms = $wpdb->get_results(
-                $wpdb->prepare(
-                    $terms_query . $final_query,
-                    $query_args
-                )
-            );
-            // Get total count for pagination
-            $count_query = str_replace($query_limit, '', $query_where);
-            $count_query = "SELECT COUNT(DISTINCT t.term_id) FROM {$wpdb->terms} t
-                           JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
-                           JOIN {$manager->getTableName()} tr ON (
-                               (tr.primary_taxonomy = %s AND tr.primary_term_id = t.term_id)
-                               OR (tr.related_taxonomy = %s AND tr.related_term_id = t.term_id)
-                           ) " . $count_query;
-
-            // Remove pagination args
-            array_pop($query_args);
-            array_pop($query_args);
-
-            $total = $wpdb->get_var(
-                $wpdb->prepare(
-                    $count_query,
-                    $query_args
-                )
-            );
-
-            // Format terms
-            $formatted_terms = [];
-            $is_hierarchical = is_taxonomy_hierarchical($taxonomy);
-
-            foreach ($terms as $term) {
-                $formatted_terms[$term->term_id] = [
-                    'id' => $term->term_id,
-                    'name' => $term->name,
-                    'count' => $term->count,
-                    'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy),
-                    'relationship_strength' => $term->relationship_count ?? 0
-                ];
-            }
-
-            // Build response
-            $total_pages = ceil($total / $per_page);
-            $results = [
-                'items' => $formatted_terms,
-                'pagination'    => [
-                    'page'      => (int)$page,
-                    'per_page'  => (int)$per_page,
-                    'total_terms'=> $total,
-                    'total_pages'=> $total_pages,
-                    'has_more'  => $page < $total_pages
-                ]
-            ];
-
-            // Cache results
-            $this->cache->set($cache_key, $results);
-
-            return new WP_REST_Response($results);
-
-        } catch (Exception $e) {
-            return new WP_REST_Response([
-                'success'   => false,
-                'message'   => $e->getMessage()
-            ]);
-        }
-    }
-
-    /**
-     * @param WP_REST_Request $request
-     *
-     * @return WP_REST_Response
-     */
-    public function handleTermsRequest(WP_REST_Request $request):WP_REST_Response
-    {
-        $taxonomy = BASE.$request->get_param('taxonomy');
-        $search = $request->get_param('search');
-        $page = max(1, intval($request->get_param('page')));
-        $per_page = $request->get_param('per_page') ?: $this->per_page;
-
-        // Create cache key
-        $cache_key = "terms_{$taxonomy}_" . md5("{$search}_{$page}_{$per_page}");
-        $cache = $this->cache->get($cache_key);
-        if ($cache) {
-            return new WP_REST_Response($cache);
-        }
-
-        try {
-            // Build query args
-            $args = [
-                'taxonomy' => $taxonomy,
-                'hide_empty' => false,
-                'orderby' => $search ? 'name' : 'count',
-                'order' => $search ? 'ASC' : 'DESC',
-                'number' => $per_page,
-                'offset' => ($page - 1) * $per_page,
-                'fields' => 'all'
-            ];
-
-            // Add search if provided
-            if ($search) {
-                $args['search'] = $search;
-            }
-
-            // Get terms
-            $terms = get_terms($args);
-
-            if (is_wp_error($terms)) {
-                return new WP_REST_Response([
-                    'items' => [],
-                    'pagination' => [
-                        'page' => 0,
-                        'per_page' => 20,
-                        'total_pages' => 0,
-                        'total_terms' => 0,
-                        'has_more' => 0
-                    ]
-                ]);
-            }
-
-            // Check if taxonomy is hierarchical
-            $is_hierarchical = is_taxonomy_hierarchical($taxonomy);
-
-            // Format terms
-            $formatted_terms = [];
-            foreach ($terms as $term) {
-                $formatted_terms[$term->term_id] = [
-                    'id' => $term->term_id,
-                    'name' => $term->name,
-                    'count' => $term->count,
-                    'parent' => $term->parent,
-                    'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy)
-                ];
-            }
-
-            // Get total for pagination
-            $total_args = array_merge($args, ['fields' => 'count', 'number' => '']);
-            $total = wp_count_terms($taxonomy, $total_args);
-            $total_pages = ceil($total / $per_page);
-
-            $results = [
-                'items' => $formatted_terms,
-                'pagination' => [
-                    'page' => (int)$page,
-                    'per_page' => (int)$per_page,
-                    'total_pages' => $total_pages,
-                    'total_terms' => (int)$total,
-                    'has_more' => $page < $total_pages
-                ]
-            ];
-
-            // Cache results
-            $this->cache->set($cache_key, $results);
-
-            return new WP_REST_Response($results);
-
-        } catch (Exception $e) {
-            return new WP_REST_Response([
-                'success'       => false,
-                'message'       =>  $e->getMessage()
-            ]);
-        }
-    }
-
-    /**
-     * @param WP_REST_Request $request
-     *
-     * @return WP_REST_Response
-     */
-    public function createTermRequest(WP_REST_Request $request):WP_REST_Response
-    {
-        $user_id = get_current_user_id();
-        $taxonomy = $request->get_param('taxonomy');
-        $name = sanitize_text_field($request->get_param('name'));
-        $parent = (int)$request->get_param('parent') ?: 0;
-
-        try {
-            // Check if term already exists
-            $existing = term_exists($name, jvbCheckBase($taxonomy), $parent);
-
-            if ($existing) {
-                $term = get_term($existing['term_id'], jvbCheckBase($taxonomy));
-
-                return new WP_REST_Response([
-                    'success' => false,
-                    'message' => 'Term already exists',
-                    'term' => [
-                        'id' => $term->term_id,
-                        'name' => $term->name,
-                        'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy)
-                    ]
-                ]);
-            }
-
-
-			if (jvbSiteHasTermApproval()) {
-				// Get approval routes instance
-				$approval_routes = JVB()->routes('approvals');
-				// Create approval request
+			$membership = Site::membership();
+			if ($membership && $membership->has('term_approval')) {
+				$approval_routes = ApprovalRoutes::getInstance();
 				$request_id = $approval_routes->createTermApprovalRequest(
 					$user_id,
 					$taxonomy,
 					sanitize_title($name),
 					absint($parent)
 				);
+
 				if (!$request_id) {
 					throw new Exception('Failed to create approval request');
 				}
-				$return = [
-					'success' => true,
+
+				return $this->success([
 					'message' => 'Term suggestion submitted for approval',
 					'term' => [
 						'id' => 'pending_' . $request_id,
@@ -855,48 +578,50 @@
 						'pending' => true,
 						'request_id' => $request_id
 					]
-				];
-			} else {
-				$termID = wp_insert_term(
-					$name,
-					jvbCheckBase($taxonomy),
-					[
-						'parent'	=> absint($parent??0)
-					]
-				);
-
-				if (is_wp_error($termID)) {
-					throw new Exception('Failed to create new term');
-				}
-
-				$return = [
-					'success'	=> true,
-					'message'	=> $name.' created successfully',
-					'term'		=> [
-						'id'	=> $termID['term_id'],
-						'name'	=> $name,
-						'path'	=> $this->getTermPath($termID['term_id'], $name, $taxonomy)
-					]
-				];
+				], 202); // 202 Accepted for pending approval
 			}
 
-            return new WP_REST_Response($return);
+			$termID = wp_insert_term(
+				$name,
+				jvbCheckBase($taxonomy),
+				['parent' => absint($parent)]
+			);
 
-        } catch (Exception $e) {
-            JVB()->error()->log(
-                'terms',
-                'Term creation failed: ' . $e->getMessage(),
-                [
-                    'user_id' => $user_id,
-                    'taxonomy' => $taxonomy,
-                    'name' => $name
-                ]
-            );
+			if (is_wp_error($termID)) {
+				throw new Exception($termID->get_error_message());
+			}
 
-            return new WP_REST_Response([
-                'success' => false,
-                'message' => $e->getMessage()
-            ], 500);
-        }
-    }
+			return $this->success([
+				'message' => $name . ' created successfully',
+				'term' => [
+					'id' => $termID['term_id'],
+					'name' => $name,
+					'path' => $this->getTermPath($termID['term_id'], $name, $taxonomy)
+				]
+			], 201); // 201 Created
+
+		} catch (Exception $e) {
+			Errors::getInstance()->log(
+				'terms',
+				'Term creation failed: ' . $e->getMessage(),
+				['user_id' => $user_id, 'taxonomy' => $taxonomy, 'name' => $name]
+			);
+
+			return $this->error($e->getMessage(), 'term_creation_failed', 500);
+		}
+	}
+
+	protected function emptyResult(int $page = 1, int $per_page = 20):WP_REST_Response
+	{
+		return $this->success([
+			'items' => [],
+			'pagination' => [
+				'page' => $page,
+				'per_page' => $per_page,
+				'total_pages' => 0,
+				'total_terms' => 0
+			],
+			'has_more' => false
+		]);
+	}
 }

--
Gitblit v1.10.0