301 lines
10 KiB
PHP
301 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* /lib/compatibility/woocommerce.php
|
|
*
|
|
* WooCommerce compatibility features.
|
|
*
|
|
* @package Relevanssi
|
|
* @author Mikko Saari
|
|
* @license https://wordpress.org/about/gpl/ GNU General Public License
|
|
* @see https://www.relevanssi.com/
|
|
*/
|
|
|
|
add_filter( 'relevanssi_indexing_restriction', 'relevanssi_woocommerce_restriction' );
|
|
add_filter( 'relevanssi_admin_search_blocked_post_types', 'relevanssi_woocommerce_admin_search_blocked_post_types' );
|
|
add_filter( 'relevanssi_modify_wp_query', 'relevanssi_woocommerce_filters' );
|
|
add_filter( 'relevanssi_post_ok', 'relevanssi_variation_post_ok', 10, 2 );
|
|
|
|
/**
|
|
* This action solves the problems introduced by adjust_posts_count() in
|
|
* WooCommerce version 4.4.0.
|
|
*/
|
|
add_action( 'woocommerce_before_shop_loop', 'relevanssi_wc_reset_loop' );
|
|
|
|
RELEVANSSI_PREMIUM && add_filter( 'relevanssi_match', 'relevanssi_sku_boost' );
|
|
|
|
/**
|
|
* Resets the WC post loop in search queries.
|
|
*
|
|
* Hooks on to woocommerce_before_shop_loop.
|
|
*/
|
|
function relevanssi_wc_reset_loop() {
|
|
global $wp_query;
|
|
if ( $wp_query->is_search ) {
|
|
wc_reset_loop();
|
|
}
|
|
}
|
|
/**
|
|
* Applies the WooCommerce product visibility filter.
|
|
*
|
|
* @param array $restriction An array with two values: 'mysql' for the MySQL
|
|
* query restriction to modify, 'reason' for the reason of restriction.
|
|
*/
|
|
function relevanssi_woocommerce_restriction( $restriction ) {
|
|
// Backwards compatibility code for 2.8.0, remove at some point.
|
|
if ( is_string( $restriction ) ) {
|
|
$restriction = array(
|
|
'mysql' => $restriction,
|
|
'reason' => '',
|
|
);
|
|
}
|
|
|
|
$restriction['mysql'] .= relevanssi_woocommerce_indexing_filter();
|
|
$restriction['reason'] .= 'WooCommerce';
|
|
return $restriction;
|
|
}
|
|
|
|
/**
|
|
* WooCommerce product visibility filtering for indexing.
|
|
*
|
|
* This filter is applied before the posts are selected for indexing, so this will
|
|
* skip all the excluded posts right away.
|
|
*
|
|
* @since 4.0.9 (2.1.5)
|
|
* @global $wpdb The WordPress database interface.
|
|
*
|
|
* @return string $restriction The query restriction for the WooCommerce filtering.
|
|
*/
|
|
function relevanssi_woocommerce_indexing_filter() {
|
|
global $wpdb;
|
|
|
|
$restriction = '';
|
|
$woocommerce_blocks = array(
|
|
'outofstock' => false,
|
|
'exclude-from-catalog' => false,
|
|
'exclude-from-search' => true,
|
|
);
|
|
/**
|
|
* Controls the WooCommerce product visibility filtering.
|
|
*
|
|
* @param array $woocommerce_blocks Has three keys: 'outofstock',
|
|
* 'exclude-from-catalog' and 'exclude-from-search', matching three different
|
|
* product visibility settings. If the filter sets some of these to 'true',
|
|
* those posts will be filtered in the indexing.
|
|
*/
|
|
$woocommerce_blocks = apply_filters( 'relevanssi_woocommerce_indexing', $woocommerce_blocks );
|
|
$term_taxonomy_id_array = array();
|
|
if ( $woocommerce_blocks['outofstock'] ) {
|
|
$out_of_stock = get_term_by( 'slug', 'outofstock', 'product_visibility', OBJECT );
|
|
if ( $out_of_stock && isset( $out_of_stock->term_taxonomy_id ) ) {
|
|
$term_taxonomy_id_array[] = $out_of_stock->term_taxonomy_id;
|
|
}
|
|
}
|
|
if ( $woocommerce_blocks['exclude-from-catalog'] ) {
|
|
$exclude_from_catalog = get_term_by( 'slug', 'exclude-from-catalog', 'product_visibility', OBJECT );
|
|
if ( $exclude_from_catalog && isset( $exclude_from_catalog->term_taxonomy_id ) ) {
|
|
$term_taxonomy_id_array[] = $exclude_from_catalog->term_taxonomy_id;
|
|
}
|
|
}
|
|
if ( $woocommerce_blocks['exclude-from-search'] ) {
|
|
$exclude_from_search = get_term_by( 'slug', 'exclude-from-search', 'product_visibility', OBJECT );
|
|
if ( $exclude_from_search && isset( $exclude_from_search->term_taxonomy_id ) ) {
|
|
$term_taxonomy_id_array[] = $exclude_from_search->term_taxonomy_id;
|
|
}
|
|
}
|
|
if ( ! empty( $term_taxonomy_id_array ) ) {
|
|
$term_taxonomy_id_string = implode( ',', $term_taxonomy_id_array );
|
|
$restriction .= " AND post.ID NOT IN (SELECT object_id FROM $wpdb->term_relationships WHERE object_id = post.ID AND term_taxonomy_id IN ($term_taxonomy_id_string)) ";
|
|
}
|
|
return $restriction;
|
|
}
|
|
|
|
/**
|
|
* SKU weight boost.
|
|
*
|
|
* Increases the weight for matches in the _sku custom field. The amount of
|
|
* boost can be adjusted with the `relevanssi_sku_boost` filter hook. The
|
|
* default is 2.
|
|
*
|
|
* @param object $match_object The match object.
|
|
*
|
|
* @return object The match object.
|
|
*/
|
|
function relevanssi_sku_boost( $match_object ) {
|
|
$custom_field_detail = json_decode( $match_object->customfield_detail );
|
|
if ( null !== $custom_field_detail && isset( $custom_field_detail->_sku ) ) {
|
|
/**
|
|
* Filters the SKU boost value.
|
|
*
|
|
* @param float The boost multiplier, default 2.
|
|
*/
|
|
$match_object->weight *= apply_filters( 'relevanssi_sku_boost', 2 );
|
|
}
|
|
return $match_object;
|
|
}
|
|
|
|
/**
|
|
* Adds blocked WooCommerce post types to the list of blocked post types.
|
|
*
|
|
* Stops Relevanssi from taking over the admin search for the WooCommerce
|
|
* blocked post types using the relevanssi_admin_search_blocked_post_types
|
|
* filter hook.
|
|
*
|
|
* @param array $post_types The list of blocked post types.
|
|
* @return array
|
|
*/
|
|
function relevanssi_woocommerce_admin_search_blocked_post_types( array $post_types ): array {
|
|
$woo_post_types = array(
|
|
'shop_coupon',
|
|
'shop_order',
|
|
'shop_order_refund',
|
|
'wc_order_status',
|
|
'wc_order_email',
|
|
'shop_webhook',
|
|
);
|
|
return array_merge( $post_types, $woo_post_types );
|
|
}
|
|
|
|
/**
|
|
* Relevanssi support for WooCommerce filtering.
|
|
*
|
|
* @param WP_Query $query The WP_Query object.
|
|
* @return WP_Query The WP_Query object.
|
|
*/
|
|
function relevanssi_woocommerce_filters( $query ) {
|
|
// phpcs:disable WordPress.Security.NonceVerification.Recommended
|
|
|
|
$min_price = isset( $_REQUEST['min_price'] ) ? intval( $_REQUEST['min_price'] ) : false;
|
|
$max_price = isset( $_REQUEST['max_price'] ) ? intval( $_REQUEST['max_price'] ) : false;
|
|
|
|
$meta_query = $query->get( 'meta_query' );
|
|
if ( $min_price ) {
|
|
$meta_query[] = array(
|
|
'key' => '_price',
|
|
'value' => $min_price,
|
|
'compare' => '>=',
|
|
'type' => 'NUMERIC',
|
|
);
|
|
}
|
|
if ( $max_price ) {
|
|
$meta_query[] = array(
|
|
'key' => '_price',
|
|
'value' => $max_price,
|
|
'compare' => '<=',
|
|
'type' => 'NUMERIC',
|
|
);
|
|
}
|
|
if ( $meta_query ) {
|
|
$query->set( 'meta_query', $meta_query );
|
|
}
|
|
|
|
foreach ( array( 'product_tag', 'product_cat', 'product_brand' ) as $taxonomy ) {
|
|
$value = isset( $_REQUEST[ $taxonomy ] ) ? intval( $_REQUEST[ $taxonomy ] ) : false;
|
|
if ( $value ) {
|
|
$tax_query = $query->get( 'tax_query' );
|
|
if ( ! is_array( $tax_query ) ) {
|
|
$tax_query = array();
|
|
}
|
|
$tax_query[] = array(
|
|
'taxonomy' => $taxonomy,
|
|
'field' => 'term_id',
|
|
'terms' => $value,
|
|
);
|
|
$query->set( 'tax_query', $tax_query );
|
|
}
|
|
}
|
|
|
|
if ( 'no' === get_option( 'woocommerce_attribute_lookup_enabled' ) ) {
|
|
return $query;
|
|
}
|
|
|
|
$chosen_attributes = array();
|
|
|
|
if ( ! empty( $_GET ) ) {
|
|
foreach ( $_GET as $key => $value ) {
|
|
if ( 0 === strpos( $key, 'filter_' ) ) {
|
|
$attribute = wc_sanitize_taxonomy_name( str_replace( 'filter_', '', $key ) );
|
|
$taxonomy = wc_attribute_taxonomy_name( $attribute );
|
|
$filter_terms = ! empty( $value ) ? explode( ',', wc_clean( wp_unslash( $value ) ) ) : array();
|
|
|
|
if ( empty( $filter_terms ) || ! taxonomy_exists( $taxonomy ) || ! wc_attribute_taxonomy_id_by_name( $attribute ) ) {
|
|
continue;
|
|
}
|
|
|
|
$query_type = ! empty( $_GET[ 'query_type_' . $attribute ] ) && in_array( $_GET[ 'query_type_' . $attribute ], array( 'and', 'or' ), true )
|
|
? wc_clean( wp_unslash( $_GET[ 'query_type_' . $attribute ] ) )
|
|
: '';
|
|
|
|
$chosen_attributes[ $taxonomy ]['terms'] = array_map( 'sanitize_title', $filter_terms );
|
|
$chosen_attributes[ $taxonomy ]['query_type'] = $query_type ? $query_type : apply_filters( 'woocommerce_layered_nav_default_query_type', 'and' );
|
|
}
|
|
}
|
|
}
|
|
|
|
$tax_query = $query->get( 'tax_query' );
|
|
if ( ! is_array( $tax_query ) ) {
|
|
$tax_query = array();
|
|
}
|
|
foreach ( $chosen_attributes as $taxonomy => $data ) {
|
|
$tax_query[] = array(
|
|
'taxonomy' => $taxonomy,
|
|
'field' => 'slug',
|
|
'terms' => $data['terms'],
|
|
'operator' => 'and' === $data['query_type'] ? 'AND' : 'IN',
|
|
'include_children' => false,
|
|
);
|
|
}
|
|
$query->set( 'tax_query', $tax_query );
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Provides layered navigation term counts based on Relevanssi searches.
|
|
*
|
|
* Hooks onto woocommerce_get_filtered_term_product_counts_query to provide
|
|
* accurate term counts.
|
|
*
|
|
* @param array $query The MySQL query parts.
|
|
*
|
|
* @return array The modified query.
|
|
*/
|
|
function relevanssi_filtered_term_product_counts_query( $query ) {
|
|
if ( defined( 'BeRocket_AJAX_filters_version' ) ) {
|
|
return $query;
|
|
}
|
|
|
|
global $relevanssi_variables, $wpdb;
|
|
|
|
if ( false !== stripos( $query['select'], 'product_or_parent_id' ) ) {
|
|
$query['from'] = str_replace( 'FROM ', "FROM {$relevanssi_variables['relevanssi_table']} AS relevanssi, ", $query['from'] );
|
|
$query['where'] = str_replace( 'WHERE ', " WHERE relevanssi.doc = $wpdb->posts.ID AND ", $query['where'] );
|
|
$query['where'] = preg_replace( '/\(\w+posts.post_title LIKE(.*?)\)\)/', 'relevanssi.term LIKE\1)', $query['where'] );
|
|
$query['where'] = preg_replace( array( '/OR \(\w+posts.post_excerpt LIKE .*?\)/', '/OR \(\w+posts.post_content LIKE .*?\)/' ), '', $query['where'] );
|
|
} else {
|
|
$query['select'] = 'SELECT COUNT( DISTINCT( relevanssi.doc ) ) AS term_count, terms.term_id AS term_count_id';
|
|
$query['from'] = "FROM {$relevanssi_variables['relevanssi_table']} AS relevanssi, $wpdb->posts";
|
|
$query['where'] = str_replace( 'WHERE ', " WHERE relevanssi.doc = $wpdb->posts.ID AND ", $query['where'] );
|
|
$query['where'] = preg_replace( '/\(\w+posts.post_title LIKE(.*?)\)\)/', 'relevanssi.term LIKE\1)', $query['where'] );
|
|
$query['where'] = preg_replace( array( '/OR \(\w+posts.post_excerpt LIKE .*?\)/', '/OR \(\w+posts.post_content LIKE .*?\)/' ), '', $query['where'] );
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Checks the parent product status for product variations.
|
|
*
|
|
* @param bool $ok Whether the post is OK to return in search.
|
|
* @param int $post_id The post ID.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function relevanssi_variation_post_ok( $ok, $post_id ) : bool {
|
|
$post_type = relevanssi_get_post_type( $post_id );
|
|
if ( 'product_variation' === $post_type ) {
|
|
$parent = get_post_parent( $post_id );
|
|
return apply_filters( 'relevanssi_post_ok', $ok, $parent->ID );
|
|
}
|
|
return $ok;
|
|
}
|