Jake Vanderwerf
2026-08-28 5b50afdcafd150186ea5c7b0e1cad98065590b35
inc/rest/routes/FeedRoutes.php
@@ -1,17 +1,19 @@
<?php
namespace JVBase\rest\routes;
use JVBase\base\Site;
use JVBase\Instance;
use JVBase\integrations\services\Umami;
use JVBase\managers\Cache;
use JVBase\meta\Meta;
use JVBase\registrar\Registrar;
use JVBase\rest\Rest;
use JVBase\integrations\Umami;
use JVBase\rest\Route;
use JVBase\base\Site;
use WP_Query;
use WP_Post;
use WP_Term;
use WP_Query;
use WP_REST_Request;
use WP_REST_Response;
use WP_Term;
if (!defined('ABSPATH')) {
    exit; // Exit if accessed directly
@@ -19,6 +21,7 @@
class FeedRoutes extends Rest
{
   use Instance;
   protected int $per_page = 36;
   protected ?Umami $tracker = null;
@@ -31,21 +34,16 @@
      $this->cacheName = 'feed';
      $this->cacheTtl = 86400;
      parent::__construct();
      $this->cache
         ->connect('post', true)
         ->connect('taxonomy', true)
         ->connect('user', true);
      $this->cache = Cache::for('feed');
      if (JVB_TESTING) {
         $this->cache->flush();
      }
   }
   public function init():void
   {
      if (Site::hasIntegration('umami')) {
         $this->tracker = JVB()->connect('umami');
         $this->tracker = Umami::getInstance();
      }
   }
@@ -70,11 +68,10 @@
            'context' => 'string',
            'contextId' => 'string',
            'favourites' => 'boolean',
            'user' => 'integer',
            'highlight' => 'string',
         ])
         ->auth('public')
         ->rateLimit(30, 60)
         ->rateLimit(30)
         ->post([$this, 'handleFeedRequest'])
         ->args([
            'content' => 'string',
@@ -89,7 +86,6 @@
            'context' => 'string',
            'contextId' => 'string',
            'favourites' => 'boolean',
            'user' => 'integer',
            'highlight' => 'string',
         ])
         ->auth('public')
@@ -105,9 +101,9 @@
   }
   /**
    * Formats an item
    * @param int $postID
    *
    * @param string $type
    * @param $skip
    * @return array
    */
   protected function formatItem(int $postID, string $type = 'post', $skip = false): array
