PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.2.0
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.2.0
4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.5.2 All 199 releases
← All changes | includes/Core/Query.php +1088 -1147 4.5.54.2.0 View file →
@@ -1,14 +1,8 @@
1 1 <?php
2 -// phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_query,WordPress.DB.SlowDBQuery.slow_db_query_tax_query,WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- core docs query builder; meta/tax filtering required for docs/category/KB filtering.
3 -// phpcs:disable WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- excludes are user-driven (settings UI) and part of the query builder's public contract.
2 +
4 3 namespace WPDeveloper\BetterDocs\Core;
5 4
6 -if ( ! defined( 'ABSPATH' ) ) {
7 - exit;
8 -}
9 -
10 -
11 5 use WP_Query;
12 6 use WPDeveloper\BetterDocs\Utils\Base;
13 7 use WPDeveloper\BetterDocs\Utils\Database;
14 8 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
@@ -13,1113 +7,1066 @@
13 7 use WPDeveloper\BetterDocs\Utils\Database;
14 8 use WPDeveloper\BetterDocs\Dependencies\DI\Container;
15 9
16 10 class Query extends Base {
17 - private $container;
18 - protected $database;
19 - protected $settings;
11 + private $container;
12 + protected $database;
13 + protected $settings;
20 14
21 - public function __construct( Container $container, Database $database, Settings $settings ) {
22 - $this->container = $container;
23 - $this->database = $database;
24 - $this->settings = $settings;
15 + public function __construct( Container $container, Database $database, Settings $settings ) {
16 + $this->container = $container;
17 + $this->database = $database;
18 + $this->settings = $settings;
25 19
26 - add_action( 'parse_term_query', array( $this, 'parse_term_query' ) );
27 - // add_action( 'parse_query', [$this, 'parse_query'], 1 );
28 - add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ), 1 );
29 - add_filter( 'betterdocs_base_terms_args', array( $this, 'modify_terms_args_for_private_docs' ), 10, 1 );
20 + add_action( 'parse_term_query', [ $this, 'parse_term_query' ] );
21 + // add_action( 'parse_query', [$this, 'parse_query'], 1 );
22 + add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ], 1 );
23 + add_filter( 'betterdocs_base_terms_args', [ $this, 'modify_terms_args_for_private_docs' ], 10, 1 );
30 24
31 - /**
32 - * These below filters are hooked for navigation only.
33 - *
34 - * For old version of this portion.
35 - * @see `betterdocs_single_post_nav` filter
36 - *
37 - * For details:
38 - * @see https://developer.wordpress.org/reference/functions/get_next_post/
39 - * @see https://developer.wordpress.org/reference/functions/get_previous_post/
40 - *
41 - * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
42 - */
43 - add_filter( 'get_next_post_where', array( $this, 'next_post_where' ), 99, 5 );
44 - add_filter( 'get_previous_post_where', array( $this, 'previous_post_where' ), 99, 5 );
25 + /**
26 + * These below filters are hooked for navigation only.
27 + *
28 + * For old version of this portion.
29 + * @see `betterdocs_single_post_nav` filter
30 + *
31 + * For details:
32 + * @see https://developer.wordpress.org/reference/functions/get_next_post/
33 + * @see https://developer.wordpress.org/reference/functions/get_previous_post/
34 + *
35 + * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
36 + */
37 + add_filter( 'get_next_post_where', [ $this, 'next_post_where' ], 99, 5 );
38 + add_filter( 'get_previous_post_where', [ $this, 'previous_post_where' ], 99, 5 );
45 39
46 - $this->init();
40 + $this->init();
47 41
48 - /**
49 - * Modify Popular Docs Query (For Shortcode & Widget)
50 - */
51 - add_filter( 'posts_clauses', array( $this, 'mod_query_popular_docs' ), 10, 2 );
52 - }
42 + /**
43 + * Modify Popular Docs Query (For Shortcode & Widget)
44 + */
45 + add_filter( 'posts_clauses', [ $this, 'mod_query_popular_docs' ], 10, 2 );
46 + }
53 47
54 - public function mod_query_popular_docs( $clauses, $wp_query ) {
55 - if ( isset( $wp_query->query[ 'meta_key' ] ) ) {
56 - if ( '_betterdocs_meta_views' == $wp_query->query[ 'meta_key' ] ) {
57 - global $wpdb;
58 - $order = isset( $wp_query->query[ 'order' ] ) ? $wp_query->query[ 'order' ] : '';
59 - $order_by_query = ( 'ASC' == $order || 'DESC' == $order ) ? "SUM({$wpdb->prefix}betterdocs_analytics.impressions) {$order}" : ( 'MODIFIED' == $order ? "{$wpdb->prefix}posts.post_modified_gmt DESC" : "{$wpdb->prefix}posts.post_date_gmt DESC" );
60 - $clauses[ 'join' ] = "JOIN {$wpdb->prefix}betterdocs_analytics ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}betterdocs_analytics.post_id";
61 - $clauses[ 'where' ] = ! current_user_can( 'read_private_docs' ) ? "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' ) )" : "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' OR {$wpdb->prefix}posts.post_status = 'draft' OR {$wpdb->prefix}posts.post_status = 'pending' OR {$wpdb->prefix}posts.post_status = 'private' ) )";
62 - $clauses[ 'orderby' ] = $order_by_query;
63 - $clauses[ 'groupby' ] = "{$wpdb->prefix}betterdocs_analytics.post_id";
64 - if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
65 - global $sitepress;
66 - if ( $sitepress->is_setup_complete() ) {
67 - $constant_language_code = ICL_LANGUAGE_CODE;
68 - $clauses[ 'join' ] .= " JOIN {$wpdb->prefix}icl_translations ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}icl_translations.element_id";
69 - $clauses[ 'where' ] .= " AND ( {$wpdb->prefix}icl_translations.language_code = '{$constant_language_code}' ) AND ( {$wpdb->prefix}icl_translations.element_type = CONCAT('post_', {$wpdb->prefix}posts.post_type ) )";
70 - }
71 - }
72 - }
73 - }
74 - return $clauses;
75 - }
48 + public function mod_query_popular_docs( $clauses, $wp_query ) {
49 + if ( isset( $wp_query->query['meta_key'] ) ) {
50 + if ( $wp_query->query['meta_key'] == '_betterdocs_meta_views' ) {
51 + global $wpdb;
52 + $order = isset( $wp_query->query['order'] ) ? $wp_query->query['order'] : '';
53 + $order_by_query = ( $order == 'ASC' || $order == 'DESC' ) ? "SUM({$wpdb->prefix}betterdocs_analytics.impressions) {$order}" : ( $order == 'MODIFIED' ? "{$wpdb->prefix}posts.post_modified_gmt DESC" : "{$wpdb->prefix}posts.post_date_gmt DESC" );
54 + $clauses['join'] = "JOIN {$wpdb->prefix}betterdocs_analytics ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}betterdocs_analytics.post_id";
55 + $clauses['where'] = ! current_user_can( 'read_private_docs' ) ? "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' ) )" : "AND ( ( {$wpdb->prefix}posts.post_type = 'docs' ) AND ( {$wpdb->prefix}posts.post_status = 'publish' OR {$wpdb->prefix}posts.post_status = 'future' OR {$wpdb->prefix}posts.post_status = 'draft' OR {$wpdb->prefix}posts.post_status = 'pending' OR {$wpdb->prefix}posts.post_status = 'private' ) )";
56 + $clauses['orderby'] = $order_by_query;
57 + $clauses['groupby'] = "{$wpdb->prefix}betterdocs_analytics.post_id";
58 + if ( is_plugin_active( 'sitepress-multilingual-cms/sitepress.php' ) ) {
59 + global $sitepress;
60 + if ( $sitepress->is_setup_complete() ) {
61 + $constant_language_code = ICL_LANGUAGE_CODE;
62 + $clauses['join'] .= " JOIN {$wpdb->prefix}icl_translations ON {$wpdb->prefix}posts.ID = {$wpdb->prefix}icl_translations.element_id";
63 + $clauses['where'] .= " AND ( {$wpdb->prefix}icl_translations.language_code = '{$constant_language_code}' ) AND ( {$wpdb->prefix}icl_translations.element_type = CONCAT('post_', {$wpdb->prefix}posts.post_type ) )";
64 + }
65 + }
66 + }
67 + }
68 + return $clauses;
69 + }
76 70
77 - public function init() {
78 - }
71 + public function init() {
72 + }
79 73
80 - /**
81 - * Modify terms args to include terms with only private docs for users with read_private_docs capability
82 - *
83 - * @param array $args
84 - * @return array
85 - */
86 - public function modify_terms_args_for_private_docs( $args ) {
87 - // Only modify for users with read_private_docs capability and supported taxonomies
88 - if ( ! current_user_can( 'read_private_docs' ) || ! isset( $args[ 'taxonomy' ] ) ) {
89 - return $args;
90 - }
74 + /**
75 + * Modify terms args to include terms with only private docs for users with read_private_docs capability
76 + *
77 + * @param array $args
78 + * @return array
79 + */
80 + public function modify_terms_args_for_private_docs( $args ) {
81 + // Only modify for users with read_private_docs capability and supported taxonomies
82 + if ( ! current_user_can( 'read_private_docs' ) || ! isset( $args['taxonomy'] ) ) {
83 + return $args;
84 + }
91 85
92 - // Only support doc_category taxonomy for private docs filtering
93 - // knowledge_base terms don't have docs directly assigned to them
94 - $supported_taxonomies = array( 'doc_category' );
95 - if ( ! in_array( $args[ 'taxonomy' ], $supported_taxonomies ) ) {
96 - return $args;
97 - }
86 + // Only support doc_category taxonomy for private docs filtering
87 + // knowledge_base terms don't have docs directly assigned to them
88 + $supported_taxonomies = ['doc_category'];
89 + if ( ! in_array( $args['taxonomy'], $supported_taxonomies ) ) {
90 + return $args;
91 + }
98 92
99 - // If hide_empty is true, we need to modify the logic to include terms with private docs
100 - if ( isset( $args[ 'hide_empty' ] ) && $args[ 'hide_empty' ] ) {
101 - // Set hide_empty to false and we'll filter manually later
102 - $args[ 'hide_empty' ] = false;
103 - // Add a flag to indicate we need to filter manually
104 - $args[ '_betterdocs_filter_private' ] = true;
105 - }
93 + // If hide_empty is true, we need to modify the logic to include terms with private docs
94 + if ( isset( $args['hide_empty'] ) && $args['hide_empty'] ) {
95 + // Set hide_empty to false and we'll filter manually later
96 + $args['hide_empty'] = false;
97 + // Add a flag to indicate we need to filter manually
98 + $args['_betterdocs_filter_private'] = true;
99 + }
106 100
107 - return $args;
108 - }
101 + return $args;
102 + }
109 103
110 - public function parse_term_query( $term_query ) {
111 - if ( empty( $term_query->query_vars[ 'taxonomy' ] ) ) {
112 - return;
113 - }
104 + public function parse_term_query( $term_query ) {
105 + if ( empty( $term_query->query_vars['taxonomy'] ) ) {
106 + return;
107 + }
114 108
115 - if ( ! in_array( 'doc_category', $term_query->query_vars[ 'taxonomy' ], true ) ) {
116 - return;
117 - }
109 + if ( ! in_array( 'doc_category', $term_query->query_vars['taxonomy'], true ) ) {
110 + return;
111 + }
118 112
119 - global $current_screen;
113 + global $current_screen;
120 114
121 - if ( null == $current_screen ) {
122 - return;
123 - }
115 + if ( $current_screen == null ) {
116 + return;
117 + }
124 118
125 - if ( 'doc_category' !== $current_screen->taxonomy || 'edit-doc_category' != $current_screen->id ) {
126 - return;
127 - }
119 + if ( $current_screen->taxonomy !== 'doc_category' || $current_screen->id != 'edit-doc_category' ) {
120 + return;
121 + }
128 122
129 - // Use base meta key for admin listing - fallback logic is handled in set_tax_order
130 - $meta_key = 'doc_category_order';
123 + $term_query->query_vars['meta_query'] = [
124 + [
125 + 'key' => 'doc_category_order',
126 + 'type' => 'NUMERIC'
127 + ]
128 + ];
131 129
132 - $term_query->query_vars[ 'meta_query' ] = array(
133 - array(
134 - 'key' => $meta_key,
135 - 'type' => 'NUMERIC'
136 - )
137 - );
130 + $term_query->query_vars['orderby'] = 'meta_value_num';
131 + }
138 132
139 - $term_query->query_vars[ 'orderby' ] = 'meta_value_num';
140 - }
133 + public function parse_query( &$query ) {
134 + // dump( is_single(), $query->query_vars );
135 + // if ( is_single() && isset( $query->query_vars['post_type'] ) && $query->query_vars['post_type'] == 'docs' ) {
136 + // $query->is_single = false;
137 + // $query->is_archive = true;
138 + // $query->set( 'knowledge_base', $query->query_vars['docs'] );
139 + // }
140 + }
141 141
142 - public function parse_query( &$query ) {
143 - // dump( is_single(), $query->query_vars );
144 - // if ( is_single() && isset( $query->query_vars['post_type'] ) && $query->query_vars['post_type'] == 'docs' ) {
145 - // $query->is_single = false;
146 - // $query->is_archive = true;
147 - // $query->set( 'knowledge_base', $query->query_vars['docs'] );
148 - // }
149 - }
142 + public function pre_get_posts( &$query ) {
143 + if ( is_admin() || ! $query->is_main_query() ) {
144 + return;
145 + }
150 146
151 - public function pre_get_posts( &$query ) {
152 - if ( is_admin() || ! $query->is_main_query() ) {
153 - return;
154 - }
147 + if ( is_tax( 'doc_category' ) ) {
148 + $query->set( 'post_type', 'docs' );
149 + $query->set( 'posts_per_archive_page', -1 );
155 150
156 - if ( is_tax( 'doc_category' ) ) {
157 - $query->set( 'post_type', 'docs' );
158 - $query->set( 'posts_per_archive_page', -1 );
151 + $term = get_term_by( 'slug', $query->get( 'doc_category', '' ), 'doc_category' );
159 152
160 - $term = get_term_by( 'slug', $query->get( 'doc_category', '' ), 'doc_category' );
153 + if ( $term && isset( $term->term_id ) ) {
154 + $post__in = $this->get_docs_order_by_terms( $term->term_id );
155 + } else {
156 + $post__in = [];
157 + }
158 + if ( ! empty( $post__in ) ) {
159 + $query->set( 'orderby', 'post__in' );
160 + $query->set( 'post__in', $post__in );
161 + }
161 162
162 - if ( $term && isset( $term->term_id ) ) {
163 - $post__in = $this->get_docs_order_by_terms( $term->term_id );
164 - } else {
165 - $post__in = array();
166 - }
167 - if ( ! empty( $post__in ) ) {
168 - $query->set( 'orderby', 'post__in' );
169 - $query->set( 'post__in', $post__in );
170 - }
163 + // if ( ! empty( $query->query_vars['knowledge_base'] ) ) {
164 + // $query->betterdocs_terms = get_terms( [
165 + // 'taxonomy' => 'doc_category',
166 + // 'parent' => 0,
167 + // 'hide_empty' => true,
168 + // 'meta_query' => [
169 + // 'relation' => 'OR',
170 + // [
171 + // 'key' => 'doc_category_knowledge_base',
172 + // 'value' => $query->query_vars['knowledge_base'],
173 + // 'compare' => 'LIKE'
174 + // ]
175 + // ]
176 + // ] );
177 + // }
178 + }
171 179
172 - // if ( ! empty( $query->query_vars['knowledge_base'] ) ) {
173 - // $query->betterdocs_terms = get_terms( [
174 - // 'taxonomy' => 'doc_category',
175 - // 'parent' => 0,
176 - // 'hide_empty' => true,
177 - // 'meta_query' => [
178 - // 'relation' => 'OR',
179 - // [
180 - // 'key' => 'doc_category_knowledge_base',
181 - // 'value' => $query->query_vars['knowledge_base'],
182 - // 'compare' => 'LIKE'
183 - // ]
184 - // ]
185 - // ] );
186 - // }
187 - }
180 + // dump( is_archive(), $query->query_vars );
181 + }
188 182
189 - // dump( is_archive(), $query->query_vars );
190 - }
183 + /**
184 + * Get docs orders by term_id.
185 + *
186 + * @since 2.5.0
187 + * @param int $term_id
188 + *
189 + * @return array
190 + */
191 + public function get_docs_order_by_terms( $term_id ) {
192 + global $wpdb;
193 + $_docs_order = get_term_meta( $term_id, '_docs_order', true );
194 + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
195 + $query_key = "docs_order_by_terms_{$term_id}_" . md5( $query );
191 196
192 - /**
193 - * Get docs orders by term_id.
194 - *
195 - * @since 2.5.0
196 - * @param int $term_id
197 - *
198 - * @return array
199 - */
200 - public function get_docs_order_by_terms( $term_id ) {
201 - global $wpdb;
197 + if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
198 + return $results;
199 + }
202 200
203 - // Get language-specific meta key with fallback
204 - $meta_key = \WPDeveloper\BetterDocs\Utils\Helper::get_meta_key_with_fallback( '_docs_order', $term_id );
205 - $_docs_order = get_term_meta( $term_id, $meta_key, true );
201 + if ( ! empty( $_docs_order ) ) {
202 + $_docs_order = explode( ',', $_docs_order );
203 + $new_ids = [];
206 204
207 - $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
208 - $query_key = "docs_order_by_terms_{$term_id}_{$meta_key}_" . md5( $query );
205 + $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
209 206
210 - if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
211 - return $results;
212 - }
207 + if ( is_array( $results ) && ! empty( $results ) ) {
208 + $object_ids = array_filter(
209 + $results,
210 + function ( $value ) use ( $_docs_order ) {
211 + return ! in_array( $value->object_id, $_docs_order );
212 + }
213 + );
213 214
214 - if ( ! empty( $_docs_order ) ) {
215 - $_docs_order = explode( ',', $_docs_order );
216 - $new_ids = array();
215 + if ( ! empty( $object_ids ) ) {
216 + array_walk(
217 + $object_ids,
218 + function ( $value ) use ( &$new_ids ) {
219 + $new_ids[] = $value->object_id;
220 + }
221 + );
222 + }
223 + }
217 224
218 - // $query is prepared upstream; results are cached via $this->database->get_cache above (line 210).
219 - $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
225 + $_docs_order = array_merge( $new_ids, $_docs_order );
226 + $this->database->set_cache( $query_key, $_docs_order, 1 );
220 227
221 - if ( is_array( $results ) && ! empty( $results ) ) {
222 - $object_ids = array_filter(
223 - $results,
224 - function ( $value ) use ( $_docs_order ) {
225 - return ! in_array( $value->object_id, $_docs_order );
226 - }
227 - );
228 + return $_docs_order;
229 + }
228 230
229 - if ( ! empty( $object_ids ) ) {
230 - array_walk(
231 - $object_ids,
232 - function ( $value ) use ( &$new_ids ) {
233 - $new_ids[ ] = $value->object_id;
234 - }
235 - );
236 - }
237 - }
231 + return [];
232 + }
238 233
239 - $_docs_order = array_merge( $new_ids, $_docs_order );
240 - $this->database->set_cache( $query_key, $_docs_order, 1 );
234 + /**
235 + * For determine the next post ID
236 + *
237 + * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
238 + *
239 + * @param mixed $where
240 + * @param mixed $in_same_term
241 + * @param mixed $excluded_terms
242 + * @param mixed $taxonomy
243 + * @param mixed $post
244 + * @return mixed
245 + */
246 + public function next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
247 + return $this->get_adjacent_post_id( 'next', $where, $post, $taxonomy );
248 + }
241 249
242 - return $_docs_order;
243 - }
250 + /**
251 + * For determine the previous post ID
252 + *
253 + * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
254 + *
255 + * @param mixed $where
256 + * @param mixed $in_same_term
257 + * @param mixed $excluded_terms
258 + * @param mixed $taxonomy
259 + * @param mixed $post
260 + * @return mixed
261 + */
262 + public function previous_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
263 + return $this->get_adjacent_post_id( 'previous', $where, $post, $taxonomy );
264 + }
244 265
245 - return array();
246 - }
266 + /**
267 + * Get where clause for next/previous post ID mainly used in post navigation.
268 + *
269 + * @see `previous_post_where` and `next_post_where` methods.
270 + *
271 + * @param mixed $adjacent
272 + * @param mixed $where
273 + * @param mixed $post
274 + * @param mixed $taxonomy
275 + * @return mixed
276 + */
277 + private function get_adjacent_post_id( $adjacent, $where, $post, $taxonomy ) {
278 + if ( $taxonomy !== 'doc_category' ) {
279 + return $where;
280 + }
247 281
248 - /**
249 - * For determine the next post ID
250 - *
251 - * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
252 - *
253 - * @param mixed $where
254 - * @param mixed $in_same_term
255 - * @param mixed $excluded_terms
256 - * @param mixed $taxonomy
257 - * @param mixed $post
258 - * @return mixed
259 - */
260 - public function next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
261 - return $this->get_adjacent_post_id( 'next', $where, $post, $taxonomy );
262 - }
282 + $_id = null;
283 + $_terms = get_the_terms( $post->ID, 'doc_category' );
284 + if ( empty( $_terms ) ) {
285 + return $where;
286 + }
263 287
264 - /**
265 - * For determine the previous post ID
266 - *
267 - * @link https://developer.wordpress.org/reference/hooks/get_adjacent_post_where/
268 - *
269 - * @param mixed $where
270 - * @param mixed $in_same_term
271 - * @param mixed $excluded_terms
272 - * @param mixed $taxonomy
273 - * @param mixed $post
274 - * @return mixed
275 - */
276 - public function previous_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
277 - return $this->get_adjacent_post_id( 'previous', $where, $post, $taxonomy );
278 - }
288 + global $wp_query, $wpdb;
279 289
280 - /**
281 - * Get where clause for next/previous post ID mainly used in post navigation.
282 - *
283 - * @see `previous_post_where` and `next_post_where` methods.
284 - *
285 - * @param mixed $adjacent
286 - * @param mixed $where
287 - * @param mixed $post
288 - * @param mixed $taxonomy
289 - * @return mixed
290 - */
291 - private function get_adjacent_post_id( $adjacent, $where, $post, $taxonomy ) {
292 - if ( 'doc_category' !== $taxonomy ) {
293 - return $where;
294 - }
290 + $_docs_order = $this->get_docs_order_by_terms( $_terms[0]->term_id );
295 291
296 - $_id = null;
297 - $_terms = get_the_terms( $post->ID, 'doc_category' );
298 - if ( empty( $_terms ) ) {
299 - return $where;
300 - }
292 + $_where = explode( 'AND', $where );
293 + if ( is_array( $_where ) ) {
294 + // $_where[0] = str_replace( 'WHERE ', '', $_where[0] );
295 + unset( $_where[0] );
296 + $_where = implode( 'AND', $_where );
297 + }
301 298
302 - global $wp_query, $wpdb;
299 + $_orderby = $this->settings->get( 'alphabetically_order_post', 'betterdocs_order' );
300 + $_order = $this->settings->get( 'docs_order', 'ASC' );
301 + $_docs_order = $_orderby === 'betterdocs_order' ? $_docs_order : [];
303 302
304 - $_docs_order = $this->get_docs_order_by_terms( $_terms[ 0 ]->term_id );
303 + if ( empty( $_docs_order ) ) {
304 + $statuses = [ 'publish' ];
305 305
306 - $_orderby = $this->settings->get( 'alphabetically_order_post', 'betterdocs_order' );
307 - $_order = $this->settings->get( 'docs_order', 'ASC' );
308 - $_docs_order = 'betterdocs_order' === $_orderby ? $_docs_order : array();
306 + if ( current_user_can( 'read_private_docs' ) ) {
307 + $statuses[] = 'private';
308 + }
309 309
310 - if ( empty( $_docs_order ) ) {
311 - $statuses = array( 'publish' );
310 + $_args = [
311 + 'post_status' => $statuses
312 + ];
313 + if ( isset( $wp_query->query_vars['doc_category'] ) ) {
314 + $_args['tax_query'][] = [
315 + 'taxonomy' => 'doc_category',
316 + 'field' => 'slug',
317 + 'terms' => $wp_query->query_vars['doc_category'],
318 + 'operator' => 'AND',
319 + 'include_children' => false
320 + ];
321 + } else if( isset( $wp_query->query_vars['name'] ) && isset( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] == 'docs' ) {
322 + $post = get_page_by_path($wp_query->query_vars['name'] , OBJECT, 'docs');
323 + $doc_terms = [];
312 324
313 - if ( current_user_can( 'read_private_docs' ) ) {
314 - $statuses[ ] = 'private';
315 - }
325 + if( isset( $post->ID ) ) {
326 + $terms = get_the_terms($post->ID, 'doc_category');
327 + if( ! empty( $terms ) ) {
328 + $doc_terms = wp_list_pluck( $terms, 'term_id' );
329 + }
330 + }
316 331
317 - $_args = array(
318 - 'post_status' => $statuses,
319 - 'term_id' => $_terms[ 0 ]->term_id
320 - );
321 - if ( isset( $wp_query->query_vars[ 'doc_category' ] ) ) {
322 - $_args[ 'tax_query' ][ ] = array(
323 - 'taxonomy' => 'doc_category',
324 - 'field' => 'slug',
325 - 'terms' => $wp_query->query_vars[ 'doc_category' ],
326 - 'operator' => 'AND',
327 - 'include_children' => false
328 - );
329 - } elseif ( isset( $wp_query->query_vars[ 'name' ] ) && isset( $wp_query->query_vars[ 'post_type' ] ) && 'docs' == $wp_query->query_vars[ 'post_type' ] ) {
330 - $_post = get_page_by_path( $wp_query->query_vars[ 'name' ], OBJECT, 'docs' );
331 - $doc_terms = array();
332 + if( ! empty( $doc_terms ) ) {
333 + $_args['tax_query'][] = [
334 + 'taxonomy' => 'doc_category',
335 + 'field' => 'term_id',
336 + 'terms' => $doc_terms,
337 + 'operator' => 'AND',
338 + 'include_children' => false
339 + ];
340 + }
341 + }
332 342
333 - if ( isset( $_post->ID ) ) {
334 - $terms = get_the_terms( $_post->ID, 'doc_category' );
335 - if ( ! empty( $terms ) ) {
336 - $doc_terms = wp_list_pluck( $terms, 'term_id' );
337 - }
338 - }
343 + $_args['orderby'] = $_orderby;
344 + if ( $_orderby != 'betterdocs_order' ) {
345 + $_args['order'] = $_order;
346 + }
339 347
340 - if ( ! empty( $doc_terms ) ) {
341 - $_args[ 'tax_query' ][ ] = array(
342 - 'taxonomy' => 'doc_category',
343 - 'field' => 'term_id',
344 - 'terms' => $doc_terms,
345 - 'operator' => 'AND',
346 - 'include_children' => false
347 - );
348 - $_args[ 'term_id' ] = $doc_terms[ 0 ];
349 - }
350 - } else {
351 - // Fallback: use the term from current post
352 - $_args[ 'tax_query' ][ ] = array(
353 - 'taxonomy' => 'doc_category',
354 - 'field' => 'term_id',
355 - 'terms' => $_terms[ 0 ]->term_id,
356 - 'operator' => 'AND',
357 - 'include_children' => false
358 - );
359 - }
348 + /**
349 + * Before Query
350 + */
351 + do_action_ref_array( 'betterdocs_navigation_docs_query', [ &$_args ] );
352 + $docs = $this->get_posts( $this->docs_query_args( $_args ) );
360 353
361 - $_args[ 'orderby' ] = $_orderby;
362 - if ( 'betterdocs_order' != $_orderby ) {
363 - $_args[ 'order' ] = $_order;
364 - }
354 + $_docs = [];
355 + if ( $docs->have_posts() ) {
356 + array_map(
357 + function ( $post ) use ( &$_docs ) {
358 + $_docs[] = $post->ID;
359 + },
360 + $docs->posts
361 + );
362 + }
365 363
366 - /**
367 - * Before Query
368 - */
369 - do_action_ref_array( 'betterdocs_navigation_docs_query', array( &$_args ) );
370 - $docs = $this->get_posts( $this->docs_query_args( $_args ) );
364 + $_docs_order = $_docs;
365 + }
371 366
372 - $_docs = array();
373 - if ( $docs->have_posts() ) {
374 - array_map(
375 - function ( $_post ) use ( &$_docs ) {
376 - $_docs[ ] = $_post->ID;
377 - },
378 - $docs->posts
379 - );
380 - }
367 + $_docs_order = apply_filters( 'betterdocs_adjacent_docs_order', $_docs_order, $_terms );
381 368
382 - $_docs_order = $_docs;
383 - }
369 + $_id_index = array_search( $post->ID, $_docs_order );
370 + $_id_index = $adjacent === 'next' ? $_id_index + 1 : $_id_index - 1;
371 + $_id = isset( $_docs_order[ $_id_index ] ) ? (int) $_docs_order[ $_id_index ] : null;
384 372
385 - $_docs_order = apply_filters( 'betterdocs_adjacent_docs_order', $_docs_order, $_terms );
373 + return $wpdb->prepare( "WHERE p.ID = %s AND $_where", (int) $_id );
374 + }
386 375
387 - // Ensure consistent type comparison by converting all IDs to integers
388 - $_docs_order = array_map( 'intval', $_docs_order );
389 - $_docs_order = array_values( $_docs_order ); // Re-index array after type conversion
376 + public function parse_terms_args( $args = [] ) {
377 + $_default_args = [
378 + 'hide_empty' => true,
379 + 'taxonomy' => 'doc_category'
380 + ];
390 381
391 - $_id_index = array_search( (int) $post->ID, $_docs_order, true );
382 + // OrderBy & Order
383 + $_orderby = ! empty( $args['orderby'] ) && $args['orderby'] != 1 ? $args['orderby'] : 'name';
384 + $_order = ! empty( $args['order'] ) ? $args['order'] : '';
392 385
393 - // If current post not found in order list, return original where clause
394 - if ( false === $_id_index ) {
395 - return $where;
396 - }
386 + if ( $_orderby == 'betterdocs_order' ) {
387 + $args['meta_key'] = 'doc_category_order';
388 + $args['orderby'] = 'meta_value_num';
389 + $args['order'] = 'ASC';
390 + } else {
391 + $args['orderby'] = $_orderby;
392 + $args['order'] = $_order;
393 + }
397 394
398 - $_id_index = 'next' === $adjacent ? $_id_index + 1 : $_id_index - 1;
399 - $_id = isset( $_docs_order[ $_id_index ] ) ? (int) $_docs_order[ $_id_index ] : null;
395 + // Nested Sub Category
396 + // $args['parent'] = 0;
397 + // if ( $nested_subcategory == true ) {
398 + // }
400 399
401 - // Fix: replace only ID comparison in where clause using regex
402 - if ( $_id ) {
403 - // This replaces any 'p.ID < N', 'p.ID > N', 'p.ID <= N', or 'p.ID >= N' etc, with 'p.ID = $_id'
404 - $where = preg_replace( '/p\.ID\s*[<>!=]+\s*\d+/', 'p.ID = ' . (int) $_id, $where );
405 - }
400 + if ( ! isset( $args['number'] ) ) {
401 + global $wp_query;
402 + if ( $wp_query->query === null || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
403 + $args['number'] = 4;
404 + }
405 + }
406 406
407 - return $where;
408 - }
407 + // Includes
408 + if ( isset( $args['include'] ) ) {
409 + $_include = ! is_array( $args['include'] ) ? explode( ',', $args['include'] ) : $args['include'];
410 + $args['include'] = $_include;
411 + $args['orderby'] = 'include';
409 412
410 - public function parse_terms_args( $args = array() ) {
411 - $_default_args = array(
412 - 'hide_empty' => true,
413 - 'taxonomy' => 'doc_category'
414 - );
413 + unset( $args['parent'] );
414 + }
415 415
416 - // OrderBy & Order
417 - $_orderby = ! empty( $args[ 'orderby' ] ) && 1 != $args[ 'orderby' ] ? $args[ 'orderby' ] : 'name';
418 - $_order = ! empty( $args[ 'order' ] ) ? $args[ 'order' ] : '';
416 + $_meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
417 + $args['meta_query'] = apply_filters( 'betterdocs_taxonomy_object_meta_query', $_meta_query, $args );
419 418
420 - if ( 'betterdocs_order' == $_orderby ) {
421 - // Use base meta key - fallback logic will be handled in the terms_clauses filter
422 - $args[ 'meta_key' ] = 'doc_category_order';
423 - $args[ 'orderby' ] = 'meta_value_num';
424 - $args[ 'order' ] = 'ASC';
425 - } else {
426 - $args[ 'orderby' ] = $_orderby;
427 - $args[ 'order' ] = $_order;
428 - }
419 + return apply_filters( 'betterdocs_category_terms_object', wp_parse_args( $args, $_default_args ), $args );
420 + }
429 421
430 - // Nested Sub Category
431 - // $args['parent'] = 0;
432 - // if ( $nested_subcategory == true ) {
433 - // }
422 + public function get_terms( $args ) {
423 + $parsed_args = $this->parse_terms_args( $args );
424 + $terms = get_terms( $parsed_args );
434 425
435 - if ( ! isset( $args[ 'number' ] ) ) {
436 - global $wp_query;
437 - if ( null === $wp_query->query || ( isset( $wp_query->query[ 'post_type' ] ) && 'docs' != $wp_query->query[ 'post_type' ] ) ) {
438 - $args[ 'number' ] = 4;
439 - }
440 - }
426 + // Filter terms manually if we need to consider private docs for users with read_private_docs capability
427 + if ( isset( $parsed_args['_betterdocs_filter_private'] ) && $parsed_args['_betterdocs_filter_private'] && current_user_can( 'read_private_docs' ) ) {
428 + $terms = array_filter( $terms, function( $term ) {
429 + // Get the actual count including private docs for users with read_private_docs capability
430 + $actual_count = $this->get_docs_count( $term, false );
431 + return $actual_count > 0;
432 + });
433 + }
441 434
442 - // Includes
443 - if ( isset( $args[ 'include' ] ) ) {
444 - $_include = ! is_array( $args[ 'include' ] ) ? explode( ',', $args[ 'include' ] ) : $args[ 'include' ];
445 - $args[ 'include' ] = $_include;
446 - $args[ 'orderby' ] = 'include';
435 + return $terms;
436 + }
447 437
448 - unset( $args[ 'parent' ] );
449 - }
438 + public function get_child_terms( $args ) {
439 + if ( ! isset( $args['number'] ) ) {
440 + global $wp_query;
441 + if ( $wp_query->query === null || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
442 + $args['number'] = 1;
443 + }
444 + }
450 445
451 - $_meta_query = ! empty( $args[ 'meta_query' ] ) ? $args[ 'meta_query' ] : array();
452 - $args[ 'meta_query' ] = apply_filters( 'betterdocs_taxonomy_object_meta_query', $_meta_query, $args );
446 + return $this->get_terms( $args );
447 + }
453 448
454 - return apply_filters( 'betterdocs_category_terms_object', wp_parse_args( $args, $_default_args ), $args );
455 - }
449 + /**
450 + * Get POSTs of Docs type.
451 + *
452 + * @param mixed $args
453 + * @return WP_Query
454 + */
455 + public function get_posts( $args, $ignore = false ) {
456 + if ( ! $ignore ) {
457 + $args = $this->docs_query_args( $args );
458 + }
456 459
457 - public function get_terms( $args ) {
458 - $parsed_args = $this->parse_terms_args( $args );
459 - $terms = get_terms( $parsed_args );
460 + return new WP_Query( $args );
461 + }
460 462
461 - // Filter terms manually if we need to consider private docs for users with read_private_docs capability
462 - if ( isset( $parsed_args[ '_betterdocs_filter_private' ] ) && $parsed_args[ '_betterdocs_filter_private' ] && current_user_can( 'read_private_docs' ) ) {
463 - $terms = array_filter( $terms, function ( $term ) {
464 - // Get the actual count including private docs for users with read_private_docs capability
465 - $actual_count = $this->get_docs_count( $term, false );
466 - return $actual_count > 0;
467 - } );
468 - }
463 + public function get_taxonomy( $tax = '' ) {
464 + global $wp_query;
465 + if ( is_tax( 'knowledge_base' ) ) {
466 + $_tax = $wp_query->tax_query->queried_terms;
467 + if ( array_key_exists( 'doc_category', $_tax ) ) {
468 + $tax = 'doc_category';
469 + } else {
470 + $tax = 'knowledge_base';
471 + }
472 + } elseif ( is_tax( 'doc_category' ) ) {
473 + $tax = 'doc_category';
474 + }
469 475
470 - return $terms;
471 - }
476 + return $tax;
477 + }
472 478
473 - public function get_child_terms( $args ) {
474 - if ( ! isset( $args[ 'number' ] ) ) {
475 - global $wp_query;
476 - if ( null === $wp_query->query || ( isset( $wp_query->query[ 'post_type' ] ) && 'docs' != $wp_query->query[ 'post_type' ] ) ) {
477 - $args[ 'number' ] = 1;
478 - }
479 - }
479 + public function terms_query( $args = [] ) {
480 + global $wp_query;
481 + $_origin_args = $args;
480 482
481 - return $this->get_terms( $args );
482 - }
483 + $default_args = [
484 + 'hide_empty' => true,
485 + 'taxonomy' => 'doc_category',
486 + 'orderby' => 'name'
487 + ];
483 488
484 - /**
485 - * Get POSTs of Docs type.
486 - *
487 - * @param mixed $args
488 - * @return WP_Query
489 - */
490 - public function get_posts( $args, $ignore = false ) {
491 - if ( ! $ignore ) {
492 - $args = $this->docs_query_args( $args );
493 - }
489 + /**
490 + * Number Set
491 + *
492 + * @FIX: If Built-in Docs page off and docs_page in use, then Terms Query Number 4|1 set.
493 + */
494 + // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
495 + // $default_args['number'] = 1;
496 + // }
494 497
495 - return new WP_Query( $args );
496 - }
498 + /**
499 + * Nested Sub Category
500 + */
501 + if ( isset( $args['nested_subcategory'] ) && $args['nested_subcategory'] == true ) {
502 + $default_args['parent'] = 0;
503 + unset( $args['nested_subcategory'] );
504 + // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
505 + // $default_args['number'] = 4;
506 + // }
507 + }
497 508
498 - public function get_taxonomy( $tax = '' ) {
499 - global $wp_query;
500 - if ( is_tax( 'knowledge_base' ) ) {
501 - $_tax = $wp_query->tax_query->queried_terms;
502 - if ( array_key_exists( 'doc_category', $_tax ) ) {
503 - $tax = 'doc_category';
504 - } else {
505 - $tax = 'knowledge_base';
506 - }
507 - } elseif ( is_tax( 'doc_category' ) ) {
508 - $tax = 'doc_category';
509 - }
509 + /**
510 + * OrderBy and Order
511 + */
512 + if ( ! isset( $args['orderby'] ) ) {
513 + $_orderby = $this->settings->get( 'terms_orderby', 'name' );
514 + $_order = $this->settings->get( 'terms_order', '' );
515 + } else {
516 + $_orderby = $args['orderby'];
517 + $_order = ! empty( $args['order'] ) ? $args['order'] : '';
518 + }
510 519
511 - return $tax;
512 - }
520 + if ( 'betterdocs_order' === $_orderby ) {
521 + // Use different meta keys for different taxonomies
522 + if ( isset( $args['taxonomy'] ) && $args['taxonomy'] === 'knowledge_base' ) {
523 + $default_args['meta_key'] = 'kb_order';
524 + } else {
525 + $default_args['meta_key'] = 'doc_category_order';
526 + }
527 + $_orderby = 'meta_value_num';
528 + $_order = 'ASC';
529 + } elseif ( $_orderby === true ) {
530 + $_orderby = 'name';
531 + }
513 532
514 - public function terms_query( $args = array() ) {
515 - global $wp_query;
516 - $_origin_args = $args;
533 + $args['orderby'] = $_orderby;
534 + if ( ! empty( $_order ) ) {
535 + $args['order'] = $_order;
536 + }
517 537
518 - $default_args = array(
519 - 'hide_empty' => true,
520 - 'taxonomy' => 'doc_category',
521 - 'orderby' => 'name'
522 - );
538 + /**
539 + * @todo old hook
540 + * hook: betterdocs_child_taxonomy_meta_query
541 + */
542 + $_multiple_kb = apply_filters(
543 + 'betterdocs_query_args_multiple_kb_enabled',
544 + isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
545 + $_origin_args
546 + );
523 547
524 - /**
525 - * Number Set
526 - *
527 - * @FIX: If Built-in Docs page off and docs_page in use, then Terms Query Number 4|1 set.
528 - */
529 - // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
530 - // $default_args['number'] = 1;
531 - // }
548 + $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
532 549
533 - /**
534 - * Nested Sub Category
535 - */
536 - if ( isset( $args[ 'nested_subcategory' ] ) && true == $args[ 'nested_subcategory' ] ) {
537 - $default_args[ 'parent' ] = 0;
538 - unset( $args[ 'nested_subcategory' ] );
539 - // if ( $wp_query->query === NULL || ( isset( $wp_query->query['post_type'] ) && $wp_query->query['post_type'] != 'docs' ) ) {
540 - // $default_args['number'] = 4;
541 - // }
542 - }
550 + unset( $args['multiple_kb'] );
551 + unset( $args['kb_slug'] );
543 552
544 - /**
545 - * OrderBy and Order
546 - */
547 - if ( ! isset( $args[ 'orderby' ] ) ) {
548 - $_orderby = $this->settings->get( 'terms_orderby', 'name' );
549 - $_order = $this->settings->get( 'terms_order', '' );
550 - } else {
551 - $_orderby = $args[ 'orderby' ];
552 - $_order = ! empty( $args[ 'order' ] ) ? $args[ 'order' ] : '';
553 - }
553 + $meta_query = ! empty( $args['meta_query'] ) ? $args['meta_query'] : [];
554 + $meta_query = apply_filters( 'betterdocs_terms_meta_query_args', $meta_query, $_multiple_kb, $_kb_slug, $_origin_args );
554 555
555 - if ( 'betterdocs_order' === $_orderby ) {
556 - // Use different meta keys for different taxonomies
557 - if ( isset( $args[ 'taxonomy' ] ) && 'knowledge_base' === $args[ 'taxonomy' ] ) {
558 - $default_args[ 'meta_key' ] = 'kb_order';
559 - } else {
560 - $default_args[ 'meta_key' ] = 'doc_category_order';
561 - }
562 - $_orderby = 'meta_value_num';
563 - $_order = 'ASC';
564 - } elseif ( true === $_orderby ) {
565 - $_orderby = 'name';
566 - }
556 + if ( ! empty( $meta_query ) ) {
557 + $default_args['meta_query'] = $meta_query;
558 + }
567 559
568 - $args[ 'orderby' ] = $_orderby;
569 - if ( ! empty( $_order ) ) {
570 - $args[ 'order' ] = $_order;
571 - }
560 + if ( ! empty( $args['terms'] ) ) {
561 + $args['include'] = explode( ',', $args['terms'] );
562 + $args['orderby'] = 'include';
563 + $args['order'] = 'ASC';
572 564
573 - /**
574 - * @todo old hook
575 - * hook: betterdocs_child_taxonomy_meta_query
576 - */
577 - $_multiple_kb = apply_filters(
578 - 'betterdocs_query_args_multiple_kb_enabled',
579 - isset( $args[ 'multiple_kb' ] ) ? (bool) $args[ 'multiple_kb' ] : false,
580 - $_origin_args
581 - );
565 + unset( $default_args['parent'] );
566 + unset( $args['parent'] );
567 + unset( $args['terms'] );
568 + }
582 569
583 - $_kb_slug = isset( $args[ 'kb_slug' ] ) ? trim( $args[ 'kb_slug' ] ) : '';
570 + $_query_args = wp_parse_args( $args, $default_args );
571 + $_query_args = apply_filters( 'betterdocs_terms_query_args', $_query_args, $_origin_args );
584 572
585 - unset( $args[ 'multiple_kb' ] );
586 - unset( $args[ 'kb_slug' ] );
573 + // Apply private docs logic for logged-in users
574 + $_query_args = $this->modify_terms_args_for_private_docs( $_query_args );
587 575
588 - $meta_query = ! empty( $args[ 'meta_query' ] ) ? $args[ 'meta_query' ] : array();
589 - $meta_query = apply_filters( 'betterdocs_terms_meta_query_args', $meta_query, $_multiple_kb, $_kb_slug, $_origin_args );
576 + return $_query_args;
577 + }
590 578
591 - if ( ! empty( $meta_query ) ) {
592 - $default_args[ 'meta_query' ] = $meta_query;
593 - }
579 + public function get_term_parents( $term_id, $taxonomy = 'doc_category', $args = [] ) {
580 + $term = get_term( $term_id, $taxonomy );
581 + if ( is_wp_error( $term ) ) {
582 + return $term;
583 + }
594 584
595 - if ( ! empty( $args[ 'terms' ] ) ) {
596 - $args[ 'include' ] = explode( ',', $args[ 'terms' ] );
597 - $args[ 'orderby' ] = 'include';
598 - $args[ 'order' ] = 'ASC';
585 + if ( ! $term || ! isset( $term->term_id ) ) {
586 + return [];
587 + }
599 588
600 - unset( $default_args[ 'parent' ] );
601 - unset( $args[ 'parent' ] );
602 - unset( $args[ 'terms' ] );
603 - }
589 + $_lists = [];
590 + $origin_term = $term_id;
591 + $term_id = $term->term_id;
604 592
605 - $_query_args = wp_parse_args( $args, $default_args );
606 - $_query_args = apply_filters( 'betterdocs_terms_query_args', $_query_args, $_origin_args );
593 + $defaults = [
594 + 'format' => 'name',
595 + 'inclusive' => true
596 + ];
607 597
608 - // Apply private docs logic for logged-in users
609 - $_query_args = $this->modify_terms_args_for_private_docs( $_query_args );
598 + $args = wp_parse_args( $args, $defaults );
610 599
611 - return $_query_args;
612 - }
600 + $args['inclusive'] = wp_validate_boolean( $args['inclusive'] );
613 601
614 - public function get_term_parents( $term_id, $taxonomy = 'doc_category', $args = array() ) {
615 - $term = get_term( $term_id, $taxonomy );
616 - if ( is_wp_error( $term ) ) {
617 - return $term;
618 - }
602 + $parents = get_ancestors( $term_id, $taxonomy );
619 603
620 - if ( ! $term || ! isset( $term->term_id ) ) {
621 - return array();
622 - }
604 + if ( $args['inclusive'] ) {
605 + array_unshift( $parents, $term_id );
606 + }
623 607
624 - $_lists = array();
625 - $origin_term = $term_id;
626 - $term_id = $term->term_id;
608 + foreach ( array_reverse( $parents ) as $term_id ) {
609 + $parent = get_term( $term_id, $taxonomy );
610 + $name = ( 'slug' === $args['format'] ) ? $parent->slug : $parent->name;
611 + $term_permalink = get_term_link( $parent->term_id, $taxonomy );
612 + $term_permalink = apply_filters( 'betterdocs_breadcrumb_term_permalink', $term_permalink, $term_id );
627 613
628 - $defaults = array(
629 - 'format' => 'name',
630 - 'inclusive' => true
631 - );
614 + $_item = [
615 + 'url' => $term_permalink,
616 + 'text' => $name
617 + ];
632 618
633 - $args = wp_parse_args( $args, $defaults );
619 + $_lists[] = $_item;
620 + }
634 621
635 - $args[ 'inclusive' ] = wp_validate_boolean( $args[ 'inclusive' ] );
622 + return apply_filters( 'betterdocs_breadcrumb_archive_lists', $_lists, $origin_term );
623 + }
636 624
637 - $parents = get_ancestors( $term_id, $taxonomy );
625 + /**
626 + * Get all non-empty child term IDs recursively for a given taxonomy and parent term.
627 + *
628 + * This function retrieves all child terms for a specified taxonomy and parent term,
629 + * recursively fetching child terms of child terms, and returns only those with a non-zero post count.
630 + *
631 + * @param string $taxonomy The taxonomy name (e.g., 'doc_category').
632 + * @param int $parent_id The ID of the parent term to start retrieving children from.
633 + *
634 + * @return array An array of non-empty child term IDs.
635 + */
636 + public function get_all_child_term_ids( $taxonomy, $parent_id ) {
637 + // Set up the arguments for retrieving child terms
638 + $args = apply_filters(
639 + 'betterdocs_get_child_term_ids_args',
640 + [
641 + 'taxonomy' => $taxonomy,
642 + 'parent' => $parent_id,
643 + 'hide_empty' => true,
644 + 'fields' => 'ids'
645 + ]
646 + );
638 647
639 - if ( $args[ 'inclusive' ] ) {
640 - array_unshift( $parents, $term_id );
641 - }
648 + // Get the terms based on the arguments
649 + $terms = get_terms( $args );
642 650
643 - foreach ( array_reverse( $parents ) as $term_id ) {
644 - $parent = get_term( $term_id, $taxonomy );
645 - $name = ( 'slug' === $args[ 'format' ] ) ? $parent->slug : $parent->name;
646 - $term_permalink = get_term_link( $parent->term_id, $taxonomy );
647 - $term_permalink = apply_filters( 'betterdocs_breadcrumb_term_permalink', $term_permalink, $term_id );
651 + // Initialize an empty array to hold non-empty child term IDs
652 + $non_empty_children = [];
648 653
649 - $_item = array(
650 - 'url' => $term_permalink,
651 - 'text' => $name
652 - );
654 + // Check if terms were retrieved without errors and that the result isn't empty
655 + if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
656 + $term_ids = [];
653 657
654 - $_lists[ ] = $_item;
655 - }
658 + // Loop through each term ID
659 + foreach ( $terms as $term_id ) {
660 + // Add the current term ID to the term_ids array
661 + $term_ids[] = $term_id;
656 662
657 - return apply_filters( 'betterdocs_breadcrumb_archive_lists', $_lists, $origin_term );
658 - }
663 + // Recursively get child terms for the current term
664 + $child_term_ids = $this->get_all_child_term_ids( $taxonomy, $term_id );
659 665
660 - /**
661 - * Get all non-empty child term IDs recursively for a given taxonomy and parent term.
662 - *
663 - * This function retrieves all child terms for a specified taxonomy and parent term,
664 - * recursively fetching child terms of child terms, and returns only those with a non-zero post count.
665 - *
666 - * @param string $taxonomy The taxonomy name (e.g., 'doc_category').
667 - * @param int $parent_id The ID of the parent term to start retrieving children from.
668 - *
669 - * @return array An array of non-empty child term IDs.
670 - */
671 - public function get_all_child_term_ids( $taxonomy, $parent_id ) {
672 - // Set up the arguments for retrieving child terms
673 - $args = apply_filters(
674 - 'betterdocs_get_child_term_ids_args',
675 - array(
676 - 'taxonomy' => $taxonomy,
677 - 'parent' => $parent_id,
678 - 'hide_empty' => true,
679 - 'fields' => 'ids'
680 - )
681 - );
666 + // If there are child terms, merge them into the term_ids array
667 + if ( ! empty( $child_term_ids ) ) {
668 + $term_ids = array_merge( $term_ids, $child_term_ids );
669 + }
670 + }
682 671
683 - // Get the terms based on the arguments
684 - $terms = get_terms( $args );
672 + // Loop through all retrieved term IDs to filter out empty ones
673 + foreach ( $term_ids as $term_id ) {
674 + // Get the term object for the current term ID
675 + $child_term = get_term( $term_id, $taxonomy );
685 676
686 - // Initialize an empty array to hold non-empty child term IDs
687 - $non_empty_children = array();
677 + // Only include terms that have a non-zero post count
678 + if ( $child_term && isset( $child_term->term_id ) && $child_term->count > 0 ) {
679 + $non_empty_children[] = $child_term->term_id;
680 + }
681 + }
682 + }
688 683
689 - // Check if terms were retrieved without errors and that the result isn't empty
690 - if ( ! is_wp_error( $terms ) && ! empty( $terms ) ) {
691 - $term_ids = array();
684 + // Return the final array of non-empty child term IDs
685 + return $non_empty_children;
686 + }
692 687
693 - // Loop through each term ID
694 - foreach ( $terms as $term_id ) {
695 - // Add the current term ID to the term_ids array
696 - $term_ids[ ] = $term_id;
688 + /**
689 + * Get all nested child term IDs of a specific parent term in a taxonomy and return as a comma-separated string.
690 + *
691 + * @param string $taxonomy The taxonomy.
692 + * @param int $parent_id The parent term ID.
693 + * @return string The comma-separated term IDs.
694 + */
695 + public function get_child_term_ids_by_parent_id( $taxonomy, $parent_id ) {
696 + $term_ids = $this->get_all_child_term_ids( $taxonomy, $parent_id );
697 + return implode( ',', $term_ids );
698 + }
697 699
698 - // Recursively get child terms for the current term
699 - $child_term_ids = $this->get_all_child_term_ids( $taxonomy, $term_id );
700 + public function get_terms_children( $taxonomy, $parent_id ) {
701 + $children = get_term_children( $parent_id, $taxonomy );
702 + $non_empty_children = [];
703 + if ( ! is_wp_error( $children ) && ! empty( $children ) ) {
704 + foreach ( $children as $child_id ) {
705 + $child_term = get_term( $child_id, $taxonomy );
700 706
701 - // If there are child terms, merge them into the term_ids array
702 - if ( ! empty( $child_term_ids ) ) {
703 - $term_ids = array_merge( $term_ids, $child_term_ids );
704 - }
705 - }
707 + // Only include non-empty terms
708 + if ( $child_term && $child_term->count > 0 ) {
709 + $non_empty_children[] = $child_term;
710 + }
711 + }
712 + }
706 713
707 - // Loop through all retrieved term IDs to filter out empty ones
708 - foreach ( $term_ids as $term_id ) {
709 - // Get the term object for the current term ID
710 - $child_term = get_term( $term_id, $taxonomy );
714 + return $non_empty_children;
715 + }
711 716
712 - // Only include terms that have a non-zero post count
713 - if ( $child_term && isset( $child_term->term_id ) && $child_term->count > 0 ) {
714 - $non_empty_children[ ] = $child_term->term_id;
715 - }
716 - }
717 - }
717 + public function get_terms_children_ids( $taxonomy, $parent_id ) {
718 + $children = $this->get_terms_children( $taxonomy, $parent_id );
719 + $term_ids = wp_list_pluck( $children, 'term_id' );
718 720
719 - // Return the final array of non-empty child term IDs
720 - return $non_empty_children;
721 - }
721 + return implode( ',', $term_ids );
722 + }
722 723
723 - /**
724 - * Get all nested child term IDs of a specific parent term in a taxonomy and return as a comma-separated string.
725 - *
726 - * @param string $taxonomy The taxonomy.
727 - * @param int $parent_id The parent term ID.
728 - * @return string The comma-separated term IDs.
729 - */
730 - public function get_child_term_ids_by_parent_id( $taxonomy, $parent_id ) {
731 - $term_ids = $this->get_all_child_term_ids( $taxonomy, $parent_id );
732 - return implode( ',', $term_ids );
733 - }
724 + public function count_terms_children( $taxonomy, $parent_id ) {
725 + return count( $this->get_terms_children( $taxonomy, $parent_id ) );
726 + }
734 727
735 - public function get_terms_children( $taxonomy, $parent_id ) {
736 - $children = get_term_children( $parent_id, $taxonomy );
737 - $non_empty_children = array();
738 - if ( ! is_wp_error( $children ) && ! empty( $children ) ) {
739 - foreach ( $children as $child_id ) {
740 - $child_term = get_term( $child_id, $taxonomy );
728 + public function is_inner_templates() {
729 + if ( is_tax( 'knowledge_base' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
730 + return true;
731 + }
732 + return false;
733 + }
741 734
742 - // Only include non-empty terms
743 - if ( $child_term && $child_term->count > 0 ) {
744 - $non_empty_children[ ] = $child_term;
745 - }
746 - }
747 - }
735 + /**
736 + * Summary of docs_query_args
737 + * @param mixed $args
738 + * @throws \Exception
739 + * @return mixed
740 + */
741 + public function docs_query_args( $args, $filter = [] ) {
742 + $_origin_args = $args;
748 743
749 - return $non_empty_children;
750 - }
744 + $default_args = [
745 + 'post_type' => 'docs'
746 + ];
751 747
752 - public function get_terms_children_ids( $taxonomy, $parent_id ) {
753 - $children = $this->get_terms_children( $taxonomy, $parent_id );
754 - $term_ids = wp_list_pluck( $children, 'term_id' );
748 + if ( ! empty( $args['post_type'] ) && trim( $args['post_type'] ) === 'docs_any' ) {
749 + $default_args['post_type'] = 'docs';
750 + $default_args['post_status'] = 'any';
755 751
756 - return implode( ',', $term_ids );
757 - }
752 + unset( $args['post_type'] );
753 + }
758 754
759 - public function count_terms_children( $taxonomy, $parent_id ) {
760 - return count( $this->get_terms_children( $taxonomy, $parent_id ) );
761 - }
755 + /**
756 + * OrderBy and Order
757 + */
758 + if ( ! isset( $args['orderby'] ) ) {
759 + $_orderby = $this->settings->get( 'alphabetically_order_post' );
760 + $_order = $this->settings->get( 'docs_order', 'ASC' );
761 + } else {
762 + $_orderby = $args['orderby'];
763 + $_order = ! empty( $args['order'] ) ? $args['order'] : 'ASC';
764 + }
762 765
763 - public function is_inner_templates() {
764 - if ( is_tax( 'knowledge_base' ) || is_tax( 'doc_category' ) || is_tax( 'doc_tag' ) || is_singular( 'docs' ) ) {
765 - return true;
766 - }
767 - return false;
768 - }
766 + if ( 'betterdocs_order' != $_orderby ) {
767 + if ( $_orderby === true ) {
768 + $args['orderby'] = 'title';
769 + } else {
770 + $args['orderby'] = $_orderby;
771 + }
769 772
770 - /**
771 - * Summary of docs_query_args
772 - * @param mixed $args
773 - * @throws \Exception
774 - * @return mixed
775 - */
776 - public function docs_query_args( $args, $filter = array() ) {
777 - $_origin_args = $args;
773 + $args['order'] = $_order;
774 + } elseif ( 'betterdocs_order' == $_orderby ) {
775 + unset( $args['orderby'] );
776 + }
778 777
779 - $default_args = array(
780 - 'post_type' => 'docs'
781 - );
778 + if ( empty( $args['orderby'] ) ) {
779 + unset( $args['order'] );
780 + }
782 781
783 - if ( ! empty( $args[ 'post_type' ] ) && trim( $args[ 'post_type' ] ) === 'docs_any' ) {
784 - $default_args[ 'post_type' ] = 'docs';
785 - $default_args[ 'post_status' ] = 'any';
782 + /**
783 + * Term ID
784 + */
785 + $_term_id = null;
786 + if ( ! empty( $args['term_id'] ) ) {
787 + $_term_id = intval( $args['term_id'] );
788 + unset( $args['term_id'] );
789 + }
786 790
787 - unset( $args[ 'post_type' ] );
788 - }
791 + // if ( $_term_id == null ) {
792 + // throw new \Exception( __( '$args["term_id"] cannot be null.', 'betterdocs' ) );
793 + // }
789 794
790 - /**
791 - * OrderBy and Order
792 - */
793 - if ( ! isset( $args[ 'orderby' ] ) ) {
794 - $_orderby = $this->settings->get( 'alphabetically_order_post' );
795 - $_order = $this->settings->get( 'docs_order', 'ASC' );
796 - } else {
797 - $_orderby = $args[ 'orderby' ];
798 - $_order = ! empty( $args[ 'order' ] ) ? $args[ 'order' ] : 'ASC';
799 - }
795 + /**
796 + * Term Slug for tax_query
797 + */
798 + $_term_slug = '';
799 + if ( ! empty( $args['term_slug'] ) ) {
800 + $_term_slug = trim( $args['term_slug'] );
801 + unset( $args['term_slug'] );
802 + }
800 803
801 - if ( 'betterdocs_order' != $_orderby ) {
802 - if ( true === $_orderby ) {
803 - $args[ 'orderby' ] = 'title';
804 - } else {
805 - $args[ 'orderby' ] = $_orderby;
806 - }
804 + /**
805 + * @todo old hook
806 + * hook: betterdocs_cat_template_multikb
807 + */
807 808
808 - $args[ 'order' ] = $_order;
809 - } elseif ( 'betterdocs_order' == $_orderby ) {
810 - unset( $args[ 'orderby' ] );
811 - }
809 + $_multiple_kb = apply_filters(
810 + 'betterdocs_enable_multiple_knowledge_base',
811 + isset( $args['multiple_kb'] ) ? (bool) $args['multiple_kb'] : false,
812 + $_origin_args
813 + );
812 814
813 - if ( empty( $args[ 'orderby' ] ) ) {
814 - unset( $args[ 'order' ] );
815 - }
815 + $_kb_slug = isset( $args['kb_slug'] ) ? trim( $args['kb_slug'] ) : '';
816 816
817 - /**
818 - * Term ID
819 - */
820 - $_term_id = null;
821 - if ( ! empty( $args[ 'term_id' ] ) ) {
822 - $_term_id = intval( $args[ 'term_id' ] );
823 - unset( $args[ 'term_id' ] );
824 - }
817 + unset( $args['multiple_kb'] );
818 + unset( $args['kb_slug'] );
825 819
826 - // if ( $_term_id == null ) {
827 - // throw new \Exception( __( '$args["term_id"] cannot be null.', 'betterdocs' ) );
828 - // }
820 + $tax_query = [
821 + [
822 + 'taxonomy' => 'doc_category',
823 + 'field' => 'slug',
824 + 'terms' => $_term_slug,
825 + 'operator' => 'AND',
826 + 'include_children' => false
827 + ]
828 + ];
829 829
830 - /**
831 - * Term Slug for tax_query
832 - */
833 - $_term_slug = '';
834 - if ( ! empty( $args[ 'term_slug' ] ) ) {
835 - $_term_slug = trim( $args[ 'term_slug' ] );
836 - unset( $args[ 'term_slug' ] );
837 - }
830 + if ( ! isset( $args['orderby'] ) || $args['orderby'] == 'betterdocs_order' ) {
831 + $args['orderby'] = 'post__in';
832 + $args['post__in'] = $this->get_docs_order_by_terms( $_term_id );
833 + }
838 834
839 - /**
840 - * @todo old hook
841 - * hook: betterdocs_cat_template_multikb
842 - */
835 + if ( isset( $args['tax_query'] ) ) {
836 + $tax_query = $args['tax_query'];
837 + }
843 838
844 - $_multiple_kb = apply_filters(
845 - 'betterdocs_enable_multiple_knowledge_base',
846 - isset( $args[ 'multiple_kb' ] ) ? (bool) $args[ 'multiple_kb' ] : false,
847 - $_origin_args
848 - );
839 + $args['tax_query'] = apply_filters(
840 + 'betterdocs_docs_tax_query_args',
841 + $tax_query,
842 + $_multiple_kb,
843 + $_term_slug,
844 + $_kb_slug,
845 + $_origin_args,
846 + $this->is_inner_templates()
847 + );
848 + /**
849 + * Final parse args
850 + */
851 + $args = wp_parse_args( $args, $default_args );
849 852
850 - $_kb_slug = isset( $args[ 'kb_slug' ] ) ? trim( $args[ 'kb_slug' ] ) : '';
853 + if ( ! empty( $filter ) ) {
854 + $filter = array_flip( $filter );
855 + $args = array_filter(
856 + $args,
857 + function ( $item ) use ( $filter ) {
858 + return ! array_key_exists( $item, $filter );
859 + },
860 + ARRAY_FILTER_USE_KEY
861 + );
862 + }
851 863
852 - unset( $args[ 'multiple_kb' ] );
853 - unset( $args[ 'kb_slug' ] );
864 + return apply_filters( 'betterdocs_articles_args', $args, $_term_id, $_origin_args );
865 + }
854 866
855 - $tax_query = array(
856 - array(
857 - 'taxonomy' => 'doc_category',
858 - 'field' => 'slug',
859 - 'terms' => $_term_slug,
860 - 'operator' => 'AND',
861 - 'include_children' => false
862 - )
863 - );
867 + public function faq_terms_query_args( $includes = '', $excludes = '', $args = [] ) {
868 + $_args = [
869 + 'taxonomy' => 'betterdocs_faq_category',
870 + 'meta_key' => 'order',
871 + 'orderby' => 'meta_value_num',
872 + 'order' => 'ASC',
873 + 'include' => $includes,
874 + 'exclude' => $excludes,
875 + 'meta_query' => [
876 + [
877 + 'key' => 'status',
878 + 'value' => 1,
879 + 'compare' => '=='
880 + ]
881 + ]
882 + ];
864 883
865 - if ( ! isset( $args[ 'orderby' ] ) || 'betterdocs_order' == $args[ 'orderby' ] ) {
866 - $args[ 'orderby' ] = 'post__in';
867 - $args[ 'post__in' ] = $this->get_docs_order_by_terms( $_term_id );
868 - }
884 + if ( $_args['include'] == 'all' ) {
885 + unset( $_args['include'] );
886 + unset( $_args['exclude'] );
887 + }
869 888
870 - if ( isset( $args[ 'tax_query' ] ) ) {
871 - $tax_query = $args[ 'tax_query' ];
872 - }
889 + if ( empty( $_args['exclude'] ) ) {
890 + unset( $_args['exclude'] );
891 + }
873 892
874 - $args[ 'tax_query' ] = apply_filters(
875 - 'betterdocs_docs_tax_query_args',
876 - $tax_query,
877 - $_multiple_kb,
878 - $_term_slug,
879 - $_kb_slug,
880 - $_origin_args,
881 - $this->is_inner_templates()
882 - );
883 - /**
884 - * Final parse args
885 - */
886 - $args = wp_parse_args( $args, $default_args );
893 + if ( empty( $_args['include'] ) ) {
894 + unset( $_args['include'] );
895 + }
887 896
888 - if ( ! empty( $filter ) ) {
889 - $filter = array_flip( $filter );
890 - $args = array_filter(
891 - $args,
892 - function ( $item ) use ( $filter ) {
893 - return ! array_key_exists( $item, $filter );
894 - },
895 - ARRAY_FILTER_USE_KEY
896 - );
897 - }
897 + return wp_parse_args( $args, $_args );
898 + }
898 899
899 - $final_args = apply_filters( 'betterdocs_articles_args', $args, $_term_id, $_origin_args );
900 + public function get_faq_by_term( $term_id ) {
901 + global $wpdb;
900 902
901 - // Process betterdocs_order AFTER all filters have run
902 - if ( isset( $final_args[ 'orderby' ] ) && 'betterdocs_order' == $final_args[ 'orderby' ] ) {
903 - $docs_order = $this->get_docs_order_by_terms( $_term_id );
903 + $args = [
904 + 'post_type' => 'betterdocs_faq',
905 + 'post_status' => 'publish',
906 + 'tax_query' => [
907 + [
908 + 'taxonomy' => 'betterdocs_faq_category',
909 + 'field' => 'term_id',
910 + 'terms' => $term_id,
911 + 'operator' => 'AND'
912 + ]
913 + ],
914 + 'posts_per_page' => -1
915 + ];
904 916
905 - if ( ! empty( $docs_order ) ) {
906 - $final_args[ 'orderby' ] = 'post__in';
907 - $final_args[ 'post__in' ] = $docs_order;
908 - } else {
909 - $final_args[ 'orderby' ] = 'menu_order';
910 - $final_args[ 'order' ] = 'ASC';
911 - }
912 - }
917 + $args['orderby'] = 'post__in';
918 + $args['post__in'] = $this->get_faq_orders( $term_id );
913 919
914 - return $final_args;
915 - }
920 + return new WP_Query( $args );
921 + }
916 922
917 - public function faq_terms_query_args( $includes = '', $excludes = '', $args = array() ) {
918 - $_args = array(
919 - 'taxonomy' => 'betterdocs_faq_category',
920 - 'meta_key' => 'order',
921 - 'orderby' => 'meta_value_num',
922 - 'order' => 'ASC',
923 - 'include' => $includes,
924 - 'exclude' => $excludes,
925 - 'meta_query' => array(
926 - array(
927 - 'key' => 'status',
928 - 'value' => 1,
929 - 'compare' => '=='
930 - )
931 - )
932 - );
923 + public function get_faq_orders( $term_id = null ) {
924 + global $wpdb;
925 + $faq_order = get_term_meta( $term_id, '_betterdocs_faq_order', true );
926 + $faq_order = explode( ',', $faq_order );
933 927
934 - if ( 'all' == $_args[ 'include' ] ) {
935 - unset( $_args[ 'include' ] );
936 - unset( $_args[ 'exclude' ] );
937 - }
928 + $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
929 + $query_key = 'betterdocs_faq_order_' . md5( $query );
938 930
939 - if ( empty( $_args[ 'exclude' ] ) ) {
940 - unset( $_args[ 'exclude' ] );
941 - }
931 + if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
932 + return $results;
933 + }
942 934
943 - if ( empty( $_args[ 'include' ] ) ) {
944 - unset( $_args[ 'include' ] );
945 - }
935 + if ( ! empty( $faq_order ) ) {
936 + $new_ids = [];
937 + $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
946 938
947 - return wp_parse_args( $args, $_args );
948 - }
939 + if ( ! is_null( $results ) && ! empty( $results ) && is_array( $results ) ) {
940 + $object_ids = array_filter(
941 + $results,
942 + function ( $value ) use ( $faq_order ) {
943 + return ! in_array( $value->object_id, $faq_order );
944 + }
945 + );
949 946
950 - public function get_faq_by_term( $term_id ) {
951 - global $wpdb;
947 + if ( ! empty( $object_ids ) ) {
948 + array_walk(
949 + $object_ids,
950 + function ( $value ) use ( &$new_ids ) {
951 + $new_ids[] = $value->object_id;
952 + }
953 + );
954 + }
955 + }
952 956
953 - $args = array(
954 - 'post_type' => 'betterdocs_faq',
955 - 'post_status' => 'publish',
956 - 'tax_query' => array(
957 - array(
958 - 'taxonomy' => 'betterdocs_faq_category',
959 - 'field' => 'term_id',
960 - 'terms' => $term_id,
961 - 'operator' => 'AND'
962 - )
963 - ),
964 - 'posts_per_page' => -1
965 - );
957 + $faq_order = array_merge( $new_ids, $faq_order );
958 + }
966 959
967 - $args[ 'orderby' ] = 'post__in';
968 - $args[ 'post__in' ] = $this->get_faq_orders( $term_id );
960 + $this->database->set_cache( $query_key, $faq_order, 1 );
969 961
970 - return new WP_Query( $args );
971 - }
962 + return $faq_order;
963 + }
972 964
973 - public function get_faq_orders( $term_id = null ) {
974 - global $wpdb;
975 - $faq_order = get_term_meta( $term_id, '_betterdocs_faq_order', true );
976 - $faq_order = explode( ',', $faq_order );
965 + public function get_faq_terms( $terms = [] ) {
966 + $_terms = get_terms(
967 + [
968 + 'taxonomy' => 'betterdocs_faq_category',
969 + 'hide_empty' => true,
970 + 'orderby' => 'name',
971 + 'order' => 'ASC',
972 + 'meta_query' => [
973 + [
974 + 'key' => 'status',
975 + 'value' => 1,
976 + 'compare' => '=='
977 + ]
978 + ]
979 + ]
980 + );
977 981
978 - $query = $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}term_relationships WHERE term_taxonomy_id = %d", $term_id );
979 - $query_key = 'betterdocs_faq_order_' . md5( $query );
982 + if ( ! is_wp_error( $_terms ) ) {
983 + foreach ( $_terms as $term ) {
984 + $terms[ $term->term_id ] = $term->name;
985 + }
986 + }
980 987
981 - if ( ( $results = $this->database->get_cache( $query_key ) ) !== false ) {
982 - return $results;
983 - }
988 + return $terms;
989 + }
984 990
985 - if ( ! empty( $faq_order ) ) {
986 - $new_ids = array();
987 - // $query is prepared upstream; results are cached via $this->database->get_cache above (line 980).
988 - $results = $wpdb->get_results( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
991 + public function get_doc_terms( $terms = [] ) {
992 + $_terms = get_terms(
993 + [
994 + 'taxonomy' => 'doc_category',
995 + 'hide_empty' => true,
996 + 'orderby' => 'name',
997 + 'order' => 'ASC',
998 + ]
999 + );
989 1000
990 - if ( ! is_null( $results ) && ! empty( $results ) && is_array( $results ) ) {
991 - $object_ids = array_filter(
992 - $results,
993 - function ( $value ) use ( $faq_order ) {
994 - return ! in_array( $value->object_id, $faq_order );
995 - }
996 - );
1001 + if ( ! is_wp_error( $_terms ) ) {
1002 + foreach ( $_terms as $term ) {
1003 + $terms[ $term->term_id ] = $term->name;
1004 + }
1005 + }
997 1006
998 - if ( ! empty( $object_ids ) ) {
999 - array_walk(
1000 - $object_ids,
1001 - function ( $value ) use ( &$new_ids ) {
1002 - $new_ids[ ] = $value->object_id;
1003 - }
1004 - );
1005 - }
1006 - }
1007 + return $terms;
1008 + }
1007 1009
1008 - $faq_order = array_merge( $new_ids, $faq_order );
1009 - }
1010 1010
1011 - $this->database->set_cache( $query_key, $faq_order, 1 );
1011 + public function get_docs_count( $term, $nested_subcategory = false, $args = [] ) {
1012 + // Validate term object
1013 + if ( ! is_object( $term ) ) {
1014 + return 0;
1015 + }
1012 1016
1013 - return $faq_order;
1014 - }
1017 + $counts = isset( $term->count ) ? $term->count : 0;
1015 1018
1016 - public function get_faq_terms( $terms = array() ) {
1017 - $_terms = get_terms(
1018 - array(
1019 - 'taxonomy' => 'betterdocs_faq_category',
1020 - 'hide_empty' => true,
1021 - 'orderby' => 'name',
1022 - 'order' => 'ASC',
1023 - 'meta_query' => array(
1024 - array(
1025 - 'key' => 'status',
1026 - 'value' => 1,
1027 - 'compare' => '=='
1028 - )
1029 - )
1030 - )
1031 - );
1019 + if ( $nested_subcategory == false ) {
1020 + // For non-nested categories, we need to recalculate counts based on user capabilities
1021 + // Only proceed if we have a valid term with required properties
1022 + if ( isset( $term->term_id ) && isset( $term->taxonomy ) && is_numeric( $term->term_id ) ) {
1023 + // Get all post IDs for this term
1024 + $post_ids = get_objects_in_term( $term->term_id, $term->taxonomy );
1032 1025
1033 - if ( ! is_wp_error( $_terms ) ) {
1034 - foreach ( $_terms as $term ) {
1035 - $terms[ $term->term_id ] = $term->name;
1036 - }
1037 - }
1026 + if ( ! empty( $post_ids ) ) {
1027 + if ( current_user_can( 'read_private_docs' ) ) {
1028 + // For users with read_private_docs capability, include both private and public posts
1029 + $filtered_post_ids = array_filter( $post_ids, function( $post_id ) {
1030 + $post_status = get_post_status( $post_id );
1031 + return $post_status === 'private' || is_post_publicly_viewable( $post_id );
1032 + });
1033 + } else {
1034 + // For users without read_private_docs capability, only include public posts
1035 + $filtered_post_ids = array_filter( $post_ids, function( $post_id ) {
1036 + return is_post_publicly_viewable( $post_id );
1037 + });
1038 + }
1038 1039
1039 - return $terms;
1040 - }
1040 + $counts = count( $filtered_post_ids );
1041 + } else {
1042 + $counts = 0;
1043 + }
1044 + }
1041 1045
1042 - public function get_doc_terms( $terms = array() ) {
1043 - $_terms = get_terms(
1044 - array(
1045 - 'taxonomy' => 'doc_category',
1046 - 'hide_empty' => true,
1047 - 'orderby' => 'name',
1048 - 'order' => 'ASC'
1049 - )
1050 - );
1046 + return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
1047 + }
1051 1048
1052 - if ( ! is_wp_error( $_terms ) ) {
1053 - foreach ( $_terms as $term ) {
1054 - $terms[ $term->term_id ] = $term->name;
1055 - }
1056 - }
1049 + $_child_terms_docs_ids = $this->get_doc_ids_by_term( $term, null, $nested_subcategory );
1050 + if ( is_array( $_child_terms_docs_ids ) ) {
1051 + $counts = count( $_child_terms_docs_ids );
1052 + }
1057 1053
1058 - return $terms;
1059 - }
1054 + return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
1055 + }
1060 1056
1061 - public function get_docs_count( $term, $nested_subcategory = false, $args = array() ) {
1062 - // Validate term object
1063 - if ( ! is_object( $term ) ) {
1064 - return 0;
1065 - }
1057 + public function get_doc_ids_by_term( $term, $optional = null, $nested_subcategory = false ) {
1058 + // Check if term has required properties and is a valid object
1059 + if ( ! is_object( $term ) || ! isset( $term->term_id ) || ! isset( $term->taxonomy ) || ! is_numeric( $term->term_id ) ) {
1060 + return false;
1061 + }
1066 1062
1067 - $counts = isset( $term->count ) ? $term->count : 0;
1068 -
1069 - if ( false == $nested_subcategory ) {
1070 - // For non-nested categories, we need to recalculate counts based on user capabilities
1071 - // Only proceed if we have a valid term with required properties
1072 - if ( isset( $term->term_id ) && isset( $term->taxonomy ) && is_numeric( $term->term_id ) ) {
1073 - // Get all post IDs for this term
1074 - $post_ids = get_objects_in_term( $term->term_id, $term->taxonomy );
1075 -
1076 - if ( ! empty( $post_ids ) ) {
1077 - if ( current_user_can( 'read_private_docs' ) ) {
1078 - // For users with read_private_docs capability, include both private and public posts
1079 - $filtered_post_ids = array_filter( $post_ids, function ( $post_id ) {
1080 - $post_status = get_post_status( $post_id );
1081 - return 'private' === $post_status || is_post_publicly_viewable( $post_id );
1082 - } );
1083 - } else {
1084 - // For users without read_private_docs capability, only include public posts
1085 - $filtered_post_ids = array_filter( $post_ids, function ( $post_id ) {
1086 - return is_post_publicly_viewable( $post_id );
1087 - } );
1088 - }
1089 -
1090 - $counts = count( $filtered_post_ids );
1091 - } else {
1092 - $counts = 0;
1093 - }
1094 - }
1095 -
1096 - return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
1097 - }
1098 -
1099 - $_child_terms_docs_ids = $this->get_doc_ids_by_term( $term, null, $nested_subcategory );
1100 - if ( is_array( $_child_terms_docs_ids ) ) {
1101 - $counts = count( $_child_terms_docs_ids );
1102 - }
1103 -
1104 - return apply_filters( 'betterdocs_docs_count', $counts, $term, $nested_subcategory, $args );
1105 - }
1106 -
1107 - public function get_doc_ids_by_term( $term, $optional = null, $nested_subcategory = false ) {
1108 - // Check if term has required properties and is a valid object
1109 - if ( ! is_object( $term ) || ! isset( $term->term_id ) || ! isset( $term->taxonomy ) || ! is_numeric( $term->term_id ) ) {
1110 - return false;
1111 - }
1112 -
1113 - $args = array(
1114 - 'taxonomy' => $term->taxonomy,
1115 - 'include' => $term->term_id,
1116 - );
1063 + $args = ['include' => $term->term_id];
1117 1064 if ( $nested_subcategory ) {
1118 - $args[ 'child_of' ] = $term->term_id;
1119 - unset( $args[ 'include' ] );
1065 + $args['child_of'] = $term->term_id;
1066 + unset( $args['include'] );
1120 1067 }
1121 - $_child_terms = get_terms( $args );
1068 + $_child_terms = get_terms( $term->taxonomy, $args );
1122 1069
1123 1070 if ( ! is_array( $_child_terms ) ) {
1124 1071 return false;
1125 1072 }
@@ -1129,9 +1076,9 @@
1129 1076 $_child_terms_ids = array_column( $_child_terms, 'term_id' );
1130 1077 $_child_terms_taxs = array_column( $_child_terms, 'taxonomy' );
1131 1078 $_child_terms_docs_ids = get_objects_in_term( $_child_terms_ids, $_child_terms_taxs );
1132 1079
1133 - if ( null !== $optional ) {
1080 + if ( $optional !== null ) {
1134 1081 $_optional_doc_ids = get_objects_in_term( $optional->term_id, $optional->taxonomy );
1135 1082 $_child_terms_docs_ids = array_intersect( $_child_terms_docs_ids, $_optional_doc_ids );
1136 1083 }
1137 1084
@@ -1140,247 +1087,241 @@
1140 1087 return is_post_publicly_viewable( $doc_id );
1141 1088 }
1142 1089
1143 1090 $_status = get_post_status( $doc_id );
1144 - return 'private' == $_status || is_post_publicly_viewable( $doc_id );
1091 + return $_status == 'private' || is_post_publicly_viewable( $doc_id );
1145 1092 } );
1146 - }
1093 + }
1147 1094
1148 - /**
1149 - * Get the common query arguments for WP_Query.
1150 - *
1151 - * @param string $terms The taxonomy.
1152 - * @param string $term_slug The taxonomy term slug.
1153 - * @param array $additional_args Additional arguments to merge with the common arguments.
1154 - * @return array The query arguments.
1155 - */
1156 - private function tax_query_args( $terms, $term_slug, $additional_args = array() ) {
1157 - $common_args = array(
1158 - 'post_type' => 'docs',
1159 - 'post_status' => 'publish',
1160 - 'tax_query' => array(
1161 - array(
1162 - 'taxonomy' => $terms,
1163 - 'field' => 'slug',
1164 - 'terms' => $term_slug
1165 - )
1166 - )
1167 - );
1168 - return array_merge( $common_args, $additional_args );
1169 - }
1095 + /**
1096 + * Get the common query arguments for WP_Query.
1097 + *
1098 + * @param string $terms The taxonomy.
1099 + * @param string $term_slug The taxonomy term slug.
1100 + * @param array $additional_args Additional arguments to merge with the common arguments.
1101 + * @return array The query arguments.
1102 + */
1103 + private function tax_query_args( $terms, $term_slug, $additional_args = array() ) {
1104 + $common_args = array(
1105 + 'post_type' => 'docs',
1106 + 'post_status' => 'publish',
1107 + 'tax_query' => array(
1108 + array(
1109 + 'taxonomy' => $terms,
1110 + 'field' => 'slug',
1111 + 'terms' => $term_slug,
1112 + ),
1113 + ),
1114 + );
1115 + return array_merge( $common_args, $additional_args );
1116 + }
1170 1117
1171 - /**
1172 - * Get the latest updated date for a specific taxonomy term.
1173 - *
1174 - * @param string $terms The taxonomy.
1175 - * @param string $term_slug The taxonomy term slug.
1176 - * @return string|null The latest modified date or null if no posts found.
1177 - */
1178 - public function latest_updated_date( $terms, $term_slug ) {
1179 - $args = $this->tax_query_args(
1180 - $terms,
1181 - $term_slug,
1182 - array(
1183 - 'posts_per_page' => 1,
1184 - 'orderby' => 'modified',
1185 - 'order' => 'DESC'
1186 - )
1187 - );
1118 + /**
1119 + * Get the latest updated date for a specific taxonomy term.
1120 + *
1121 + * @param string $terms The taxonomy.
1122 + * @param string $term_slug The taxonomy term slug.
1123 + * @return string|null The latest modified date or null if no posts found.
1124 + */
1125 + public function latest_updated_date( $terms, $term_slug ) {
1126 + $args = $this->tax_query_args(
1127 + $terms,
1128 + $term_slug,
1129 + array(
1130 + 'posts_per_page' => 1,
1131 + 'orderby' => 'modified',
1132 + 'order' => 'DESC',
1133 + )
1134 + );
1188 1135
1189 - $query = new WP_Query( $args );
1136 + $query = new WP_Query( $args );
1190 1137
1191 - if ( $query->have_posts() ) {
1192 - while ( $query->have_posts() ) {
1193 - $query->the_post();
1194 - $latest_post = get_the_modified_date();
1195 - wp_reset_postdata();
1196 - return $latest_post;
1197 - }
1198 - } else {
1199 - return null;
1200 - }
1201 - }
1138 + if ( $query->have_posts() ) {
1139 + while ( $query->have_posts() ) {
1140 + $query->the_post();
1141 + $latest_post = get_the_modified_date();
1142 + wp_reset_postdata();
1143 + return $latest_post;
1144 + }
1145 + } else {
1146 + return null;
1147 + }
1148 + }
1202 1149
1203 - /**
1204 - * Check if there are any new posts within the last 7 days for a specific taxonomy term.
1205 - *
1206 - * @param string $terms The taxonomy.
1207 - * @param string $term_slug The taxonomy term slug.
1208 - * @return bool True if there are new posts, false otherwise.
1209 - */
1210 - public function check_new_posts( $terms, $term_slug ) {
1211 - $date_7_days_ago = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
1150 + /**
1151 + * Check if there are any new posts within the last 7 days for a specific taxonomy term.
1152 + *
1153 + * @param string $terms The taxonomy.
1154 + * @param string $term_slug The taxonomy term slug.
1155 + * @return bool True if there are new posts, false otherwise.
1156 + */
1157 + public function check_new_posts( $terms, $term_slug ) {
1158 + $date_7_days_ago = date( 'Y-m-d H:i:s', strtotime( '-7 days' ) );
1212 1159
1213 - $args = $this->tax_query_args(
1214 - $terms,
1215 - $term_slug,
1216 - array(
1217 - 'posts_per_page' => 1,
1218 - 'orderby' => 'modified',
1219 - 'order' => 'DESC',
1220 - 'date_query' => array(
1221 - array(
1222 - 'after' => $date_7_days_ago,
1223 - 'inclusive' => true
1224 - )
1225 - )
1226 - )
1227 - );
1160 + $args = $this->tax_query_args(
1161 + $terms,
1162 + $term_slug,
1163 + array(
1164 + 'posts_per_page' => 1,
1165 + 'orderby' => 'modified',
1166 + 'order' => 'DESC',
1167 + 'date_query' => array(
1168 + array(
1169 + 'after' => $date_7_days_ago,
1170 + 'inclusive' => true,
1171 + ),
1172 + ),
1173 + )
1174 + );
1228 1175
1229 - $query = new WP_Query( $args );
1176 + $query = new WP_Query( $args );
1230 1177
1231 - $has_new_posts = $query->have_posts();
1178 + $has_new_posts = $query->have_posts();
1232 1179
1233 - wp_reset_postdata();
1180 + wp_reset_postdata();
1234 1181
1235 - return $has_new_posts;
1236 - }
1182 + return $has_new_posts;
1183 + }
1237 1184
1238 - public function insert_search_keyword( $search_input, $input_not_found ) {
1239 - if ( empty( $search_input ) ) {
1240 - return false;
1241 - }
1185 + public function insert_search_keyword( $search_input, $input_not_found ) {
1186 + if ( empty( $search_input ) ) {
1187 + return false;
1188 + }
1242 1189
1243 - global $wpdb;
1244 -
1245 - // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- live search-keyword analytics; cache would defeat the purpose.
1246 - // Use BINARY comparison to avoid collation mismatch errors
1247 - // This works across all character sets (latin1, utf8, utf8mb4, etc.)
1248 - $search = $wpdb->get_results(
1249 - $wpdb->prepare(
1250 - "SELECT *
1190 + global $wpdb;
1191 + $search = $wpdb->get_results(
1192 + $wpdb->prepare(
1193 + "SELECT *
1251 1194 FROM {$wpdb->prefix}betterdocs_search_keyword
1252 - WHERE BINARY keyword = %s",
1253 - $search_input
1254 - )
1255 - );
1195 + WHERE keyword = %s",
1196 + $search_input
1197 + )
1198 + );
1256 1199
1257 - if ( ! empty( $search ) ) {
1258 - $search_log = $wpdb->get_results(
1259 - $wpdb->prepare(
1260 - "SELECT *
1200 + if ( ! empty( $search ) ) {
1201 + $search_log = $wpdb->get_results(
1202 + $wpdb->prepare(
1203 + "SELECT *
1261 1204 FROM {$wpdb->prefix}betterdocs_search_log
1262 1205 WHERE created_at = %s AND keyword_id = %d",
1263 - gmdate( 'Y-m-d' ),
1264 - $search[ 0 ]->id
1265 - )
1266 - );
1206 + date( 'Y-m-d' ),
1207 + $search[0]->id
1208 + )
1209 + );
1267 1210
1268 - if ( ! empty( $search_log ) ) {
1269 - if ( ! empty( $input_not_found ) ) {
1270 - $tbl_field = 'not_found_count';
1271 - $count = $search_log[ 0 ]->not_found_count + 1;
1272 - } else {
1273 - $tbl_field = 'count';
1274 - $count = $search_log[ 0 ]->count + 1;
1275 - }
1276 - // $tbl_field is validated immediately above to be either 'count' or
1277 - // 'not_found_count' — safe to interpolate as a column identifier.
1278 - // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter -- $tbl_field is an allowlisted column name ('count'|'not_found_count'); safe to interpolate.
1279 - $insert = $wpdb->query(
1280 - $wpdb->prepare(
1281 - "UPDATE {$wpdb->prefix}betterdocs_search_log SET {$tbl_field} = %d WHERE created_at = %s AND keyword_id = %d", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
1282 - $count,
1283 - $search_log[ 0 ]->created_at,
1284 - $search_log[ 0 ]->keyword_id
1285 - )
1286 - );
1287 - } else {
1288 - if ( ! empty( $input_not_found ) ) {
1289 - $count = 0;
1290 - $not_found_count = 1;
1291 - } else {
1292 - $count = 1;
1293 - $not_found_count = 0;
1294 - }
1295 - $insert = $wpdb->query(
1296 - $wpdb->prepare(
1297 - "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1211 + if ( ! empty( $search_log ) ) {
1212 + if ( ! empty( $input_not_found ) ) {
1213 + $tbl_field = 'not_found_count';
1214 + $count = $search_log[0]->not_found_count + 1;
1215 + } else {
1216 + $tbl_field = 'count';
1217 + $count = $search_log[0]->count + 1;
1218 + }
1219 + // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
1220 + $insert = $wpdb->query(
1221 + $wpdb->prepare(
1222 + "UPDATE {$wpdb->prefix}betterdocs_search_log
1223 + SET " . $tbl_field . ' = ' . $count . '
1224 + WHERE created_at = %s AND keyword_id = %d',
1225 + $search_log[0]->created_at,
1226 + $search_log[0]->keyword_id
1227 + )
1228 + );
1229 + } else {
1230 + if ( ! empty( $input_not_found ) ) {
1231 + $count = 0;
1232 + $not_found_count = 1;
1233 + } else {
1234 + $count = 1;
1235 + $not_found_count = 0;
1236 + }
1237 + $insert = $wpdb->query(
1238 + $wpdb->prepare(
1239 + "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1298 1240 ( keyword_id, count, not_found_count, created_at )
1299 1241 VALUES ( %d, %d, %d, %s )",
1300 - array(
1301 - $search[ 0 ]->id,
1302 - $count,
1303 - $not_found_count,
1304 - gmdate( 'Y-m-d' )
1305 - )
1306 - )
1307 - );
1308 - }
1309 - } else {
1310 - $insert = $wpdb->query(
1311 - $wpdb->prepare(
1312 - "INSERT INTO {$wpdb->prefix}betterdocs_search_keyword
1242 + [
1243 + $search[0]->id,
1244 + $count,
1245 + $not_found_count,
1246 + date( 'Y-m-d' )
1247 + ]
1248 + )
1249 + );
1250 + }
1251 + } else {
1252 + $insert = $wpdb->query(
1253 + $wpdb->prepare(
1254 + "INSERT INTO {$wpdb->prefix}betterdocs_search_keyword
1313 1255 ( keyword )
1314 1256 VALUES ( %s )",
1315 - array(
1316 - $search_input
1317 - )
1318 - )
1319 - );
1257 + [
1258 + $search_input
1259 + ]
1260 + )
1261 + );
1320 1262
1321 - if ( $insert ) {
1322 - if ( ! empty( $input_not_found ) ) {
1323 - $count = 0;
1324 - $not_found_count = 1;
1325 - } else {
1326 - $count = 1;
1327 - $not_found_count = 0;
1328 - }
1329 - $insert = $wpdb->query(
1330 - $wpdb->prepare(
1331 - "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1263 + if ( $insert ) {
1264 + if ( ! empty( $input_not_found ) ) {
1265 + $count = 0;
1266 + $not_found_count = 1;
1267 + } else {
1268 + $count = 1;
1269 + $not_found_count = 0;
1270 + }
1271 + $insert = $wpdb->query(
1272 + $wpdb->prepare(
1273 + "INSERT INTO {$wpdb->prefix}betterdocs_search_log
1332 1274 ( keyword_id, count, not_found_count, created_at )
1333 1275 VALUES ( %d, %d, %d, %s )",
1334 - array(
1335 - $wpdb->insert_id,
1336 - $count,
1337 - $not_found_count,
1338 - gmdate( 'Y-m-d' )
1339 - )
1340 - )
1341 - );
1342 - }
1343 - }
1344 - // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
1345 - return $insert;
1346 - }
1276 + [
1277 + $wpdb->insert_id,
1278 + $count,
1279 + $not_found_count,
1280 + date( 'Y-m-d' )
1281 + ]
1282 + )
1283 + );
1284 + }
1285 + }
1286 + return $insert;
1287 + }
1347 1288
1348 - /**
1349 - * Counts the number of 'doc_category' terms assigned to a specific 'knowledge_base' by slug.
1350 - *
1351 - * This method retrieves all terms in the 'doc_category' taxonomy and checks the term meta
1352 - * 'doc_category_knowledge_base' to determine how many 'doc_category' terms are associated
1353 - * with a given 'knowledge_base' slug.
1354 - *
1355 - * @param string $knowledge_base_slug The slug of the 'knowledge_base' to count the assignments for.
1356 - *
1357 - * @return int The count of 'doc_category' terms assigned to the specified 'knowledge_base'.
1358 - */
1359 - public function count_doc_categories_for_knowledge_base( $knowledge_base_slug ) {
1360 - $count = 0;
1289 + /**
1290 + * Counts the number of 'doc_category' terms assigned to a specific 'knowledge_base' by slug.
1291 + *
1292 + * This method retrieves all terms in the 'doc_category' taxonomy and checks the term meta
1293 + * 'doc_category_knowledge_base' to determine how many 'doc_category' terms are associated
1294 + * with a given 'knowledge_base' slug.
1295 + *
1296 + * @param string $knowledge_base_slug The slug of the 'knowledge_base' to count the assignments for.
1297 + *
1298 + * @return int The count of 'doc_category' terms assigned to the specified 'knowledge_base'.
1299 + */
1300 + public function count_doc_categories_for_knowledge_base( $knowledge_base_slug ) {
1301 + $count = 0;
1361 1302
1362 - $doc_categories = get_terms(
1363 - array(
1364 - 'taxonomy' => 'doc_category',
1365 - 'hide_empty' => true
1366 - )
1367 - );
1303 + $doc_categories = get_terms(
1304 + array(
1305 + 'taxonomy' => 'doc_category',
1306 + 'hide_empty' => true,
1307 + )
1308 + );
1368 1309
1369 - if ( ! empty( $doc_categories ) && ! is_wp_error( $doc_categories ) ) {
1370 - foreach ( $doc_categories as $term ) {
1371 - $meta_value = get_term_meta( $term->term_id, 'doc_category_knowledge_base', true );
1310 + if ( ! empty( $doc_categories ) && ! is_wp_error( $doc_categories ) ) {
1311 + foreach ( $doc_categories as $term ) {
1312 + $meta_value = get_term_meta( $term->term_id, 'doc_category_knowledge_base', true );
1372 1313
1373 - if ( $meta_value ) {
1374 - $knowledge_bases = maybe_unserialize( $meta_value );
1314 + if ( $meta_value ) {
1315 + $knowledge_bases = maybe_unserialize( $meta_value );
1375 1316
1376 - // Check if the specific knowledge base slug is present in the meta value array
1377 - if ( is_array( $knowledge_bases ) && in_array( $knowledge_base_slug, $knowledge_bases ) ) {
1378 - ++$count;
1379 - }
1380 - }
1381 - }
1382 - }
1317 + // Check if the specific knowledge base slug is present in the meta value array
1318 + if ( is_array( $knowledge_bases ) && in_array( $knowledge_base_slug, $knowledge_bases ) ) {
1319 + ++$count;
1320 + }
1321 + }
1322 + }
1323 + }
1383 1324
1384 - return $count;
1385 - }
1325 + return $count;
1326 + }
1386 1327 }