From 235ce5716edc2f7cbe80fdccf26eac7269587839 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 08 Jun 2026 04:38:18 +0000
Subject: [PATCH] =FavouritesManager.php and FavouritesRoutes.php fixes. Moving all logic to FavouritesManager.php. Still some left to do

---
 inc/rest/routes/TermRoutes.php |  752 +++++++++++++++++++++++++--------------------------------
 1 files changed, 330 insertions(+), 422 deletions(-)

diff --git a/inc/rest/routes/TermRoutes.php b/inc/rest/routes/TermRoutes.php
index 269dc2a..8954448 100644
--- a/inc/rest/routes/TermRoutes.php
+++ b/inc/rest/routes/TermRoutes.php
@@ -1,10 +1,11 @@
 <?php
 namespace JVBase\rest\routes;
 
-use JVBase\JVB;
-use JVBase\rest\RestRouteManager;
-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,17 +13,21 @@
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
 }
-class TermRoutes extends RestRouteManager
+class TermRoutes extends Rest
 {
     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');
+		if (JVB_TESTING) {
+			$this->cache->flush();
+		}
+		$this->cache->connect('taxonomy', true);
         $this->per_page = 20;
+
         add_action('edited_term', [$this, 'deleteTermPath']);
         add_action('wp_login', [$this, 'clearUserTaxonomyCache'], 10, 2);
     }
@@ -33,60 +38,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 +76,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 = JVB()->termRelationships();
             $related_taxonomies = $request->get_param('related_taxonomies') ?: ['jvb_style', 'jvb_theme', 'jvb_city'];
 
             $data['relationships'] = [];
@@ -130,7 +103,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 +111,7 @@
             }
         }
 