@@ -117,11 +113,13 @@
            $post = get_post($postID);
            $type = jvbNoBase($post->post_type);
            $metaType = 'post';
            $this->cache = Cache::for('feedPosts', 'post');
            break;
         default:
            $post = get_term($postID, jvbCheckBase($type));
            $type = jvbNoBase($type);
            $metaType = 'term';
            $this->cache = Cache::for('feedTerms', 'term');
            break;
      }
      if (!$post || is_wp_error($post)) {
@@ -298,6 +296,7 @@
   protected function formatTaxonomy(WP_Term|int $term, int $postID, string $type)
   {
      $this->cache = Cache::for('feedTaxonomies', 'term');
      return $this->cache->remember(
         $term->term_id,
         function () use ($term, $postID, $type) {
@@ -318,6 +317,8 @@
   protected function getAuthorData(WP_Post $post)
   {
      //Connects to their profile, not user id
      $this->cache = Cache::for('feedUsers', 'post');
      $author = $post->post_author;
      $userLink = get_user_meta($author, BASE.'profile_link', true);
      return $this->cache->remember(
@@ -344,6 +345,10 @@
   protected function getTaxonomies(int $postID, string $content): array
   {
      $registrar = Registrar::getInstance($content)??false;
      if (!$registrar) {
         return [];
      }
      $this->cache = Cache::for('feedTaxonomyLinks', 'term');
      $taxonomies = $registrar->registrar->taxonomies;
      $out = [];
      foreach ($taxonomies as $tax) {
@@ -415,48 +420,23 @@
   public function handleFeedRequest(WP_REST_Request $request): WP_REST_Response
   {
      $args = $this->buildRequestArgs($request);
      $key = $this->cache->generateKey($args);
      // Check HTTP cache headers first
      $cache_check = $this->checkHeaders(
         $request,
         $key
      );
      if ($cache_check) {
         return $cache_check; // Returns 304 Not Modified
      }
      $cached = $this->cache->get($key);
      if ($cached) {
         if ($request->get_param('highlight')) {
            $highlight = json_decode($request->get_param('highlight'), true);
            $args['highlight'] = $highlight;
         }
         $cached['items'] = $this->processHighlightedItem($cached['items'], $args);
         $response = $this->success($cached);
         return $this->addCacheHeaders($response);
      }
      // Fetch and format items
      $items = $this->fetchFeedItems($args);
      $ttl = (str_contains($args['orderby'], 'RAND')) ? 300 : $this->cacheTtl;
      $this->cache->set($key, $items, $ttl);
      if ($request->get_param('highlight')) {
         $highlight = json_decode($request->get_param('highlight'), true);
      $highlight = $request->get_param('highlight');
      if (!empty($highlight)) {
         $highlight = json_decode($highlight, true);
         $args['highlight'] = $highlight;
         $items['items'] = $this->processHighlightedItem($items['items'], $args);
      }
      $items['items'] = $this->processHighlightedItem($items['items'], $args);
      $response = $this->success($items);
      return $this->addCacheHeaders($response);
      return $this->success($items);
   }
   /**
    * @param array $items Formatted Args for WP_Query
    * @param array $data parsed Request Data
    *
    * @return array|null
    * @return array
    */
   protected function processHighlightedItem(array $items, array $data): array
   {
@@ -537,17 +517,21 @@
   /**
    * @param array $args
    * @param array $filters
    * @param array $data
    *
    * @return array
    */
   protected function applyFavouritesFilter(array $args, array $data): array
   {
      if (empty($data['favourites']) || empty($data['user'])) {
      if (empty($data['favourites'])) {
         return $args;
      }
      $user_id = (int)$data['user'];
      $user_id = get_current_user_id();
      if (!$user_id) {
         //Shouldn't happen, but whatever
         return $args;
      }
      $content = jvbNoBase($args['post_type']);
      // Get user's favourites for this content type
@@ -592,15 +576,11 @@
      $args['fields'] = 'ids';
      // Get post IDs
      $query = new WP_Query($args);
      // Batch prefetch related data
      update_meta_cache('post', $query->posts);
      update_object_term_cache($query->posts, $args['post_type']);
      // Format regular items
      $items = array_map(fn($post) => $this->formatItem($post), $query->posts);
      wp_reset_postdata();
      return [
         'items' => $items,
         'has_more' => $query->max_num_pages > $args['paged'],
@@ -1120,16 +1100,7 @@
    */
   public function getFeedTypes(WP_REST_Request $request): WP_REST_Response
   {
      // Check HTTP cache
      $cache_check = $this->checkHeaders($request, 'feed_types');
      if ($cache_check) {
         return $cache_check;
      }
      $feedTypes = $this->buildFeedTypesConfig();
      $response = $this->success($feedTypes);
      return $this->addCacheHeaders($response);
      return $this->success($this->buildFeedTypesConfig());
   }
   public function getFeedTypesConfig():array
@@ -1149,7 +1120,6 @@
            // Get content types with show_feed
            $contentTypes = Registrar::withFeature('show_feed', 'post');
            foreach ($contentTypes as $slug) {
               $this->cache->tag('content:'.$slug);
               $registrar = Registrar::getInstance($slug);
               if (!$registrar) continue;
@@ -1169,9 +1139,6 @@
               if (!$registrar || !($registrar->hasFeature('is_content') ?? false)) {
                  continue;
               }
               $this->cache->tag('taxonomy:'.$slug);
               $config[$slug] = [
                  'type' => 'taxonomy',
                  'singular' => $registrar->getSingular(),