From 6c1b358b5f9169870770a5348573783cbe751f0a Mon Sep 17 00:00:00 2001 From: Jake Oehler Morrison Date: Mon, 21 Oct 2019 17:07:25 +0100 Subject: [PATCH 1/5] Refactor `get_job_listings()` to a class --- ...wp-job-manager-listing-query-generator.php | 371 +++++++++++++++++ .../class-wp-job-manager-cache-helper.php | 1 + .../class-wp-job-manager-query-generator.php | 372 ++++++++++++++++++ includes/class-wp-job-manager.php | 2 + wp-job-manager-functions.php | 208 +--------- 5 files changed, 749 insertions(+), 205 deletions(-) create mode 100644 includes/abstracts/abstract-wp-job-manager-listing-query-generator.php create mode 100644 includes/class-wp-job-manager-query-generator.php diff --git a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php new file mode 100644 index 000000000..426db8355 --- /dev/null +++ b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php @@ -0,0 +1,371 @@ +args = self::parse_args( $args ); + $this->use_cache = $use_cache; + + $this->init(); + } + + /** + * Salt used for the cache key. + * + * @return string + */ + abstract protected function get_cache_key_salt(); + + /** + * Get the query arguments used for `WP_Query`. + * + * @return array + */ + abstract protected function get_query_args(); + + /** + * Get the default arguments used to generate the query. + * + * @return array + */ + abstract protected static function get_default_args(); + + /** + * Run any tasks that need to be done right away. + */ + protected function init() { + } + + /** + * Get the arguments used to generate the query. + * + * @return array + */ + public function get_args() { + return $this->args; + } + + /** + * Fires before the query is generated. Can prevent normal generation of `WP_Query` by returning one of its own. + * + * @return null|WP_Query + */ + protected function before_query() { + return null; + } + + /** + * Fires after the query is generated. + */ + protected function after_query() { + } + + /** + * Helper function to generate the `orderby` query argument. + * + * @return array|string + */ + protected function parse_orderby() { + $args = $this->get_args(); + + if ( ! empty( $args['orderby'] ) ) { + if ( 'featured' === $args['orderby'] ) { + return [ + 'menu_order' => 'ASC', + 'date' => 'DESC', + 'ID' => 'DESC', + ]; + } + + if ( 'rand_featured' === $args['orderby'] ) { + return [ + 'menu_order' => 'ASC', + 'rand' => 'ASC', + ]; + } + + return $args['orderby']; + } + + // By default, order by date and then ID. + return [ + 'date' => 'DESC', + 'ID' => 'DESC', + ]; + } + + /** + * Returns the default order direction. By default, this will be ignored + * + * @return string + */ + protected function parse_order() { + $args = $this->get_args(); + + if ( isset( $args['order'] ) ) { + return $args['order']; + } + + return 'DESC'; + } + + /** + * Parses the `offset` argument used in the query. + * + * @return int + */ + protected function parse_offset() { + $args = $this->get_args(); + + if ( isset( $args['offset'] ) ) { + return absint( $args['offset'] ); + } + + return 0; + } + + /** + * Parses the `posts_per_page` argument used in the query. + * + * @return int + */ + protected function parse_posts_per_page() { + $args = $this->get_args(); + + if ( isset( $args['posts_per_page'] ) ) { + return intval( $args['posts_per_page'] ); + } + + return 20; + } + + /** + * Parses the `no_found_rows` argument used in the query. + * + * @return bool|null + */ + protected function parse_no_found_rows() { + $args = $this->get_args(); + + if ( $args['posts_per_page'] < 0 ) { + return true; + } + + return null; + } + + /** + * Parses the `fields` argument used in the query. + * + * @return string + */ + protected function parse_fields() { + $args = $this->get_args(); + + if ( ! empty( $args['fields'] ) ) { + return $args['fields']; + } + + return 'all'; + } + + /** + * Returns the query object with the result of the query. + * + * @return WP_Query + */ + public function get_query() { + $this->query = false; + $query = $this->before_query(); + $this->did_use_cache = false; + + if ( $query instanceof WP_Query ) { + $this->query = $query; + }elseif ( $this->use_cache ) { + $this->query = $this->get_cached_query(); + $this->did_use_cache = true; + } + + if ( false === $this->query ) { + $this->query = new WP_Query( $this->get_query_args() ); + $this->save_cache(); + } + + $this->after_query(); + + return $this->query; + } + + /** + * True if we actually used the cache when fetching the listing results. + * + * @return bool + */ + public function did_use_cache() { + return $this->did_use_cache; + } + + /** + * Perform tasks on cached query result. Can be extended by child classes. + * + * @param WP_Query $query Query that was just re-hydrated from a cached result. + * @return WP_Query + */ + protected function after_cache_hydration( WP_Query $query ) { + $args = $this->get_args(); + + // Random order is cached so shuffle them. Note: This doesn't really work with pagination. + if ( 'rand_featured' === $args['orderby'] ) { + usort( + $query->posts, + /** + * Helper function to maintain featured status when shuffling results. + * + * @param WP_Post $a + * @param WP_Post $b + * + * @return bool + */ + function ( $a, $b ) { + if ( -1 === $a->menu_order || -1 === $b->menu_order ) { + // Left is featured. + if ( 0 === $b->menu_order ) { + return -1; + } + // Right is featured. + if ( 0 === $a->menu_order ) { + return 1; + } + } + return wp_rand( -1, 1 ); + } + ); + } elseif ( 'rand' === $args['orderby'] ) { + shuffle( $query->posts ); + } + + return $query; + } + + /** + * Check for results in cache. + * + * @return bool|WP_Query + */ + private function get_cached_query() { + $cache_key = $this->get_cache_key(); + $query_args = $this->get_query_args(); + $cached_query_posts = get_transient( $cache_key ); + + if ( + $cached_query_posts + && is_object( $cached_query_posts ) + && isset( $cached_query_posts->max_num_pages ) + && isset( $cached_query_posts->found_posts ) + && isset( $cached_query_posts->posts ) + && is_array( $cached_query_posts->posts ) + ) { + if ( in_array( $query_args['fields'], [ 'ids', 'id=>parent' ], true ) ) { + // For these special requests, just return the array of results as set. + $posts = $cached_query_posts->posts; + } else { + $posts = array_map( 'get_post', $cached_query_posts->posts ); + } + + $result = new WP_Query(); + $result->parse_query( $query_args ); + $result->posts = $posts; + $result->found_posts = intval( $cached_query_posts->found_posts ); + $result->max_num_pages = intval( $cached_query_posts->max_num_pages ); + $result->post_count = count( $posts ); + + $result = $this->after_cache_hydration( $result ); + + return $result; + } + + return false; + } + + /** + * Save the query result to cache. + */ + private function save_cache() { + if ( ! ( $this->query instanceof WP_Query ) || ! $this->use_cache ) { + return; + } + + $cacheable_result = []; + $cacheable_result['posts'] = array_values( $this->query->posts ); + $cacheable_result['found_posts'] = $this->query->found_posts; + $cacheable_result['max_num_pages'] = $this->query->max_num_pages; + + set_transient( $this->get_cache_key(), wp_json_encode( $cacheable_result ), DAY_IN_SECONDS ); + } + + /** + * Get the key to use for caching results. + * + * @return string + */ + private function get_cache_key() { + return WP_Job_Manager_Cache_Helper::CACHE_PREFIX . md5( wp_json_encode( $this->get_query_args() ) . $this->get_cache_key_salt() ); + } + + /** + * Parses the arguments used to generated the query. + * + * @param array $args Arguments used to generate the query. + * @return array + */ + private static function parse_args( $args ) { + return wp_parse_args( $args, static::get_default_args() ); + } + +} diff --git a/includes/class-wp-job-manager-cache-helper.php b/includes/class-wp-job-manager-cache-helper.php index ad9732fb6..f6847ba6d 100644 --- a/includes/class-wp-job-manager-cache-helper.php +++ b/includes/class-wp-job-manager-cache-helper.php @@ -16,6 +16,7 @@ * @since 1.0.0 */ class WP_Job_Manager_Cache_Helper { + const CACHE_PREFIX = 'jm_ '; /** * Initializes cache hooks. diff --git a/includes/class-wp-job-manager-query-generator.php b/includes/class-wp-job-manager-query-generator.php new file mode 100644 index 000000000..e641fdf8e --- /dev/null +++ b/includes/class-wp-job-manager-query-generator.php @@ -0,0 +1,372 @@ +get_args(); + + /** + * Perform actions that need to be done prior to the start of the job listings query. + * + * @since 1.26.0 + * + * @param array $args Arguments used to retrieve job listings. + */ + do_action( 'get_job_listings_init', $args ); + } + + /** + * Get the query arguments used for `WP_Query`. + * + * @return array + */ + protected function get_query_args() { + if ( null !== $this->query_args ) { + return $this->query_args; + } + + $args = $this->get_args(); + $this->query_args = self::get_default_query_args(); + + // Common arguments. + $this->query_args['order'] = $this->parse_order(); + $this->query_args['orderby'] = $this->parse_orderby(); + $this->query_args['offset'] = $this->parse_offset(); + $this->query_args['posts_per_page'] = $this->parse_posts_per_page(); + $this->query_args['fields'] = $this->parse_fields(); + $this->query_args['no_found_rows'] = $this->parse_no_found_rows(); + + // Job listing specific arguments. + $this->query_args['s'] = $this->parse_keyword(); + $this->query_args['post_status'] = $this->parse_post_status(); + $this->query_args['meta_query'] = $this->generate_meta_query(); + $this->query_args['tax_query'] = $this->generate_tax_query(); + + /** This filter is documented in wp-job-manager.php */ + $this->query_args['lang'] = apply_filters( 'wpjm_lang', null ); + + /** + * Filter the query arguments used. + * + * @param array $query_args Query arguments used for `WP_Query`. + * @param array $args Arguments used for generating the current query arguments. + */ + $this->query_args = apply_filters( 'job_manager_get_listings', $this->query_args, $args ); + + // Cleanup. + $remove_empty = [ 'meta_query', 'tax_query' ]; + $remove_null = [ 'no_found_rows', 's' ]; + + foreach ( $remove_empty as $query_key ) { + if ( empty( $this->query_args[ $query_key ] ) ) { + unset( $this->query_args[ $query_key ] ); + } + } + + foreach ( $remove_null as $query_key ) { + if ( null === $this->query_args[ $query_key ] ) { + unset( $this->query_args[ $query_key ] ); + } + } + + return $this->query_args; + } + + /** + * Adds join and where query for keywords. + * + * @since 1.35.0 + * + * @param string $search + * @return string + */ + public function get_job_listings_keyword_search( $search ) { + global $wpdb; + + $query_args = $this->get_query_args(); + $job_manager_keyword = $query_args['s']; + + // Searchable Meta Keys: set to empty to search all meta keys. + $searchable_meta_keys = [ + '_job_location', + '_company_name', + '_application', + '_company_name', + '_company_tagline', + '_company_website', + '_company_twitter', + ]; + + $searchable_meta_keys = apply_filters( 'job_listing_searchable_meta_keys', $searchable_meta_keys ); + + // Set Search DB Conditions. + $conditions = []; + + // Search Post Meta. + if ( apply_filters( 'job_listing_search_post_meta', true ) ) { + + // Only selected meta keys. + if ( $searchable_meta_keys ) { + $conditions[] = "{$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key IN ( '" . implode( "','", array_map( 'esc_sql', $searchable_meta_keys ) ) . "' ) AND meta_value LIKE '%" . esc_sql( $job_manager_keyword ) . "%' )"; + } else { + // No meta keys defined, search all post meta value. + $conditions[] = "{$wpdb->posts}.ID IN ( SELECT post_id FROM {$wpdb->postmeta} WHERE meta_value LIKE '%" . esc_sql( $job_manager_keyword ) . "%' )"; + } + } + + // Search taxonomy. + $conditions[] = "{$wpdb->posts}.ID IN ( SELECT object_id FROM {$wpdb->term_relationships} AS tr LEFT JOIN {$wpdb->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id LEFT JOIN {$wpdb->terms} AS t ON tt.term_id = t.term_id WHERE t.name LIKE '%" . esc_sql( $job_manager_keyword ) . "%' )"; + + /** + * Filters the conditions to use when querying job listings. Resulting array is joined with OR statements. + * + * @since 1.26.0 + * + * @param array $conditions Conditions to join by OR when querying job listings. + * @param string $job_manager_keyword Search query. + */ + $conditions = apply_filters( 'job_listing_search_conditions', $conditions, $job_manager_keyword ); + if ( empty( $conditions ) ) { + return $search; + } + + $conditions_str = implode( ' OR ', $conditions ); + + if ( ! empty( $search ) ) { + $search = preg_replace( '/^ AND /', '', $search ); + $search = " AND ( {$search} OR ( {$conditions_str} ) )"; + } else { + $search = " AND ( {$conditions_str} )"; + } + + return $search; + } + + /** + * Generates the meta query used by `WP_Query`. + * + * @return array + */ + private function generate_meta_query() { + $args = $this->get_args(); + $meta_query = []; + + if ( ! empty( $args['search_location'] ) ) { + $location_meta_keys = [ 'geolocation_formatted_address', '_job_location', 'geolocation_state_long' ]; + $location_search = [ 'relation' => 'OR' ]; + foreach ( $location_meta_keys as $meta_key ) { + $location_search[] = [ + 'key' => $meta_key, + 'value' => $args['search_location'], + 'compare' => 'like', + ]; + } + $meta_query[] = $location_search; + } + + if ( ! is_null( $args['featured'] ) ) { + $meta_query[] = [ + 'key' => '_featured', + 'value' => '1', + 'compare' => $args['featured'] ? '=' : '!=', + ]; + } + + if ( ! is_null( $args['filled'] ) || 1 === absint( get_option( 'job_manager_hide_filled_positions' ) ) ) { + $meta_query[] = [ + 'key' => '_filled', + 'value' => '1', + 'compare' => $args['filled'] ? '=' : '!=', + ]; + } + + return $meta_query; + } + + /** + * Generates the taxonomy query used by `WP_Query`. + * + * @return array + */ + private function generate_tax_query() { + $args = $this->get_args(); + $tax_query = []; + + if ( ! empty( $args['job_types'] ) ) { + $tax_query[] = [ + 'taxonomy' => 'job_listing_type', + 'field' => 'slug', + 'terms' => $args['job_types'], + ]; + } + + if ( ! empty( $args['search_categories'] ) ) { + $field = is_numeric( $args['search_categories'][0] ) ? 'term_id' : 'slug'; + $operator = 'all' === get_option( 'job_manager_category_filter_type', 'all' ) && count( $args['search_categories'] ) > 1 ? 'AND' : 'IN'; + $tax_query[] = [ + 'taxonomy' => 'job_listing_category', + 'field' => $field, + 'terms' => array_values( $args['search_categories'] ), + 'include_children' => 'AND' !== $operator, + 'operator' => $operator, + ]; + } + + return $tax_query; + } + + /** + * Parses the `s` argument used in the query. + * + * @return string|null + */ + private function parse_keyword() { + $args = $this->get_args(); + $job_manager_keyword = sanitize_text_field( $args['search_keywords'] ); + + if ( ! empty( $job_manager_keyword ) && strlen( $job_manager_keyword ) >= apply_filters( 'job_manager_get_listings_keyword_length_threshold', 2 ) ) { + return $job_manager_keyword; + } + + return null; + } + + /** + * Parses the `post_status` argument used in the query. + * + * @return string|array + */ + private function parse_post_status() { + $args = $this->get_args(); + + if ( ! empty( $args['post_status'] ) ) { + return $args['post_status']; + } elseif ( 0 === intval( get_option( 'job_manager_hide_expired', get_option( 'job_manager_hide_expired_content', 1 ) ) ) ) { + return [ 'publish', 'expired' ]; + } + + return 'publish'; + } + + /** + * Fires before the query is generated. Can prevent normal generation of `WP_Query` by returning one of its own. + * + * @return void|WP_Query + */ + protected function before_query() { + $args = $this->get_args(); + $query_args = $this->get_query_args(); + + if ( ! empty( $query_args['s'] ) ) { + add_filter( 'posts_search', [ $this, 'get_job_listings_keyword_search' ] ); + } + + /** + * Runs right before job listings are retrieved. + * + * @since 1.11.0 + * @since 1.35.0 Added query generator object parameter. + * + * @param array $query_args Query arguments used for `WP_Query`. + * @param array $args Arguments used for generating the current query arguments. + * @param WP_Job_Manager_Query_Generator $query_generator Query generator object used to generate this `WP_Query`. + */ + do_action( 'before_get_job_listings', $query_args, $args, $this ); + } + + + /** + * Fires after the query is generated. + */ + protected function after_query() { + $args = $this->get_args(); + $query_args = $this->get_query_args(); + + remove_filter( 'posts_search', [ $this, 'get_job_listings_keyword_search' ] ); + + /** + * Runs right before job listings are retrieved. + * + * @since 1.11.0 + * @since 1.35.0 Added query generator object parameter. + * + * @param array $query_args Query arguments used for `WP_Query`. + * @param array $args Arguments used for generating the current query arguments. + * @param WP_Job_Manager_Query_Generator $query_generator Query generator object used to generate this `WP_Query`. + */ + do_action( 'after_get_job_listings', $query_args, $args, $this ); + } + + /** + * Get the default query arguments used for this `WP_Query`. + * + * @return array + */ + private static function get_default_query_args() { + return [ + 'post_type' => 'job_listing', + 'ignore_sticky_posts' => 1, + 'tax_query' => [], + 'meta_query' => [], + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false, + 'cache_results' => false, + 'fields' => 'all', + ]; + } + + /** + * Get the default arguments used to generate the query. + * + * @return array + */ + protected static function get_default_args() { + return [ + 'search_location' => '', + 'search_keywords' => '', + 'search_categories' => [], + 'job_types' => [], + 'post_status' => [], + 'offset' => 0, + 'posts_per_page' => 20, + 'orderby' => 'date', + 'order' => 'DESC', + 'featured' => null, + 'filled' => null, + 'fields' => 'all', + ]; + } +} diff --git a/includes/class-wp-job-manager.php b/includes/class-wp-job-manager.php index 53a6d5200..e1da6cb0c 100644 --- a/includes/class-wp-job-manager.php +++ b/includes/class-wp-job-manager.php @@ -58,8 +58,10 @@ public function __construct() { include_once JOB_MANAGER_PLUGIN_DIR . '/includes/helper/class-wp-job-manager-helper.php'; include_once JOB_MANAGER_PLUGIN_DIR . '/includes/abstracts/abstract-wp-job-manager-email.php'; include_once JOB_MANAGER_PLUGIN_DIR . '/includes/abstracts/abstract-wp-job-manager-email-template.php'; + include_once JOB_MANAGER_PLUGIN_DIR . '/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php'; include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-email-notifications.php'; include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-data-exporter.php'; + include_once JOB_MANAGER_PLUGIN_DIR . '/includes/class-wp-job-manager-query-generator.php'; if ( is_admin() ) { include_once JOB_MANAGER_PLUGIN_DIR . '/includes/admin/class-wp-job-manager-admin.php'; diff --git a/wp-job-manager-functions.php b/wp-job-manager-functions.php index da8a90c0c..850d75231 100644 --- a/wp-job-manager-functions.php +++ b/wp-job-manager-functions.php @@ -16,213 +16,11 @@ * @return WP_Query */ function get_job_listings( $args = [] ) { - global $job_manager_keyword; + $use_cache = apply_filters( 'get_job_listings_cache_results', true ); - $args = wp_parse_args( - $args, - [ - 'search_location' => '', - 'search_keywords' => '', - 'search_categories' => [], - 'job_types' => [], - 'post_status' => [], - 'offset' => 0, - 'posts_per_page' => 20, - 'orderby' => 'date', - 'order' => 'DESC', - 'featured' => null, - 'filled' => null, - 'fields' => 'all', - ] - ); - - /** - * Perform actions that need to be done prior to the start of the job listings query. - * - * @since 1.26.0 - * - * @param array $args Arguments used to retrieve job listings. - */ - do_action( 'get_job_listings_init', $args ); - - if ( ! empty( $args['post_status'] ) ) { - $post_status = $args['post_status']; - } elseif ( 0 === intval( get_option( 'job_manager_hide_expired', get_option( 'job_manager_hide_expired_content', 1 ) ) ) ) { - $post_status = [ 'publish', 'expired' ]; - } else { - $post_status = 'publish'; - } - - $query_args = [ - 'post_type' => 'job_listing', - 'post_status' => $post_status, - 'ignore_sticky_posts' => 1, - 'offset' => absint( $args['offset'] ), - 'posts_per_page' => intval( $args['posts_per_page'] ), - 'orderby' => $args['orderby'], - 'order' => $args['order'], - 'tax_query' => [], - 'meta_query' => [], - 'update_post_term_cache' => false, - 'update_post_meta_cache' => false, - 'cache_results' => false, - 'fields' => $args['fields'], - ]; - - if ( $args['posts_per_page'] < 0 ) { - $query_args['no_found_rows'] = true; - } - - if ( ! empty( $args['search_location'] ) ) { - $location_meta_keys = [ 'geolocation_formatted_address', '_job_location', 'geolocation_state_long' ]; - $location_search = [ 'relation' => 'OR' ]; - foreach ( $location_meta_keys as $meta_key ) { - $location_search[] = [ - 'key' => $meta_key, - 'value' => $args['search_location'], - 'compare' => 'like', - ]; - } - $query_args['meta_query'][] = $location_search; - } - - if ( ! is_null( $args['featured'] ) ) { - $query_args['meta_query'][] = [ - 'key' => '_featured', - 'value' => '1', - 'compare' => $args['featured'] ? '=' : '!=', - ]; - } - - if ( ! is_null( $args['filled'] ) || 1 === absint( get_option( 'job_manager_hide_filled_positions' ) ) ) { - $query_args['meta_query'][] = [ - 'key' => '_filled', - 'value' => '1', - 'compare' => $args['filled'] ? '=' : '!=', - ]; - } - - if ( ! empty( $args['job_types'] ) ) { - $query_args['tax_query'][] = [ - 'taxonomy' => 'job_listing_type', - 'field' => 'slug', - 'terms' => $args['job_types'], - ]; - } - - if ( ! empty( $args['search_categories'] ) ) { - $field = is_numeric( $args['search_categories'][0] ) ? 'term_id' : 'slug'; - $operator = 'all' === get_option( 'job_manager_category_filter_type', 'all' ) && count( $args['search_categories'] ) > 1 ? 'AND' : 'IN'; - $query_args['tax_query'][] = [ - 'taxonomy' => 'job_listing_category', - 'field' => $field, - 'terms' => array_values( $args['search_categories'] ), - 'include_children' => 'AND' !== $operator, - 'operator' => $operator, - ]; - } - - if ( 'featured' === $args['orderby'] ) { - $query_args['orderby'] = [ - 'menu_order' => 'ASC', - 'date' => 'DESC', - 'ID' => 'DESC', - ]; - } - - if ( 'rand_featured' === $args['orderby'] ) { - $query_args['orderby'] = [ - 'menu_order' => 'ASC', - 'rand' => 'ASC', - ]; - } - - $job_manager_keyword = sanitize_text_field( $args['search_keywords'] ); - - if ( ! empty( $job_manager_keyword ) && strlen( $job_manager_keyword ) >= apply_filters( 'job_manager_get_listings_keyword_length_threshold', 2 ) ) { - $query_args['s'] = $job_manager_keyword; - add_filter( 'posts_search', 'get_job_listings_keyword_search' ); - } - - $query_args = apply_filters( 'job_manager_get_listings', $query_args, $args ); - - if ( empty( $query_args['meta_query'] ) ) { - unset( $query_args['meta_query'] ); - } - - if ( empty( $query_args['tax_query'] ) ) { - unset( $query_args['tax_query'] ); - } - - /** This filter is documented in wp-job-manager.php */ - $query_args['lang'] = apply_filters( 'wpjm_lang', null ); - - // Filter args. - $query_args = apply_filters( 'get_job_listings_query_args', $query_args, $args ); - - do_action( 'before_get_job_listings', $query_args, $args ); - - // Cache results. - if ( apply_filters( 'get_job_listings_cache_results', true ) ) { - $to_hash = wp_json_encode( $query_args ); - $query_args_hash = 'jm_' . md5( $to_hash . JOB_MANAGER_VERSION ) . WP_Job_Manager_Cache_Helper::get_transient_version( 'get_job_listings' ); - $result = false; - $cached_query_results = true; - $cached_query_posts = get_transient( $query_args_hash ); - if ( is_string( $cached_query_posts ) ) { - $cached_query_posts = json_decode( $cached_query_posts, false ); - if ( - $cached_query_posts - && is_object( $cached_query_posts ) - && isset( $cached_query_posts->max_num_pages ) - && isset( $cached_query_posts->found_posts ) - && isset( $cached_query_posts->posts ) - && is_array( $cached_query_posts->posts ) - ) { - if ( in_array( $query_args['fields'], [ 'ids', 'id=>parent' ], true ) ) { - // For these special requests, just return the array of results as set. - $posts = $cached_query_posts->posts; - } else { - $posts = array_map( 'get_post', $cached_query_posts->posts ); - } - - $result = new WP_Query(); - $result->parse_query( $query_args ); - $result->posts = $posts; - $result->found_posts = intval( $cached_query_posts->found_posts ); - $result->max_num_pages = intval( $cached_query_posts->max_num_pages ); - $result->post_count = count( $posts ); - } - } - - if ( false === $result ) { - $result = new WP_Query( $query_args ); - $cached_query_results = false; - - $cacheable_result = []; - $cacheable_result['posts'] = array_values( $result->posts ); - $cacheable_result['found_posts'] = $result->found_posts; - $cacheable_result['max_num_pages'] = $result->max_num_pages; - set_transient( $query_args_hash, wp_json_encode( $cacheable_result ), DAY_IN_SECONDS ); - } - - if ( $cached_query_results ) { - // random order is cached so shuffle them. - if ( 'rand_featured' === $args['orderby'] ) { - usort( $result->posts, '_wpjm_shuffle_featured_post_results_helper' ); - } elseif ( 'rand' === $args['orderby'] ) { - shuffle( $result->posts ); - } - } - } else { - $result = new WP_Query( $query_args ); - } - - do_action( 'after_get_job_listings', $query_args, $args ); - - remove_filter( 'posts_search', 'get_job_listings_keyword_search' ); + $query_generator = new WP_Job_Manager_Query_Generator( $args, $use_cache ); - return $result; + return $query_generator->get_query(); } endif; From 38e96801b12959aeaba2fba373b359b982223a7b Mon Sep 17 00:00:00 2001 From: Jake Oehler Morrison Date: Mon, 21 Oct 2019 19:26:10 +0100 Subject: [PATCH 2/5] No protected static methods in PHP 5.6 --- .../abstract-wp-job-manager-listing-query-generator.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php index 426db8355..7ab416059 100644 --- a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php +++ b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php @@ -72,9 +72,14 @@ abstract protected function get_query_args(); /** * Get the default arguments used to generate the query. * + * @abstract * @return array */ - abstract protected static function get_default_args(); + protected static function get_default_args() { + _doing_it_wrong( __METHOD__, __( 'This method should be implemented in the class.', 'wp-job-manager' ), '1.35.0' ); + + return []; + } /** * Run any tasks that need to be done right away. From bb5bf1523530d1fbcf1d69f1f66e08b64307a91b Mon Sep 17 00:00:00 2001 From: Jake Oehler Morrison Date: Tue, 22 Oct 2019 10:34:07 +0100 Subject: [PATCH 3/5] Force implementations to generate query args once --- ...wp-job-manager-listing-query-generator.php | 24 ++++++++- .../class-wp-job-manager-query-generator.php | 51 ++++++++----------- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php index 7ab416059..827879883 100644 --- a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php +++ b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php @@ -14,6 +14,13 @@ * Generates the query used to fetch posts for Job Manager and extensions. */ abstract class WP_Job_Manager_Listing_Query_Generator { + /** + * Query arguments used to create the `WP_Query` object. + * + * @var array + */ + private $query_args; + /** * Arguments used to generate the query. Set on instantiation. * @@ -63,11 +70,11 @@ public function __construct( $args = [], $use_cache = true ) { abstract protected function get_cache_key_salt(); /** - * Get the query arguments used for `WP_Query`. + * Builds the query arguments used for `WP_Query`. * * @return array */ - abstract protected function get_query_args(); + abstract protected function build_query_args(); /** * Get the default arguments used to generate the query. @@ -81,6 +88,19 @@ protected static function get_default_args() { return []; } + /** + * Get the query arguments used for `WP_Query`. + * + * @return array + */ + protected function get_query_args() { + if ( null === $this->query_args ) { + $this->query_args = $this->build_query_args(); + } + + return $this->query_args; + } + /** * Run any tasks that need to be done right away. */ diff --git a/includes/class-wp-job-manager-query-generator.php b/includes/class-wp-job-manager-query-generator.php index e641fdf8e..fe1a0ebd2 100644 --- a/includes/class-wp-job-manager-query-generator.php +++ b/includes/class-wp-job-manager-query-generator.php @@ -14,13 +14,6 @@ * Generates the query used to fetch job listings. */ class WP_Job_Manager_Query_Generator extends WP_Job_Manager_Listing_Query_Generator { - /** - * Query arguments used to create the `WP_Query` object. Generated by the `WP_Resume_Manager_Query_Generator::get_query_args` method. - * - * @var array - */ - private $query_args; - /** * Salt used for the cache key. * @@ -53,30 +46,26 @@ protected function init() { * * @return array */ - protected function get_query_args() { - if ( null !== $this->query_args ) { - return $this->query_args; - } - - $args = $this->get_args(); - $this->query_args = self::get_default_query_args(); + protected function build_query_args() { + $args = $this->get_args(); + $query_args = self::get_default_query_args(); // Common arguments. - $this->query_args['order'] = $this->parse_order(); - $this->query_args['orderby'] = $this->parse_orderby(); - $this->query_args['offset'] = $this->parse_offset(); - $this->query_args['posts_per_page'] = $this->parse_posts_per_page(); - $this->query_args['fields'] = $this->parse_fields(); - $this->query_args['no_found_rows'] = $this->parse_no_found_rows(); + $query_args['order'] = $this->parse_order(); + $query_args['orderby'] = $this->parse_orderby(); + $query_args['offset'] = $this->parse_offset(); + $query_args['posts_per_page'] = $this->parse_posts_per_page(); + $query_args['fields'] = $this->parse_fields(); + $query_args['no_found_rows'] = $this->parse_no_found_rows(); // Job listing specific arguments. - $this->query_args['s'] = $this->parse_keyword(); - $this->query_args['post_status'] = $this->parse_post_status(); - $this->query_args['meta_query'] = $this->generate_meta_query(); - $this->query_args['tax_query'] = $this->generate_tax_query(); + $query_args['s'] = $this->parse_keyword(); + $query_args['post_status'] = $this->parse_post_status(); + $query_args['meta_query'] = $this->generate_meta_query(); + $query_args['tax_query'] = $this->generate_tax_query(); /** This filter is documented in wp-job-manager.php */ - $this->query_args['lang'] = apply_filters( 'wpjm_lang', null ); + $query_args['lang'] = apply_filters( 'wpjm_lang', null ); /** * Filter the query arguments used. @@ -84,25 +73,25 @@ protected function get_query_args() { * @param array $query_args Query arguments used for `WP_Query`. * @param array $args Arguments used for generating the current query arguments. */ - $this->query_args = apply_filters( 'job_manager_get_listings', $this->query_args, $args ); + $query_args = apply_filters( 'job_manager_get_listings', $query_args, $args ); // Cleanup. $remove_empty = [ 'meta_query', 'tax_query' ]; $remove_null = [ 'no_found_rows', 's' ]; foreach ( $remove_empty as $query_key ) { - if ( empty( $this->query_args[ $query_key ] ) ) { - unset( $this->query_args[ $query_key ] ); + if ( empty( $query_args[ $query_key ] ) ) { + unset( $query_args[ $query_key ] ); } } foreach ( $remove_null as $query_key ) { - if ( null === $this->query_args[ $query_key ] ) { - unset( $this->query_args[ $query_key ] ); + if ( null === $query_args[ $query_key ] ) { + unset( $query_args[ $query_key ] ); } } - return $this->query_args; + return $query_args; } /** From 321bb9e45a2f8765f786df5a63b20497ef297a2f Mon Sep 17 00:00:00 2001 From: Jake Oehler Morrison Date: Tue, 22 Oct 2019 10:36:36 +0100 Subject: [PATCH 4/5] Move cleanup up to the parent class --- ...wp-job-manager-listing-query-generator.php | 28 ++++++++++++++++++- .../class-wp-job-manager-query-generator.php | 16 ----------- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php index 827879883..3fed0cc3a 100644 --- a/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php +++ b/includes/abstracts/abstract-wp-job-manager-listing-query-generator.php @@ -95,7 +95,7 @@ protected static function get_default_args() { */ protected function get_query_args() { if ( null === $this->query_args ) { - $this->query_args = $this->build_query_args(); + $this->query_args = $this->clean_query_args( $this->build_query_args() ); } return $this->query_args; @@ -240,6 +240,32 @@ protected function parse_fields() { return 'all'; } + /** + * Clean generated query arguments. + * + * @param array $query_args Query arguments passed to `WP_Query`. + * @return array + */ + protected function clean_query_args( $query_args ) { + // Cleanup. + $remove_empty = [ 'meta_query', 'tax_query' ]; + $remove_null = [ 'no_found_rows', 's' ]; + + foreach ( $remove_empty as $query_key ) { + if ( empty( $query_args[ $query_key ] ) ) { + unset( $query_args[ $query_key ] ); + } + } + + foreach ( $remove_null as $query_key ) { + if ( null === $query_args[ $query_key ] ) { + unset( $query_args[ $query_key ] ); + } + } + + return $query_args; + } + /** * Returns the query object with the result of the query. * diff --git a/includes/class-wp-job-manager-query-generator.php b/includes/class-wp-job-manager-query-generator.php index fe1a0ebd2..f729e1b7c 100644 --- a/includes/class-wp-job-manager-query-generator.php +++ b/includes/class-wp-job-manager-query-generator.php @@ -75,22 +75,6 @@ protected function build_query_args() { */ $query_args = apply_filters( 'job_manager_get_listings', $query_args, $args ); - // Cleanup. - $remove_empty = [ 'meta_query', 'tax_query' ]; - $remove_null = [ 'no_found_rows', 's' ]; - - foreach ( $remove_empty as $query_key ) { - if ( empty( $query_args[ $query_key ] ) ) { - unset( $query_args[ $query_key ] ); - } - } - - foreach ( $remove_null as $query_key ) { - if ( null === $query_args[ $query_key ] ) { - unset( $query_args[ $query_key ] ); - } - } - return $query_args; } From 40739794d71506fffa7c502f15c52571138c806d Mon Sep 17 00:00:00 2001 From: Jake Oehler Morrison Date: Tue, 22 Oct 2019 10:37:44 +0100 Subject: [PATCH 5/5] Updating naming for consistency within class --- includes/class-wp-job-manager-query-generator.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/includes/class-wp-job-manager-query-generator.php b/includes/class-wp-job-manager-query-generator.php index f729e1b7c..ce176b062 100644 --- a/includes/class-wp-job-manager-query-generator.php +++ b/includes/class-wp-job-manager-query-generator.php @@ -61,8 +61,8 @@ protected function build_query_args() { // Job listing specific arguments. $query_args['s'] = $this->parse_keyword(); $query_args['post_status'] = $this->parse_post_status(); - $query_args['meta_query'] = $this->generate_meta_query(); - $query_args['tax_query'] = $this->generate_tax_query(); + $query_args['meta_query'] = $this->build_meta_query(); + $query_args['tax_query'] = $this->build_tax_query(); /** This filter is documented in wp-job-manager.php */ $query_args['lang'] = apply_filters( 'wpjm_lang', null ); @@ -149,11 +149,11 @@ public function get_job_listings_keyword_search( $search ) { } /** - * Generates the meta query used by `WP_Query`. + * Builds the meta query used by `WP_Query`. * * @return array */ - private function generate_meta_query() { + private function build_meta_query() { $args = $this->get_args(); $meta_query = []; @@ -190,11 +190,11 @@ private function generate_meta_query() { } /** - * Generates the taxonomy query used by `WP_Query`. + * Builds the taxonomy query used by `WP_Query`. * * @return array */ - private function generate_tax_query() { + private function build_tax_query() { $args = $this->get_args(); $tax_query = [];