| | |
| | | <?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; |
| | |
| | | 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); |
| | | } |
| | |
| | | */ |
| | | 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(); |
| | | } |
| | | |
| | | /** |
| | |
| | | $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'] = []; |
| | |
| | | $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); |
| | |
| | | } |
| | | } |
| | | |
| | | return new WP_REST_Response($data); |
| | | return $this->success($data); |
| | | } |
| | | |
| | | /** |
| | |
| | | * |
| | | * @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 |
| | |
| | | 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); |
| | |
| | | '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); |
| | |
| | | |
| | | // 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', |
| | |
| | | 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; |
| | |
| | | $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, |
| | |
| | | $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 = []; |
| | | |
| | |
| | | $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); |
| | | } |
| | | } |
| | | |
| | |
| | | |
| | | $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); |
| | |
| | | '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 |
| | |
| | | */ |
| | | 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'); |
| | |
| | | $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 { |
| | |
| | | $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); |
| | |
| | | '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, |
| | |
| | | '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 |
| | | ]); |
| | | } |
| | | } |