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 / PostDateEntity.php

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

567 lines 18.2 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 PostDateEntity implements Entity
10 {
11 public $items = [];
12
13 public $entityName = '';
14
15 public $excludedTerms = [];
16
17 public $isInclude = false;
18
19 public $new_date_query = [];
20
21 public $postTypes = [];
22
23 public $from = false;
24
25 public $to = false;
26
27 public $time_from = false;
28
29 public $time_to = false;
30
31 public $date_type = '';
32
33 private $doRecalculate = '';
34
35 private $post_and_types = [];
36
37 public function __construct( $name, $postType )
38 {
39 /**
40 * @feature clean code from unused methods
41 */
42 // This object is being created two times
43 // One time without post type when RequestParser select all filter terms
44 // Second time with post type when Filters widget requires filter terms to display them
45 $this->entityName = $name;
46 $this->setPostTypes( array($postType) );
47 $this->setFromAndTo();
48
49 $this->getAllExistingTerms();
50 }
51
52 public function setPostTypes( $postTypes = [] )
53 {
54 if ( isset( $postTypes[0] ) ) {
55 $this->postTypes = $postTypes;
56 }
57 }
58
59 public static function inputName( $slug, $edge = 'from' )
60 {
61 return $slug . '_' . $edge;
62 }
63
64 public function setExcludedTerms($excludedTerms, $isInclude)
65 {
66 $this->excludedTerms = $excludedTerms;
67 $this->isInclude = $isInclude;
68 }
69
70 public function getName()
71 {
72 return $this->entityName;
73 }
74
75 function excludeTerms($terms)
76 {
77 return $terms;
78 }
79
80 function getTerms()
81 {
82 return $this->excludeTerms($this->getAllExistingTerms());
83 }
84
85 /**
86 * @param int $id term id
87 * @return false|object term object of false
88 */
89 public function getTerm( $termId )
90 {
91 if (!$termId) {
92 return false;
93 }
94
95 foreach ($this->getTerms() as $term) {
96 if ($termId == $term->term_id) {
97 return $term;
98 }
99 }
100
101 return false;
102 }
103
104 public function getTermId($slug)
105 {
106 /**
107 * Post meta num value has no typical ID, so slug will be instead
108 */
109 return $slug . '_' . $this->getName();
110 }
111
112 /**
113 * @return array list of term_id and names useful to create Select dropdown
114 */
115 public function getTermsForSelect()
116 {
117 $toSelect = [];
118 foreach ($this->getTerms() as $term) {
119 $toSelect[$term->slug] = $term->name;
120 }
121
122 return $toSelect;
123 }
124
125 public function getTermsForSelect2()
126 {
127 $toSelect = [];
128 foreach ($this->getTerms() as $term) {
129 $toSelect[] = array( 'id' => $term->slug, 'text' => $term->name );
130 }
131
132 return $toSelect;
133 }
134
135 function getAllExistingTerms( $force = false )
136 {
137 if (empty($this->items) || $force) {
138 $this->items = $this->selectTerms();
139 }
140 return $this->items;
141 }
142
143 function populateTermsWithPostIds( $setId, $post_type )
144 {
145 // Does nothing. It was already done before.
146 }
147
148 protected function queryTerms()
149 { global $wpdb;
150 // @todo delete appropriate transient in resetTransitions();
151 $IN = false;
152 $translatable_post_type_exists = false;
153 $lang = '';
154 $key_in = '';
155
156 /**
157 * Check if any post type is translatable
158 */
159 if ( flrt_wpml_active() && defined('ICL_LANGUAGE_CODE') ) {
160 $lang = ICL_LANGUAGE_CODE;
161 $wpml_settings = get_option('icl_sitepress_settings');
162
163 foreach ($this->postTypes as $type) {
164 if (isset($wpml_settings['custom_posts_sync_option'][$type])) {
165 if ($wpml_settings['custom_posts_sync_option'][$type] === '1') {
166 $translatable_post_type_exists = true;
167 break;
168 }
169 }
170 }
171 }
172
173 /**
174 * Set Post types
175 */
176 if (!empty($this->postTypes) && isset($this->postTypes[0]) && $this->postTypes[0]) {
177 foreach ($this->postTypes as $postType) {
178 $key_in .= '_' . $postType;
179 $pieces[] = $wpdb->prepare("%s", $postType );
180 }
181
182 $IN = implode(", ", $pieces);
183 }
184
185 /**
186 * Set transient key
187 */
188 $transient_key = flrt_get_terms_transient_key($this->getName() . $key_in . $lang);
189
190 if ( false === ( $result = flrt_get_transient( $transient_key ) ) ) {
191 // Get all post meta values
192 $sql[] = "SELECT {$wpdb->posts}.ID,{$wpdb->posts}.post_date,{$wpdb->posts}.post_type";
193 $sql[] = "FROM {$wpdb->posts}";
194
195 /**
196 * If post type is translatable with WPML, get post meta values only with current language
197 */
198 if (flrt_wpml_active() && $lang && $translatable_post_type_exists) {
199 $sql[] = "LEFT JOIN {$wpdb->prefix}icl_translations AS wpml_translations";
200 $sql[] = "ON {$wpdb->posts}.ID = wpml_translations.element_id";
201
202 if (!empty($this->postTypes)) {
203
204 $sql[] = "AND wpml_translations.element_type IN(";
205
206 foreach ($this->postTypes as $type) {
207 $LANG_IN[] = $wpdb->prepare("CONCAT('post_', '%s')", $type);
208 }
209 $sql[] = implode(",", $LANG_IN);
210
211 $sql[] = ")";
212 }
213 }
214
215 $sql[] = "WHERE 1=1";
216
217 if ($IN) {
218 $sql[] = "AND {$wpdb->posts}.post_type IN( {$IN} )";
219 }
220
221 if (flrt_wpml_active() && $lang && $translatable_post_type_exists) {
222 $sql[] = $wpdb->prepare("AND wpml_translations.language_code = '%s'", $lang);
223 }
224
225 /**
226 * Filters terms SQL-query and allows to modify it
227 */
228 $sql = apply_filters('wpc_filter_get_post_date_terms_sql', $sql, $this->postTypes);
229
230 $sql = implode(' ', $sql);
231
232 $result = $wpdb->get_results($sql, ARRAY_A);
233
234 flrt_set_transient( $transient_key, $result, FLRT_TRANSIENT_PERIOD_HOURS * HOUR_IN_SECONDS );
235 }
236
237 return $result;
238 }
239
240 /**
241 * Selects all post dates within specified post_type.
242 * Also generates $min and $max terms with list of posts belong to them.
243 * If the $alreadyFilteredPosts specified it limits selected posts to
244 * specified in the variable.
245 *
246 * @param array $alreadyFilteredPosts
247 */
248 public function selectTerms( $alreadyFilteredPosts = [] )
249 {
250 /**
251 * Do not query post dates from DB without post type
252 */
253 if ( ! $this->postTypes[0] ) {
254 return [];
255 }
256
257 $new_result = [];
258 $new_time_result = [];
259 // $from_and_to = [
260 // 'from' => 0,
261 // 'to' => 0
262 // ];
263
264 $result = $this->queryTerms();
265 $doRecalculate = $this->doRecalculate( $alreadyFilteredPosts );
266
267 if ( ! empty( $result ) && $doRecalculate ) {
268
269 $postsIn_flipped = array_flip( $alreadyFilteredPosts );
270
271 foreach ( $result as $single_post ) {
272 /**
273 * If there are already filtered posts, we have to skip posts
274 * that are out of the queried list
275 */
276 if ( ! empty( $alreadyFilteredPosts ) ) {
277 if ( ! isset( $postsIn_flipped[$single_post['ID']] ) ) {
278 continue;
279 }
280 }
281
282 if (empty($single_post['post_date'])){
283 continue;
284 }
285
286 /**
287 * We have to generate and fill two arrays
288 * First to detect $min and $max values
289 * Second to map post_types with post IDs
290 */
291 $new_result[] = $single_post['post_date'];
292 $new_time_result[] = flrt_clean_date_time( $single_post['post_date'], 'time' );
293
294 if ( $this->from !== false && flrt_clean_date_time( $single_post['post_date'], $this->date_type ) < $this->from ){
295 continue;
296 }
297
298 if ( $this->to !== false && flrt_clean_date_time( $single_post['post_date'], $this->date_type ) > $this->to ){
299 continue;
300 }
301
302 $this->post_and_types[$single_post['ID']] = $single_post['post_type'];
303 }
304 }
305
306 if ( ! empty( $new_result ) ) {
307 // We need to find minimum and maximum date values
308 $from_and_to = [
309 'from' => apply_filters( 'wpc_set_date_shift', min( $new_result ), $this->getName() ),
310 'to' => apply_filters( 'wpc_set_date_shift', max( $new_result ), $this->getName() ),
311 ];
312
313 $time_from_and_to = [
314 'from' => apply_filters( 'wpc_set_date_shift', min( $new_time_result ), $this->getName() ),
315 'to' => apply_filters( 'wpc_set_date_shift', max( $new_time_result ), $this->getName() ),
316 ];
317
318 } else {
319 // if we didn't recalculate items, it means we have them already stored in the object
320 return $this->items;
321 }
322
323 $from_and_to = apply_filters( 'wpc_set_from_to', $from_and_to, $this->getName() );
324 $x = $this->convertSelectResult( $from_and_to, $time_from_and_to );
325
326 return $x;
327 }
328
329 public function convertSelectResult( $result, $time_from_and_to ){
330 $return = [];
331
332 if( ! is_array( $result ) ){
333 return $return;
334 }
335
336 // To make standard format for terms array;
337 $i = 1;
338 $wpManager = Container::instance()->getWpManager();
339 $queried_values = $wpManager->getQueryVar( 'queried_values' );
340
341 foreach ( $result as $edge => $value ){
342 $time_edge = 'time_'.$edge;
343
344 $termObject = new \stdClass();
345 $termObject->slug = $edge;
346 $termObject->name = $this->createTermName( $edge, $value, $queried_values );
347 $termObject->term_id = $edge . '_' . $this->getName();
348 $termObject->posts = array_keys( $this->post_and_types );
349 $termObject->count = 0;
350 $termObject->cross_count = 0;
351 $termObject->post_types = $this->post_and_types; //[];
352 $termObject->$edge = $value;
353 $termObject->$time_edge = $time_from_and_to[$edge];
354 $termObject->wp_queried = false;
355
356 $return[ $edge ] = $termObject;
357
358 $i++;
359 }
360
361 return $return;
362 }
363
364 protected function createTermName( $edge, $value, $queried_values )
365 {
366 $queriedFilter = false;
367 if( $edge === 'from' ) {
368 $name = esc_html__('After', 'filter-everything' );
369 } else if( $edge === 'to' ) {
370 $name = esc_html__('Before', 'filter-everything' );
371 }
372
373 if( $queried_values ){
374 foreach ( $queried_values as $slug => $filter ){
375 if( $filter['e_name'] === $this->getName() ){
376 $queriedFilter = $filter;
377 break;
378 }
379 }
380 }
381
382 if( isset( $queriedFilter['values'][$edge] ) ) {
383 $format = $queriedFilter['date_format'];
384 $date_type = false;
385
386 if ( isset( $queriedFilter['values']['from'] ) ) {
387 $date_type = flrt_detect_date_type( $queriedFilter['values']['from'] );
388 } else if ( isset( $queriedFilter['values']['to'] ) ) {
389 $date_type = flrt_detect_date_type($queriedFilter['values']['to']);
390 }
391
392 // IN case if we have several date filters on the same page
393 if ( in_array( $date_type, [ 'date', 'time' ] ) ) {
394 $maybe_split_format = flrt_split_date_time( $format );
395 if ( isset( $maybe_split_format[$date_type] ) ) {
396 $format = $maybe_split_format[$date_type];
397 }
398 }
399
400 $name = $name .' '. flrt_apply_date_format( $queriedFilter['values'][$edge], $format );
401 }else{
402 $name = $name .' '. $value;
403 }
404
405 return apply_filters( 'wpc_filter_post_date_term_name', $name, $this->getName() );
406 }
407
408 protected function doRecalculate($alreadyFilteredPosts )
409 {
410 $do_filtered_posts = md5( json_encode( $alreadyFilteredPosts ) );
411 $do_from = md5( $this->from );
412 $do_to = md5( $this->to );
413
414 $result = md5( $do_filtered_posts . $do_from . $do_to );
415
416 if ( $result === $this->doRecalculate ) {
417 return false;
418 }
419
420 $this->doRecalculate = $result;
421 return true;
422 }
423
424 /**
425 * Sets $this->from and $this->to values if there are queried date filter(s)
426 */
427 private function setFromAndTo()
428 {
429 $wpManager = Container::instance()->getWpManager();
430 $queried_values = $wpManager->getQueryVar( 'queried_values', [] );
431 $filter_slug = false;
432
433 /**
434 * Check if this filter was queried
435 */
436 foreach ( $queried_values as $slug => $filter ) {
437 /**
438 * At this point we do not know what exactly filter was queired
439 * because filters with the same slug can be in multiple Filter Sets with
440 * different date_type values.
441 */
442 if ( $filter['e_name'] === $this->getName() ) {
443 $filter_slug = $slug;
444 break;
445 }
446 }
447
448 /**
449 * If this filter was queried we have to receive its $from and $to values
450 */
451 if ( $filter_slug ) {
452 if ( isset( $queried_values[$filter_slug]['values']['to'] ) && $this->to === false ) {
453 $to = str_replace( '.', ':', $queried_values[$filter_slug]['values']['to'] );
454 $this->date_type = flrt_detect_date_type( $to );
455 $this->to = $this->time_to = apply_filters( 'wpc_unset_date_shift', $to, $this->getName(), $this->date_type );
456 if ( $this->date_type === 'time' ) {
457 $this->time_to = $this->to;
458 }
459 }
460
461 if ( isset( $queried_values[$filter_slug]['values']['from'] ) && $this->from === false ) {
462 $from = str_replace( '.', ':', $queried_values[$filter_slug]['values']['from'] );
463 $this->date_type = flrt_detect_date_type( $from );
464 $this->from = apply_filters( 'wpc_unset_date_shift', $from, $this->getName(), $this->date_type );
465 if ( $this->date_type === 'time' ) {
466 $this->time_from = $this->from;
467 }
468 }
469 }
470 }
471
472 /**
473 * Updates object $this->items in accordance with already queried posts
474 * @param $postsIn
475 */
476 public function updateMinAndMaxValues( $postsIn )
477 {
478 if ( ! empty( $this->items ) && ! empty( $postsIn ) ) {
479 $newItems = $this->selectTerms( $postsIn );
480
481 foreach ( $this->items as $index => $term ) {
482 $time_index = 'time_'.$index;
483 if ( isset( $this->items[$index]->$index ) ) {
484 $this->items[$index]->$index = $newItems[$index]->$index;
485 $this->items[$index]->$time_index = $newItems[$index]->$time_index;
486 }
487 }
488 }
489 }
490
491 /**
492 * @return object WP_Query
493 */
494 public function addTermsToWpQuery( $queried_value, $wp_query )
495 {
496 $wpc_date_query = [];
497 $possible_date_query = $wp_query->get( 'date_query' );
498
499 if( ! empty( $possible_date_query ) ) {
500 $this->new_date_query = $possible_date_query;
501 }
502 /**
503 * @todo Nested date queries?
504 */
505 $from = isset( $queried_value['values']['from'] ) ? $queried_value['values']['from'] : false;
506 $to = isset( $queried_value['values']['to'] ) ? $queried_value['values']['to'] : false;
507
508 if ( $from !== false ) {
509
510 $date_time_pieces = date_parse( $from );
511
512 foreach ( [ 'year', 'month', 'day', 'hour', 'minute', 'second' ] as $item) {
513 if ( $date_time_pieces[$item] !== false ) {
514 $date_from_query[$item] = $date_time_pieces[$item];
515 }
516 }
517
518 // It means datetime query
519 if ( count( $date_from_query ) > 3 ) {
520 $wpc_date_query['from'] = $from;
521 } else {
522 // either date or time request
523 if( isset( $date_from_query['year'] ) && isset( $date_from_query['month'] ) && isset( $date_from_query['day'] ) ) {
524 $this->new_date_query['after'] = $date_from_query;
525 $this->new_date_query['inclusive'] = true;
526 } else {
527 $date_from_query['compare'] = '>=' ;
528 $this->new_date_query[] = $date_from_query;
529 }
530 }
531 }
532
533 if( $to !== false ){
534 $date_time_pieces = date_parse( $to );
535
536 foreach ( [ 'year', 'month', 'day', 'hour', 'minute', 'second' ] as $item) {
537 if ( $date_time_pieces[$item] !== false ) {
538 $date_to_query[$item] = $date_time_pieces[$item];
539 }
540 }
541 // It means datetime query
542 if ( count( $date_to_query ) > 3 ) {
543 $wpc_date_query['to'] = $to;
544 } else {
545 if ( isset( $date_to_query['year'] ) && isset( $date_to_query['month'] ) && isset( $date_to_query['day'] ) ) {
546 $this->new_date_query['before'] = $date_to_query;
547 $this->new_date_query['inclusive'] = true;
548 } else {
549 $date_to_query['compare'] = '<=' ;
550 $this->new_date_query[] = $date_to_query;
551 }
552 }
553 }
554
555 if( ! empty( $this->new_date_query ) ) {
556 $wp_query->set( 'date_query', $this->new_date_query );
557 }
558
559 if ( ! empty( $wpc_date_query ) ) {
560 $wp_query->set( 'wpc_date_query', $wpc_date_query );
561 }
562
563 $this->new_date_query = [];
564
565 return $wp_query;
566 }
567 }