From 2127b1bdd73ecd2423e443992da4b442f5a3c1a3 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Wed, 04 Feb 2026 21:19:25 +0000
Subject: [PATCH] =Major overhaul of MetaManager.php -> Meta.php and RestRouteManager.php -> Rest.php. Seems to work for JakeVan

---
 inc/rest/routes/TermRoutes.php |  522 ++++++++++++++++++---------------------------------------
 1 files changed, 168 insertions(+), 354 deletions(-)

diff --git a/inc/rest/routes/TermRoutes.php b/inc/rest/routes/TermRoutes.php
index 438139e..7f03d49 100644
--- a/inc/rest/routes/TermRoutes.php
+++ b/inc/rest/routes/TermRoutes.php
@@ -2,9 +2,10 @@
 namespace JVBase\rest\routes;
 
 use JVBase\JVB;
-use JVBase\rest\RestRouteManager;
+use JVBase\rest\Rest;
 use JVBase\managers\TaxonomyRelationships;
 use JVBase\managers\UserTermsManager;
+use JVBase\rest\Route;
 use JVBase\utility\Features;
 use WP_REST_Request;
 use WP_REST_Response;
@@ -13,18 +14,19 @@
 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();
 		if (JVB_TESTING) {
 			$this->cache->flush();
 		}
+		$this->cache->connect('taxonomy', true);
         $this->per_page = 20;
 
         add_action('edited_term', [$this, 'deleteTermPath']);
@@ -37,60 +39,29 @@
      */
     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',
+			]);
 
-        //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();
     }
 
     /**
@@ -104,10 +75,7 @@
         $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 = [
@@ -142,7 +110,7 @@
             }
         }
 
-        return new WP_REST_Response($data);
+        return $this->success($data);
     }
 
     /**
@@ -150,31 +118,33 @@
      *
      * @return WP_REST_Response
      */
-    public function getTermDetails(WP_REST_Request $request):WP_REST_Response
-    {
-        $data = $request->get_params();
-		// Collect all taxonomies being queried
+	public function getTermDetails(WP_REST_Request $request): WP_REST_Response
+	{
+		$data = $request->get_params();
 		$taxonomies = array_keys($data);
 
-		// Check HTTP cache headers
-		$cache_check = $this->checkHeaders($request, $taxonomies);
+		$cache_check = $this->checkHeaders($request, $this->cache->generateKey(['termDetails' => $data]));
 		if ($cache_check) {
 			return $cache_check;
 		}
-        $terms = [];
-        foreach ($data as $tax => $IDs) {
-            $args = [
-                'taxonomy'  => BASE.$tax,
-                'include'   => $IDs
-            ];
 
-            $terms[$tax] = $this->formatTerms($args, BASE.$tax);
-        }
-        $response = new WP_REST_Response([
-            'items' => $terms,
-        ]);
-		return $this->addCacheHeaders($response);
-    }
+		$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
@@ -207,7 +177,7 @@
 			$key = $this->cache->generateKey($args);
 			$cached = $this->cache->get($key);
 			if ($cached) {
-				$response = new WP_REST_Response($cached);
+				$response = $this->success($cached);
 				return $this->addCacheHeaders($response);
 			}
 
@@ -216,7 +186,7 @@
 				'items'	=> $formatted
 			];
 			$this->cache->set($key, $response);
-			$response = new WP_REST_Response($response);
+			$response = $this->success($response);
 			return $this->addCacheHeaders($response);
 		}
 		if (array_key_exists('content', $data)) {
@@ -237,16 +207,7 @@
 		$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);
@@ -279,16 +240,7 @@
             $related = $manager->getUserTermIDs($userID, $taxonomy);
 
             if (empty($related)) {
-                $response = 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);
             }
 
@@ -302,16 +254,7 @@
             $related = $manager->getRelatedTerms($ID, BASE.$request->get_param('taxonomy'));
 
             if (empty($related)) {
-                $response = 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'] = [
@@ -361,16 +304,7 @@
                 $args['include'] = $related_term_ids;
             } else {
                 // No related terms found, return empty result
-                $response =  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);
             }
@@ -382,7 +316,7 @@
         $cache = $this->cache->get($key);
 
         if ($cache) {
-            $response = new WP_REST_Response($cache);
+            $response = $this->success($cache);
 			return $this->addCacheHeaders($response);
         }
 
@@ -411,7 +345,7 @@
         ];
 
         $this->cache->set($key, $response);
-        $response = new WP_REST_Response($response);
+        $response = $this->success($response);
 		return $this->addCacheHeaders($response);
     }
 
@@ -449,7 +383,7 @@
 			'has_more' => true,
 		];
 
-		$response = new WP_REST_Response($response);
+		$response = $this->success($response);
 		return $this->addCacheHeaders($response);
 	}
 
@@ -525,74 +459,56 @@
      *
      * @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' => true,
-            '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) {
-			// Search results show path, so includeChildren = false for performance
-			$formatted_terms[] = $this->formatSingleTerm($term, $taxonomy, false);
-		}
+				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
@@ -686,7 +602,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 {
@@ -785,156 +702,53 @@
 
             // 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' => true,
-                '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[] = $this->formatSingleTerm($term, $taxonomy, false);
+			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)
+				]]);
 			}
 
-            // 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));
-				error_log('Existing Term: '.print_r($term, true));
-                return new WP_REST_Response([
-                    'success' => false,
-                    'message' => 'Term already exists',
-                    'term' => [
-                        'id' => $term->term_id,
-                        'name' => html_entity_decode($term->name),
-                        'path' => $this->getTermPath($term->term_id, $term->name, $taxonomy)
-                    ]
-                ]);
-            }
-
-
 			if (Features::forMembership()->has('term_approval')) {
-				error_log('Term Approval required');
-				// Get approval routes instance
 				$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,
@@ -942,50 +756,50 @@
 						'pending' => true,
 						'request_id' => $request_id
 					]
-				];
-			} else {
-				error_log('Creating new Term: ');
-				$termID = wp_insert_term(
-					$name,
-					jvbCheckBase($taxonomy),
-					[
-						'parent'	=> absint($parent)
-					]
-				);
-				error_log('Result: '.print_r($termID, true));
-
-				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