-        return new WP_REST_Response($data);
+        return $this->success($data);
     }
 
     /**
@@ -146,22 +119,33 @@
      *
      * @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();
+		$taxonomies = array_keys($data);
 
-            $terms[$tax] = $this->formatTerms($args, BASE.$tax);
-        }
-        return new WP_REST_Response([
-            'items' => $terms,
-        ]);
-    }
+		$cache_check = $this->checkHeaders($request, $this->cache->generateKey(['termDetails' => $data]));
+		if ($cache_check) {
+			return $cache_check;
+		}
+
+		$terms = $this->cache->remember(
+			$this->cache->generateKey(['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->addCacheHeaders($this->success(['items' => $terms]));
+	}
 
     /**
      * @param WP_REST_Request $request
@@ -171,18 +155,31 @@
     public function handleTermSelectionRequest(WP_REST_Request $request):WP_REST_Response
     {
 		$data = $request->get_params();
-		$taxonomy = jvbCheckBase($data['taxonomy']);
+		$taxonomy = sanitize_text_field($data['taxonomy'])??'';
+		// Check HTTP cache headers
+		$cache_check = $this->checkHeaders($request, $taxonomy);
+		if ($cache_check) {
+			error_log('Header Check failed');
+			return $cache_check;
+		}
+
+		// 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);
+				$response = $this->success($cached);
+				return $this->addCacheHeaders($response);
 			}
 
 			$formatted = $this->formatTerms($args, $taxonomy);
@@ -190,34 +187,28 @@
 				'items'	=> $formatted
 			];
 			$this->cache->set($key, $response);
-			return new WP_REST_Response($response);
+			$response = $this->success($response);
+			return $this->addCacheHeaders($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')) {
+				$response = $this->getTermsForContentType($request);
+				return $this->addCacheHeaders($response);
 			}
 		}
 
         $taxonomy = BASE.$request->get_param('taxonomy');
         $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'));
+
+		$parent = (int)$data['parent']??0;
+		$page = max(1, (int)($data['page']??1));
+		$per_page = 25;
 
         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
-                ]
-            ]);
+			return $this->emptyResult();
         }
 
         $tax_obj = get_taxonomy($taxonomy);
@@ -225,13 +216,15 @@
 
         // If searching, handle differently
         if (!empty($search)) {
-            return $this->handleTermSearch($request);
+			error_log('Handling search...');
+            $response = $this->handleTermSearch($request);
+			return $this->addCacheHeaders($response);
         }
 
         // 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 +236,13 @@
         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);
+            $userID = get_post_meta($main_context['id'], BASE.'profile_link', true);
             $manager = new UserTermsManager();
-            $related = $manager->getUserTermIDs($userID, $taxonomy);
+            $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
-                    ]
-                ]);
+                $response = $this->emptyResult();
+				return $this->addCacheHeaders($response);
             }
 
             $args['include'] = $related;
@@ -266,20 +251,12 @@
             $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 = JVB()->termRelationships();
             $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
-                    ]
-                ]);
+                $response = $this->emptyResult();
+				return $this->addCacheHeaders($response);
             }
             $args['tax_query'] = [
                 'taxonomy'  => $taxonomy,
@@ -292,7 +269,7 @@
             $match = $request->get_param('match') ?? 'any';
             $context = json_decode($request['context'], true);
 
-            $relationshipManager = new TaxonomyRelationships();
+            $relationshipManager = JVB()->termRelationships();
             // Prepare array to collect term IDs that match the context
             $related_term_ids = [];
 
@@ -328,16 +305,9 @@
                 $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
-                    ]
-                ]);
+                $response = $this->emptyResult();
+
+				return $this->addCacheHeaders($response);
             }
         }
 
@@ -345,9 +315,10 @@
 
         $key = $this->cache->generateKey($args);
         $cache = $this->cache->get($key);
-		$cache = false;
+
         if ($cache) {
-            return $cache;
+            $response = $this->success($cache);
+			return $this->addCacheHeaders($response);
         }
 
         $formatted_terms = $this->formatTerms($args, $taxonomy);
@@ -369,134 +340,176 @@
                 'page' => $page,
                 'per_page' => $per_page,
                 'total_pages' => $total_pages,
-                'total_terms' => (int)$total_terms,
-                'has_more' => $has_more
-            ]
+                'total_terms' => (int)$total_terms
+            ],
+			'has_more' => $has_more
         ];
 
         $this->cache->set($key, $response);
-        return new WP_REST_Response($response);
+        $response = $this->success($response);
+		return $this->addCacheHeaders($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,
+		];
+
+		$response = $this->success($response);
+		return $this->addCacheHeaders($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(
+			$this->cache->generateKey($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, true);
+				}
+
+				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,
-                'parent' => $term->parent,
-                'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy),
-                'hasChildren' => $has_children,
-            ];
-        }
+		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(
+			$this->cache->generateKey($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->addCacheHeaders($this->success($data));
+	}
 
     /**
      * @param int $termID
@@ -570,7 +583,7 @@
      */
     public function getTermsForContentType(WP_REST_Request $request):WP_REST_Response
     {
-        $manager = new TaxonomyRelationships();
+        $manager = JVB()->termRelationships();
         $content_type = BASE . $request->get_param('content');
         $taxonomy = BASE . $request->get_param('taxonomy');
         $search = $request->get_param('search');
@@ -590,7 +603,8 @@
         $cache = $this->cache->get($cache_key);
         // Try cache first
         if ($cache !== false) {
-            return new WP_REST_Response($cache);
+			$response = $this->success($cache);
+            return $this->addCacheHeaders($response);
         }
 
         try {
@@ -667,15 +681,12 @@
             $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
-                ];
-            }
+			foreach ($terms as $term) {
+				$formatted = $this->formatSingleTerm($term, $taxonomy, false);
+				// Add relationship strength which is unique to this method
+				$formatted['relationship_strength'] = $term->relationship_count ?? 0;
+				$formatted_terms[] = $formatted;
+			}
 
             // Build response
             $total_pages = ceil($total / $per_page);
@@ -685,166 +696,61 @@
                     'page'      => (int)$page,
                     'per_page'  => (int)$per_page,
                     'total_terms'=> $total,
-                    'total_pages'=> $total_pages,
-                    'has_more'  => $page < $total_pages
-                ]
+                    'total_pages'=> $total_pages
+                ],
+				'has_more'  => $page < $total_pages
             ];
 
             // Cache results
             $this->cache->set($cache_key, $results);
-
-            return new WP_REST_Response($results);
+			$response = $this->success($results);
+            return $this->addCacheHeaders($response);
 
         } catch (Exception $e) {
-            return new WP_REST_Response([
-                'success'   => false,
-                'message'   => $e->getMessage()
-            ]);
+			return $this->error('Error getting terms for content: '.$e->getMessage(), 'get_terms_for_content');
         }
     }
 
+
     /**
      * @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;
+	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_{$taxonomy}_" . md5("{$search}_{$page}_{$per_page}");
-        $cache = $this->cache->get($cache_key);
-        if ($cache) {
-            return new WP_REST_Response($cache);
-        }
+		try {
+			$existing = term_exists($name, jvbCheckBase($taxonomy), $parent);
 
-        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'
-            ];
+			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)
+				]]);
+			}
 
-            // 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
+			$membership = Site::membership();
+			if ($membership && $membership->has('term_approval')) {
 				$approval_routes = JVB()->routes('approvals');
-				// Create approval request
 				$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,
@@ -852,48 +758,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) {
+			JVB()->error()->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