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/FeedRoutes.php |  290 +++++++++++++++++++++------------------------------------
 1 files changed, 107 insertions(+), 183 deletions(-)

diff --git a/inc/rest/routes/FeedRoutes.php b/inc/rest/routes/FeedRoutes.php
index 8c50485..af6c2c1 100644
--- a/inc/rest/routes/FeedRoutes.php
+++ b/inc/rest/routes/FeedRoutes.php
@@ -1,11 +1,10 @@
 <?php
 namespace JVBase\rest\routes;
 
-use JVBase\managers\Cache;
-use JVBase\rest\RestRouteManager;
+use JVBase\meta\Meta;
+use JVBase\rest\Rest;
 use JVBase\integrations\Umami;
-use JVBase\meta\MetaManager;
-use JVBase\managers\TaxonomyRelationships;
+use JVBase\rest\Route;
 use JVBase\utility\Checker;
 use JVBase\utility\Features;
 use WP_Query;
@@ -18,7 +17,7 @@
     exit; // Exit if accessed directly
 }
 
-class FeedRoutes extends RestRouteManager
+class FeedRoutes extends Rest
 {
 	protected int $per_page = 36;
 	protected ?Umami $tracker = null;
@@ -29,8 +28,8 @@
 
 	public function __construct()
 	{
-		$this->cache_name = 'feed';
-		$this->cache_ttl = 86400;
+		$this->cacheName = 'feed';
+		$this->cacheTtl = 86400;
 		parent::__construct();
 		$this->cache
 			->connect('post', true)
@@ -58,17 +57,51 @@
 	 */
 	public function registerRoutes(): void
 	{
-		register_rest_route($this->namespace, '/feed', [
-			'methods' => ['GET', 'POST'],
-			'callback' => [$this, 'handleFeedRequest'],
-			'permission_callback' => [$this, 'checkPermission'],
-		]);
+		Route::for('feed')
+			->get([$this, 'handleFeedRequest'])
+			->args([
+				'content' => 'string',
+				'page' => 'integer|default:1|min:1',
+				'taxonomy' => 'string',
+				'match' => 'string|enum:all,any|default:all',
+				'orderby' => 'string',
+				'order' => 'string|enum:ASC,DESC',
+				'date-filter' => 'string',
+				'dateFrom' => 'string',
+				'dateTo' => 'string',
+				'context' => 'string',
+				'source' => 'string',
+				'favourites' => 'boolean',
+				'user' => 'integer',
+				'highlight' => 'string',
+			])
+			->auth('public')
+			->rateLimit(30, 60)
+			->post([$this, 'handleFeedRequest'])
+			->args([
+				'content' => 'string',
+				'page' => 'integer|default:1|min:1',
+				'taxonomy' => 'string',
+				'match' => 'string|enum:all,any|default:all',
+				'orderby' => 'string',
+				'order' => 'string|enum:ASC,DESC',
+				'date-filter' => 'string',
+				'dateFrom' => 'string',
+				'dateTo' => 'string',
+				'context' => 'string',
+				'source' => 'string',
+				'favourites' => 'boolean',
+				'user' => 'integer',
+				'highlight' => 'string',
+			])
+			->auth('public')
+			->rateLimit(30, 60);
 
-		register_rest_route($this->namespace, 'feed/types', [
-			'permission_callback' => [$this, 'checkPermission'],
-			'methods' => 'GET',
-			'callback' => [$this, 'getFeedTypes']
-		]);
+		// Feed types endpoint
+		Route::for('feed/types')
+			->get([$this, 'getFeedTypes'])
+			->auth('public')
+			->rateLimit(60, 60);
 	}
 
 	/**
@@ -102,11 +135,15 @@
 				switch ($metaType) {
 					case 'post':
 						$config = JVB_CONTENT[$type];
+
+						$meta = Meta::forPost($postID);
 						if (!$skip && array_key_exists('is_timeline', $config) && $config['is_timeline']) {
 							return $this->formatTimeline($postID, $post);
 						}
 						break;
 					case 'term':
+
+						$meta = Meta::forTerm($postID);
 						$config = JVB_TAXONOMY[$type];
 						break;
 				}
@@ -122,7 +159,6 @@
 					}, ARRAY_FILTER_USE_KEY);
 				}
 
-				$meta = new MetaManager($postID, $metaType);
 				$values = $meta->getAll(array_keys($fields));
 
 				$out = [
@@ -216,7 +252,7 @@
 		}
 		$item = $this->formatItem($postID, 'post', true);
 		//Step 1: Get the fields that apply to all posts
-		$mainMeta = new MetaManager($post->ID, 'post');
+		$mainMeta = Meta::forPost($post->ID);
 		$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
 
 		//Step 2: Get the fields for each individual posts
@@ -228,7 +264,7 @@
 		$subFields = [];
 		$images = [];
 		foreach ($children as $child) {
-			$meta = new MetaManager($child, 'post');
+			$meta = Meta::forPost($child);
 			$f = $meta->getAll($this->timelineUniqueFields);
 			$f =  ['id' => $child] + $f;
 			$subFields[] = $f;
@@ -385,44 +421,6 @@
 		return $this->applyFavouritesFilter($args, $data);
 	}
 
-//	protected function applyTaxonomyFilters(array $args, array $data): array
-//	{
-//		if (!array_key_exists('taxonomy', $data) || empty($data['taxonomy'])) {
-//			return $args;
-//		}
-//
-//		$taxonomyFilters = $data['taxonomy'];
-//
-//		// Validate taxonomies exist and sanitize
-//		$validFilters = [];
-//		foreach ($taxonomyFilters as $taxonomy => $terms) {
-//			if (!taxonomy_exists(jvbCheckBase($taxonomy))) {
-//				continue;
-//			}
-//
-//			$validFilters[] = [
-//				'taxonomy' => jvbCheckBase($taxonomy),
-//				'field' => 'term_id',
-//				'terms' => array_map('absint', (array)$terms),
-//				'operator' => 'IN'
-//			];
-//		}
-//
-//		if (empty($validFilters)) {
-//			return $args;
-//		}
-//
-//		// Determine relation based on match filter
-//		$relation = ($data['match'] ?? 'all') === 'all' ? 'AND' : 'OR';
-//
-//		$args['tax_query'] = array_merge(
-//			['relation' => $relation],
-//			$validFilters
-//		);
-//
-//		return $args;
-//	}
-
 	/**
 	 * @param WP_REST_Request $request
 	 *
@@ -449,13 +447,13 @@
 				$args['highlight'] = $highlight;
 			}
 			$cached['items'] = $this->processHighlightedItem($cached['items'], $args);
-			$response = new WP_REST_Response($cached);
+			$response = $this->success($cached);
 			return $this->addCacheHeaders($response);
 		}
 		// Fetch and format items
 		$items = $this->fetchFeedItems($args);
 
-		$ttl = (str_contains($args['orderby'], 'RAND')) ? 300 : $this->cache_ttl;
+		$ttl = (str_contains($args['orderby'], 'RAND')) ? 300 : $this->cacheTtl;
 		$this->cache->set($key, $items, $ttl);
 
 		if ($request->get_param('highlight')) {
@@ -464,86 +462,12 @@
 		}
 
 		$items['items'] = $this->processHighlightedItem($items['items'], $args);
-		$response = new WP_REST_Response($items);
+		$response = $this->success($items);
 		return $this->addCacheHeaders($response);
 	}
 
 	/**
-	 * Build cache context from query args
-	 * Extracts content types and parameters needed for proper cache checking
-	 *
-	 * @param array $args Built WP_Query arguments
-	 * @param WP_REST_Request $request Original request
-	 * @return array Cache context with content_types and additional_params
-	 */
-	protected function buildCacheContext(array $args, WP_REST_Request $request): array
-	{
-		// Extract content types from post_type in args
-		$post_types = is_array($args['post_type'])
-			? $args['post_type']
-			: [$args['post_type']];
-
-		$content_types = array_map('jvbNoBase', $post_types);
-		$content_types[] = 'feed'; // Always include base feed type
-
-		// Build additional params for ETag uniqueness
-		$additional_params = [
-			'order' => $args['orderby'] ?? 'date',
-			'direction' => $args['order'] ?? 'DESC',
-			'page' => $args['paged'] ?? 1,
-		];
-
-		if ($request->get_param('favourites')) {
-			$additional_params['user'] = (int)$request->get_param('user');
-		}
-
-		// Include author filter if present (from context or favourites)
-		if (!empty($args['author'])) {
-			$additional_params['author'] = $args['author'];
-		}
-
-		if (!empty($args['author__in'])) {
-			$additional_params['author__in'] = $args['author__in'];
-		}
-
-		// Include taxonomy filters if present
-		if (!empty($args['tax_query'])) {
-			$tax_filters = [];
-			foreach ($args['tax_query'] as $key => $query) {
-				if ($key === 'relation' || !is_array($query)) {
-					continue;
-				}
-
-				$taxonomy = jvbNoBase($query['taxonomy'] ?? '');
-				if ($taxonomy) {
-					$tax_filters[$taxonomy] = $query['terms'] ?? [];
-					// Also add taxonomy to content_types for timestamp checking
-					$content_types[] = $taxonomy;
-				}
-			}
-			if (!empty($tax_filters)) {
-				$additional_params['taxonomies'] = $tax_filters;
-			}
-		}
-
-		// Include date filters if present
-		if (!empty($args['date_query'])) {
-			$additional_params['date_filter'] = md5(serialize($args['date_query']));
-		}
-
-		// Include meta queries if present
-		if (!empty($args['meta_query'])) {
-			$additional_params['meta_filter'] = md5(serialize($args['meta_query']));
-		}
-
-		return [
-			'content_types' => array_unique($content_types),
-			'additional_params' => $additional_params
-		];
-	}
-
-	/**
-	 * @param array $args Formatted Args for WP_Query
+	 * @param array $items Formatted Args for WP_Query
 	 * @param array $data parsed Request Data
 	 *
 	 * @return array|null
@@ -597,35 +521,39 @@
 					: explode(',', $args['post_type']);
 
 				// Check if filtering global feed content
-				$globalFeedTypes = array_map('jvbCheckBase',
-					array_keys(Features::getTypesWithFeature('show_feed', 'content'))
-				);
-
-				if (array_intersect($args['post_type'], $globalFeedTypes)) {
-					$artists = jvbGetContentUsers($context['id']);
-					if (!empty($artists)) {
-						$args['author__in'] = $artists;
+				if (in_array($context['type'], jvbGlobalFeedContentTaxonomies())) {
+					// Global: show posts from any content type with this taxonomy
+					$for_content = JVB_TAXONOMY[$context['type']]['for_content'] ?? [];
+					if (empty($for_content)) {
+						// Fall back to any content that has this taxonomy registered
+						$for_content = array_keys(
+							array_filter(
+								JVB_CONTENT,
+								fn($c) => in_array($context['type'], $c['taxonomies'] ?? [])
+							)
+						);
 					}
-				} else {
-					$args['tax_query'] = [
-						'relation' => 'AND',
-						[
-							'taxonomy' => BASE . $context['type'],
-							'terms' => $context['id'],
-						]
-					];
+
+					// Convert to full post types with BASE prefix
+					$post_types = array_map(fn($type) => BASE . $type, $for_content);
+
+					// Filter to only show_feed content types
+					$show_feed_types = Features::getTypesWithFeature('show_feed', 'content');
+					$args['post_type'] = array_intersect(
+						$post_types,
+						array_map(fn($type) => BASE . $type, $show_feed_types)
+					);
 				}
-				break;
-			case taxonomy_exists(jvbCheckBase($context['type'])):
-				$args['tax_query'] = [
-					'relation' => 'AND',
-					[
-						'taxonomy' => BASE . $context['type'],
-						'terms' => $context['id'],
-					]
+
+				// Add term to tax query
+				$args['tax_query'][] = [
+					'taxonomy' => jvbCheckBase($context['type']),
+					'field' => 'term_id',
+					'terms' => [(int)$context['id']],
 				];
 				break;
 		}
+
 		return $args;
 	}
 
@@ -635,38 +563,34 @@
 	 *
 	 * @return array
 	 */
-	protected function applyFavouritesFilter(array $args, array $filters): array
+	protected function applyFavouritesFilter(array $args, array $data): array
 	{
-		if (!array_key_exists('favourites', $filters)) {
+		if (empty($data['favourites']) || empty($data['user'])) {
 			return $args;
 		}
-		global $wpdb;
 
-		// Get post types for the current filter
-		$post_types = is_array($args['post_type'])
-			? $args['post_type']
-			: [$args['post_type']];
+		$user_id = (int)$data['user'];
+		$content = jvbNoBase($args['post_type']);
 
-		$favourites_table = $wpdb->prefix . BASE . 'favourites';
-		$placeholders = implode(',', array_fill(0, count($post_types), '%s'));
-		$favourited_ids = $wpdb->get_col($wpdb->prepare(
-			"SELECT target_id FROM {$favourites_table}
-            WHERE user_id = %d AND type IN ($placeholders)",
-			array_merge(
-				[get_current_user_id()],
-				$post_types
-			)
-		));
+		// Get user's favourites for this content type
+		$fav_key = BASE . 'favourites_' . $content;
+		$favourites = get_user_meta($user_id, $fav_key, true);
 
-		if (empty($favourited_ids)) {
-			// Force empty results
+		if (empty($favourites)) {
+			// No favourites - return empty result
+			$args['post__in'] = [0]; // Will return no results
+			return $args;
+		}
+
+		$fav_ids = array_filter(array_map('intval', explode(',', $favourites)));
+
+		if (empty($fav_ids)) {
 			$args['post__in'] = [0];
 			return $args;
 		}
 
-		$args['post__in'] = isset($args['post__in'])
-			? array_intersect($args['post__in'], $favourited_ids)
-			: $favourited_ids;
+		$args['post__in'] = $fav_ids;
+		$args['orderby'] = 'post__in'; // Preserve favourite order
 
 		return $args;
 	}
@@ -1235,7 +1159,7 @@
 
 		$feedTypes = $this->buildFeedTypesConfig();
 
-		$response = new WP_REST_Response($feedTypes);
+		$response = $this->success($feedTypes);
 		return $this->addCacheHeaders($response);
 	}
 

--
Gitblit v1.10.0