PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.9.2.1
Filter Everything — WordPress & WooCommerce Filters v1.9.2.1
1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 All 52 releases
filter-everything / src / Entities / TaxonomyEntity.php

TaxonomyEntity.php in Filter Everything — WordPress & WooCommerce Filters 1.9.2.1, at src/Entities/TaxonomyEntity.php

488 lines 15.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FilterEverything\Filter;
4
5 if ( ! defined('ABSPATH') ) {
6 exit;
7 }
8
9 class TaxonomyEntity implements Entity
10 {
11 public $items = [];
12
13 public $excludedTerms = [];
14
15 public $isInclude = false;
16
17 public $descendants = [];
18
19 private $entityName = '';
20
21 private $new_tax_query = [];
22
23 private $postTypes = [];
24
25 public function __construct( $taxName ){
26 $this->entityName = $taxName;
27 $this->getAllExistingTerms();
28 $this->passTermNames();
29 }
30
31 public function setPostTypes( $postTypes )
32 {
33 $this->postTypes = $postTypes;
34 }
35
36 public function setExcludedTerms( $excludedTerms, $isInclude )
37 {
38 $this->excludedTerms = (array) $excludedTerms;
39 $this->isInclude = $isInclude;
40 }
41
42 public function getName()
43 {
44 return $this->entityName;
45 }
46
47 function excludeTerms( $terms )
48 {
49 $exclude = [];
50
51 if( ! empty( $this->excludedTerms ) ){
52 $exclude = $this->excludedTerms;
53 }
54
55 $exclude_flipped = array_flip( $exclude );
56 if( $this->isInclude ){
57 $included_terms = [];
58 foreach( $terms as $index => $term ){
59 if( isset( $exclude_flipped[$term->term_id] ) ){
60 $included_terms[$index] = $term;
61 }
62 }
63 $terms = $included_terms;
64 }else{
65 foreach( $terms as $index => $term ){
66 if( isset( $exclude_flipped[$term->term_id] ) ){
67 unset( $terms[$index] );
68 }
69 }
70 }
71
72 return $terms;
73 }
74
75 public function getTermTaxonomyPostsIds( $termTaxonomyIds, $termIds, $filter )
76 {
77 global $wpdb;
78 $include_variation_atts = false;
79 $ids = [];
80
81 if(! isset( $filter['slug'] ) ){
82 return $ids;
83 }
84
85 // Check if it is already stored
86 $transient_key = flrt_get_post_ids_transient_key( $filter['slug'] );
87
88 if ( false === ( $results = flrt_get_transient( $transient_key ) ) ) {
89
90 // It wasn't there, so regenerate the data and save the transient
91 if( defined('FLRT_FILTERS_PRO') && FLRT_FILTERS_PRO ) {
92 if( strpos( $this->getName(), 'pa_' ) === 0 ) {
93 $include_variation_atts = true;
94 }
95 }
96
97 $query[] = "SELECT DISTINCT {$wpdb->term_relationships}.term_taxonomy_id";
98 $query[] = ", {$wpdb->term_relationships}.object_id";
99 $query[] = ", tt.term_id";
100
101 if( $include_variation_atts ){
102 $query[] = ", tm.slug";
103 }
104
105 $query[] = "FROM {$wpdb->term_relationships}";
106 $query[] = "LEFT JOIN {$wpdb->term_taxonomy} AS tt";
107 $query[] = "ON ( {$wpdb->term_relationships}.term_taxonomy_id = tt.term_taxonomy_id )";
108
109 if( $include_variation_atts ){
110 $query[] = "LEFT JOIN {$wpdb->terms} AS tm";
111 $query[] = "ON ( tt.term_id = tm.term_id )";
112 }
113
114 $query[] = "WHERE {$wpdb->term_relationships}.term_taxonomy_id IN ('" . implode("','", $termTaxonomyIds) . "')";
115
116 $query = implode(' ', $query);
117
118 $results = $wpdb->get_results($query, ARRAY_A);
119
120 flrt_set_transient( $transient_key, $results, FLRT_TRANSIENT_PERIOD_HOURS * HOUR_IN_SECONDS );
121 }
122
123 $taxonomy_terms = apply_filters( 'wpc_term_taxonomy_terms', $results, $this );
124
125 foreach ($taxonomy_terms as $key => $result) {
126 $ids[$result['term_id']][] = (int) $result['object_id'];
127 }
128
129 // Add possible empty terms without posts
130 foreach( $termIds as $term_id ){
131 if( ! isset( $ids[$term_id] ) ){
132 $ids[$term_id] = [];
133 }
134 }
135
136 // Fix for counts for parent, when 'include_children=true'
137 // Add posts from children terms to their parents
138 // To make correct counts for their parents
139 if( ! empty( $this->descendants ) && $filter['logic'] === 'or' ){
140 // array of parents term_ids, that have children
141 foreach ( $ids as $term_id => $post_ids_array ){
142 if( isset( $this->descendants[$term_id] ) ){
143 foreach ( $this->descendants[$term_id] as $child_term_id ){
144 if( isset( $ids[$child_term_id] ) ){
145 $ids[$term_id] = array_unique( array_merge( $ids[$term_id], $ids[$child_term_id] ) );
146 }
147 }
148 }
149 }
150 }
151
152 return $ids;
153 }
154
155 public function populateTermsWithPostIds( $setId, $post_type )
156 {
157 $termTaxonomyIds = [];
158 $termIds = [];
159 $termPosts = [];
160 $the_filter = [];
161 $allWpQueriedPostIds = [];
162 $em = Container::instance()->getEntityManager();
163 $allWpQueriedPostIds = $em->getAllSetWpQueriedPostIds( $setId );
164
165 $relatedFilters = $em->getSetsRelatedFilters( array( array( 'ID' => $setId) ) );
166
167 foreach ( $relatedFilters as $filter ){
168 if( isset( $filter['e_name'] ) && $filter['e_name'] === $this->getName() ){
169 $the_filter = $filter;
170 break;
171 }
172 }
173
174 foreach ( $this->getAllExistingTerms() as $term ) {
175 $termTaxonomyIds[] = $term->term_taxonomy_id;
176 $termIds[] = $term->term_id;
177 }
178
179
180 if( ! empty( $the_filter ) ){
181 $termPosts = $this->getTermTaxonomyPostsIds( $termTaxonomyIds, $termIds, $the_filter );
182 }
183
184 if( $this->getName() === 'product_shipping_class' ) {
185 foreach ( $termPosts as $term_id => $the_posts ) {
186 $termPosts[$term_id] = apply_filters( 'wpc_from_variations_to_products', $the_posts );
187 }
188 }
189
190 $wpManager = Container::instance()->getWpManager();
191 $wp_queried_object = $wpManager->getQueryVar( 'wp_queried_object' );
192 $wp_queried_term_id = ( isset( $wp_queried_object['term_id'] ) && $wp_queried_object['term_id'] > 0 ) ? $wp_queried_object['term_id'] : 0;
193
194 $allWpQueriedPostIds = array_flip( $allWpQueriedPostIds );
195
196 foreach( $this->items as $index => $term ) {
197 if( isset( $termPosts[$term->term_id] ) ) {
198 $intersected_posts = [];
199 foreach ( $termPosts[$term->term_id] as $post_id ){
200 if( isset( $allWpQueriedPostIds[$post_id] ) ){
201 $intersected_posts[] = $post_id;
202 }
203 }
204 $this->items[$index]->posts = $intersected_posts;
205 }else{
206 // Here could be items that have no posts, but their descendants have
207 $this->items[$index]->posts = [];
208 }
209
210 if( $wp_queried_term_id === $this->items[$index]->term_id ){
211 $this->items[$index]->wp_queried = true;
212 }
213
214 }
215 }
216
217 public function getTerms()
218 {
219 return $this->excludeTerms( $this->getAllExistingTerms() );
220 }
221
222 /**
223 * @param int $id term id
224 * @return false|object term object of false
225 */
226 public function getTerm( $id ){
227 if( ! $id ){
228 return false;
229 }
230
231 foreach ( $this->getAllExistingTerms() as $term ){
232 if( $id == $term->term_id ){
233 return $term;
234 }
235 }
236
237 return false;
238 }
239
240 public function getTermId( $termSlug )
241 {
242 foreach ( $this->getAllExistingTerms() as $term ){
243 if( $termSlug == $term->slug ){
244 return $term->term_id;
245 }
246 }
247
248 return false;
249 }
250
251 /**
252 * @return array list of term_id and names useful to create Select dropdown
253 */
254 public function getTermsForSelect( $optionGroup = false )
255 {
256 $toSelect = [];
257 foreach ( $this->getTerms() as $term ) {
258 if( $optionGroup ){
259 $key = $term->taxonomy.":".$term->term_id;
260 $toSelect[$key] = $term->name;
261 }else{
262 $toSelect[$term->term_id] = $term->name;
263 }
264
265 }
266
267 return $toSelect;
268 }
269
270 public function getTermsForSelect2()
271 {
272 $toSelect = [];
273 foreach ( $this->getTerms() as $term ) {
274 $toSelect[] = array( 'id' => $term->term_id, 'text' =>$term->name );
275 }
276 return $toSelect;
277 }
278
279 public function passTermNames()
280 {
281 foreach ($this->getAllExistingTerms() as $index => $term ) {
282 $this->items[$index]->name = apply_filters( 'wpc_filter_taxonomy_term_name', $term->name, $this->getName() );
283 }
284 }
285
286 function getAllExistingTerms( $force = false )
287 {
288
289 if( empty( $this->items ) || $force ){
290
291 $args = array(
292 'taxonomy' => $this->getName(),
293 'hide_empty' => false,
294 'orderby' => 'none',
295 'order' => 'ASC'
296 );
297
298 /**
299 * Filter terms query $args to allow handle cases with some specific taxonomies
300 */
301 $args = apply_filters( 'wpc_filter_term_query_args', $args, 'taxonomy', $this->getName() );
302 $result = apply_filters( 'wpc_filter_get_taxonomy_terms', get_terms( $args ), $this->getName() );
303
304 $termsUpdated = [];
305 $children_terms = [];
306
307 if( ! empty( $result ) && ! is_wp_error( $result ) ) {
308
309 foreach ($result as $i => $termObject) {
310 $termObject->name = apply_filters( 'wpc_filter_' . $this->getName() . '_term_name', $termObject->name, $termObject );
311 $termObject->cross_count = 0;
312 $termObject->posts = [];
313 $termObject->wp_queried = false;
314
315 $termsUpdated[$i] = $termObject;
316
317 if( ! empty( $termObject->parent ) ) {
318 $children_terms[$termObject->parent][] = $termObject->term_id;
319 }
320 }
321 // Solution for 'include_children=true' problem and parent counts
322 $this->descendants = flrt_find_all_descendants( $children_terms );
323 $this->items = $termsUpdated;
324 }
325
326 }
327
328 return $this->items;
329 }
330
331 private function getSqlLogicOperator( $filter ){
332 if( $filter['logic'] === 'and' ){
333 return 'AND';
334 } else {
335 return 'IN';
336 }
337 }
338
339 public function isTermAlreadyInQuery( $queried_value, $wp_query ){
340 $duplicate = [];
341
342 if ( ! empty( $wp_query->tax_query->queried_terms ) ) {
343 $native_query_terms = $wp_query->tax_query->queried_terms;
344 $queried_taxonomies = array_keys( $native_query_terms );
345
346 foreach ( $queried_taxonomies as $q_taxonomy ) {
347 if( $q_taxonomy === $queried_value['e_name'] ){
348 $query = $native_query_terms[$q_taxonomy];
349
350 if ( ! empty( $query['terms'] ) ) {
351 if ( 'term_id' == $query['field'] ) {
352 $term = get_term( reset( $query['terms'] ), $q_taxonomy );
353 } else {
354 $term = get_term_by( $query['field'], reset( $query['terms'] ), $q_taxonomy );
355 }
356
357 if( ! $term || is_wp_error( $term ) ){
358 return false;
359 }
360
361 foreach( $queried_value['values'] as $filter_slug ){
362 if( $filter_slug === $term->slug ){
363 $duplicate['taxonomy'] = $q_taxonomy;
364 $duplicate['term'] = $term->slug;
365 return $duplicate;
366 }
367 }
368 }
369 }
370 }
371 }
372
373 return false;
374 }
375
376 private function isTheSameTaxQuery( $tax_query_1, $tax_query_2 ){
377 $tax_query_1 = $this->normalizeTaxQueryArray( $tax_query_1 );
378 $tax_query_2 = $this->normalizeTaxQueryArray( $tax_query_2 );
379
380 $diff = array_diff( $tax_query_1, $tax_query_2 );
381
382 if ( empty( $diff ) ){
383 return true;
384 }
385
386 return false;
387 }
388
389 private function normalizeTaxQueryArray( $tax_query ){
390 $normalized_tax_query = [];
391
392 if( ! is_array( $tax_query ) || ! isset( $tax_query['taxonomy'] ) ) {
393 return false;
394 }
395
396 if( is_array( $tax_query['terms'] ) ){
397 sort( $tax_query['terms'] );
398 }
399
400 $normalized_tax_query['taxonomy'] = $tax_query['taxonomy'];
401 if( isset( $tax_query['field'] ) ){
402 $normalized_tax_query['field'] = $tax_query['field'];
403 }
404
405 if( is_array($tax_query['terms']) ){
406 $normalized_tax_query['terms'] = implode( '-', $tax_query['terms'] );
407 }else{
408 $normalized_tax_query['terms'] = $tax_query['terms'];
409 }
410
411 return $normalized_tax_query;
412 }
413
414 private function hasNestedQueries( $tax_query )
415 {
416 if( isset( $tax_query[0]['taxonomy'] ) ){
417 return true;
418 }
419
420 return false;
421 }
422
423 private function addTaxQueryArray( $tax_query_array ){
424 if( ! isset( $tax_query_array['taxonomy'] ) ){
425 return false;
426 }
427
428 $existing_tax_query = $this->new_tax_query;
429 foreach( $existing_tax_query as $index => $present_query ){
430 if( $this->isTheSameTaxQuery( $present_query, $tax_query_array ) ){
431 return false;
432 }
433 }
434
435 $this->new_tax_query[] = $tax_query_array;
436 }
437
438 /**
439 * @return mixed object WP_Query|string;
440 */
441 public function addTermsToWpQuery($queried_value, $wp_query ){
442 /**
443 * @feature Include children should be optionally configured in Settings. Maybe.
444 */
445 //@bug WordPress bug if slug is '0' SQL query is wrong
446 $args = array(
447 'taxonomy' => $queried_value['e_name'],
448 'field' => 'slug',
449 'terms' => $queried_value['values'],
450 );
451
452 if( isset( $queried_value['logic'] ) && $queried_value['logic'] === 'and' ){
453 $args['include_children'] = false;
454 }
455
456 $args['operator'] = $this->getSqlLogicOperator( $queried_value );
457
458 if( isset( $wp_query->tax_query->queries ) && count( $wp_query->tax_query->queries ) ){
459 foreach($wp_query->tax_query->queries as $single_tax_query ){
460 $this->addTaxQueryArray( $single_tax_query );
461 }
462 }
463
464 $this->addTaxQueryArray( $args );
465
466 $already_existing_tax_query = $wp_query->get('tax_query');
467
468 if( is_array( $already_existing_tax_query ) ){
469 foreach( $already_existing_tax_query as $value ){
470 if( $this->hasNestedQueries( $value ) ){
471 foreach( $value as $n => $nested_tax_query ){
472 $this->addTaxQueryArray( $nested_tax_query );
473 }
474 }
475 $this->addTaxQueryArray( $value );
476 }
477 }
478
479 if( count($this->new_tax_query) > 1 ){
480 $this->new_tax_query['relation'] = 'AND';
481 }
482
483 $wp_query->set('tax_query', $this->new_tax_query );
484 $this->new_tax_query = [];
485
486 return $wp_query;
487 }
488 }