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 | 296 ++++++++++++++++++++++-------------------------------------
1 files changed, 110 insertions(+), 186 deletions(-)
diff --git a/inc/rest/routes/FeedRoutes.php b/inc/rest/routes/FeedRoutes.php
index 2c83a63..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,13 +28,13 @@
public function __construct()
{
- $this->cache_name = 'feed';
- $this->cache_ttl = 86400;
+ $this->cacheName = 'feed';
+ $this->cacheTtl = 86400;
parent::__construct();
$this->cache
- ->connect('post')
- ->connect('taxonomy')
- ->connect('user');
+ ->connect('post', true)
+ ->connect('taxonomy', true)
+ ->connect('user', true);
if (JVB_TESTING) {
$this->cache->flush();
@@ -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