Created
May 2, 2024 09:46
-
-
Save jmcausing/b6819983adf39cf53004f7033333fb54 to your computer and use it in GitHub Desktop.
WPML + CommerceKit Ajax search fix - class-commercekit-ajs-index.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* | |
* Admin Ajax Search Index | |
* | |
* @package CommerceKit | |
*/ | |
/** | |
* CommerceKit_AJS_Index class. | |
*/ | |
class CommerceKit_AJS_Index { | |
/** | |
* Start time of current process. | |
* | |
* @var start_time | |
*/ | |
protected $start_time = 0; | |
/** | |
* Server memory limit. | |
* | |
* @var memory_limit | |
*/ | |
protected $memory_limit = 0; | |
/** | |
* Server execution time. | |
* | |
* @var execution_time | |
*/ | |
protected $execution_time = 0; | |
/** | |
* CommerceKit_AJS_Index Constructor | |
*/ | |
public function __construct() { | |
add_action( 'woocommerce_process_product_meta', array( $this, 'update_ajs_index_data' ), 20, 2 ); | |
add_action( 'woocommerce_updated_product_stock', array( $this, 'update_ajs_index' ), 20, 1 ); | |
add_action( 'woocommerce_save_product_variation', array( $this, 'update_ajs_index' ), 20, 1 ); | |
add_action( 'woocommerce_ajax_save_product_variations', array( $this, 'update_ajs_index' ), 20, 1 ); | |
add_action( 'save_post', array( $this, 'quick_edit_update_ajs_index' ), 20, 1 ); | |
add_action( 'deleted_post', array( $this, 'update_ajs_index_delete_variation' ), 20, 2 ); | |
add_action( 'woocommerce_product_set_stock', array( $this, 'update_ajs_index_stock_updates' ), 20, 1 ); | |
add_action( 'woocommerce_variation_set_stock', array( $this, 'update_ajs_index_stock_updates' ), 20, 1 ); | |
add_action( 'updated_post_meta', array( $this, 'update_ajs_index_meta_updates' ), 20, 4 ); | |
add_action( 'delete_post', array( $this, 'update_ajs_index_delete_product' ), 20, 1 ); | |
add_action( 'shutdown', array( $this, 'update_ajs_index_on_shutdown' ), 20 ); | |
add_action( 'init', array( $this, 'ajs_prepare_action_scheduler' ) ); | |
add_action( 'commercegurus_ajs_run_wc_product_index', array( $this, 'run_wc_product_index' ) ); | |
} | |
/** | |
* Update product index data | |
* | |
* @param string $post_id post ID. | |
* @param string $post post. | |
*/ | |
public function update_ajs_index_data( $post_id, $post ) { | |
if ( 'product' !== $post->post_type ) { | |
return; | |
} | |
$product = wc_get_product( $post_id ); | |
if ( ! $product ) { | |
return; | |
} | |
$this->build_ajs_index( $product, 'via update product' ); | |
} | |
/** | |
* Update product index on stock, variations updates | |
* | |
* @param string $product_id product ID. | |
*/ | |
public function update_ajs_index( $product_id ) { | |
$product = wc_get_product( $product_id ); | |
if ( ! $product ) { | |
return; | |
} | |
$this->build_ajs_index( $product, 'via save variations' ); | |
} | |
/** | |
* Update product index on quick edit updates | |
* | |
* @param string $product_id product ID. | |
*/ | |
public function quick_edit_update_ajs_index( $product_id ) { | |
global $post; | |
if ( isset( $post ) && 'product' === $post->post_type ) { | |
$product = wc_get_product( $product_id ); | |
if ( ! $product ) { | |
return; | |
} | |
$this->build_ajs_index( $product, 'via quick update product' ); | |
} | |
} | |
/** | |
* Update product index on delete variation | |
* | |
* @param string $postid variation ID. | |
* @param string $post variation post. | |
*/ | |
public function update_ajs_index_delete_variation( $postid, $post ) { | |
if ( $post && 'product_variation' === $post->post_type ) { | |
$product_id = $post->post_parent; | |
$product = wc_get_product( $product_id ); | |
if ( ! $product ) { | |
return; | |
} | |
$this->build_ajs_index( $product, 'via delete variation' ); | |
} | |
} | |
/** | |
* Update product index on stock updates | |
* | |
* @param string $product_obj product object. | |
*/ | |
public function update_ajs_index_stock_updates( $product_obj ) { | |
if ( $product_obj->is_type( 'variation' ) ) { | |
$product_id = $product_obj->get_parent_id(); | |
$product = wc_get_product( $product_id ); | |
} else { | |
$product = $product_obj; | |
} | |
if ( ! $product ) { | |
return; | |
} | |
$this->build_ajs_index( $product, 'via stock update' ); | |
} | |
/** | |
* Update product index on meta updates | |
* | |
* @param string $meta_id meta ID. | |
* @param string $post_id post ID. | |
* @param string $meta_key meta key. | |
* @param string $meta_value meta value. | |
*/ | |
public function update_ajs_index_meta_updates( $meta_id, $post_id, $meta_key, $meta_value ) { | |
global $cgkit_ajs_shutdowns; | |
if ( isset( $post_id ) && ! empty( $post_id ) ) { | |
$product = wc_get_product( $post_id ); | |
if ( ! $product ) { | |
return; | |
} | |
if ( ! isset( $cgkit_ajs_shutdowns ) ) { | |
$cgkit_ajs_shutdowns = array(); | |
} | |
$cgkit_ajs_shutdowns[ $post_id ] = $post_id; | |
} | |
} | |
/** | |
* Build product index on end of script. | |
*/ | |
public function update_ajs_index_on_shutdown() { | |
global $cgkit_ajs_shutdowns, $cgkit_ajs_cached_keys; | |
if ( ! isset( $cgkit_ajs_shutdowns ) ) { | |
return; | |
} | |
if ( is_array( $cgkit_ajs_shutdowns ) && count( $cgkit_ajs_shutdowns ) ) { | |
foreach ( $cgkit_ajs_shutdowns as $product_id ) { | |
$product = wc_get_product( $product_id ); | |
if ( ! $product ) { | |
continue; | |
} | |
if ( isset( $cgkit_ajs_cached_keys[ $product_id ] ) ) { | |
unset( $cgkit_ajs_cached_keys[ $product_id ] ); | |
} | |
$this->build_ajs_index( $product, 'via shutdown script' ); | |
} | |
} | |
} | |
/** | |
* Delete product index when a product is deleted. | |
* | |
* @param string $product_id product ID. | |
*/ | |
public function update_ajs_index_delete_product( $product_id ) { | |
if ( 'product' !== get_post_type( $product_id ) ) { | |
return; | |
} | |
$this->delete_index( 'product_id', $product_id ); | |
} | |
/** | |
* Build product index | |
* | |
* @param string $product product object. | |
* @param string $suffix logger suffix text. | |
*/ | |
public function build_ajs_index( $product, $suffix = '' ) { | |
global $wpdb, $cgkit_ajs_cached_keys; | |
$product_id = $product ? $product->get_id() : 0; | |
if ( ! $product_id || 'product' !== get_post_type( $product_id ) ) { | |
return; | |
} | |
if ( isset( $cgkit_ajs_cached_keys[ $product_id ] ) && $cgkit_ajs_cached_keys[ $product_id ] ) { | |
return; | |
} | |
$cgkit_ajs_cached_keys[ $product_id ] = true; | |
if ( ! empty( $suffix ) ) { | |
$suffix = ' ' . $suffix; | |
} | |
commercegurus_ajs_log( 'building ajax search index for product id ' . $product_id . $suffix ); | |
$this->create_index( $product ); | |
commercegurus_ajs_log( 'ajax search index complete for product id ' . $product_id . $suffix ); | |
} | |
/** | |
* Delete index | |
* | |
* @param string $key row name. | |
* @param string $value row value. | |
* @param string $format row format. | |
*/ | |
public function delete_index( $key, $value, $format = '%d' ) { | |
global $wpdb; | |
$table = $wpdb->prefix . 'commercekit_ajs_product_index'; | |
$sql = 'DELETE FROM ' . $table . ' WHERE ' . $key . ' = ' . $format; | |
$sql = $wpdb->prepare( $sql, $value ); // phpcs:ignore | |
$wpdb->query( $sql ); // phpcs:ignore | |
} | |
/** | |
* Create index | |
* | |
* @param string $product product object. | |
*/ | |
public function create_index( $product ) { | |
global $wpdb; | |
$product_id = $product->get_id(); | |
$table = $wpdb->prefix . 'commercekit_ajs_product_index'; | |
$row_id = (int) $wpdb->get_var( $wpdb->prepare( 'SELECT id FROM ' . $table . ' WHERE product_id = %d', $product_id ) ); // phpcs:ignore | |
$skus = array(); | |
if ( 'variable' === $product->get_type() ) { | |
$variation_ids = $product->get_visible_children(); | |
if ( count( $variation_ids ) ) { | |
foreach ( $variation_ids as $variation_id ) { | |
$variation = wc_get_product( $variation_id ); | |
if ( $variation && $variation->get_sku() ) { | |
$skus[] = $variation->get_sku(); | |
} | |
} | |
} | |
} | |
$attributes = array(); | |
foreach ( $product->get_attributes( 'edit' ) as $attribute ) { | |
if ( $attribute->is_taxonomy() ) { | |
$terms = $attribute->get_terms(); | |
if ( count( $terms ) ) { | |
foreach ( $terms as $item ) { | |
$attributes[] = $item->name; | |
} | |
} | |
} else { | |
$_options = $attribute->get_options(); | |
if ( count( $_options ) ) { | |
foreach ( $_options as $_option ) { | |
$attributes[] = $_option; | |
} | |
} | |
} | |
} | |
$is_visible = ( 'visible' === $product->get_catalog_visibility() || 'search' === $product->get_catalog_visibility() ) ? 1 : 0; | |
$product_img = has_post_thumbnail( $product_id ) ? get_the_post_thumbnail( $product_id, 'thumbnail' ) : ''; | |
$language = ''; | |
$other_lang = ''; | |
$other_urls = array(); | |
$product_url = $product->get_permalink(); | |
if ( defined( 'ICL_SITEPRESS_VERSION' ) && has_filter( 'wpml_post_language_details' ) ) { | |
$lang_info = apply_filters( 'wpml_post_language_details', null, $product_id ); | |
$language = isset( $lang_info['language_code'] ) ? $lang_info['language_code'] : $language; | |
$default_lang = apply_filters( 'wpml_default_language', null ); | |
if ( $language === $default_lang ) { | |
$other_lang = $this->get_wpml_missing_languages( $product_id, $language ); | |
if ( ! empty( $other_lang ) ) { | |
$other_urls = $this->get_wpml_missing_language_urls( $product->get_permalink(), $other_lang ); | |
} | |
} else { | |
$this->update_wpml_missing_languages( $product_id, $language, $default_lang ); | |
} | |
if ( has_filter( 'wpml_permalink' ) ) { | |
$product_url = apply_filters( 'wpml_permalink', $product_url, $language ); | |
} | |
} elseif ( function_exists( 'pll_default_language' ) && function_exists( 'pll_get_post_language' ) ) { | |
$language = pll_get_post_language( $product_id ) ? pll_get_post_language( $product_id ) : pll_default_language(); | |
} | |
$row = array( | |
'product_id' => $product_id, | |
'title' => strip_shortcodes( wp_strip_all_tags( get_the_title( $product_id ) ) ), | |
'description' => strip_shortcodes( wp_strip_all_tags( $product->get_description(), true ) ), | |
'short_description' => strip_shortcodes( wp_strip_all_tags( $product->get_short_description(), true ) ), | |
'product_sku' => $product->get_sku(), | |
'variation_sku' => implode( ',', $skus ), | |
'attributes' => implode( ',', $attributes ), | |
'product_url' => $product_url, | |
'product_img' => $product_img, | |
'in_stock' => $product->is_in_stock() ? 1 : 0, | |
'is_visible' => $is_visible, | |
'status' => $product->get_status(), | |
'lang' => $language, | |
'other_lang' => $other_lang, | |
'other_urls' => wp_json_encode( $other_urls ), | |
); | |
if ( $row_id ) { | |
$where = array( | |
'id' => $row_id, | |
); | |
$wpdb->update( $table, $row, $where ); // db call ok; no-cache ok. | |
} else { | |
$wpdb->insert( $table, $row ); // db call ok; no-cache ok. | |
} | |
} | |
/** | |
* Get wpml missing languages | |
* | |
* @param string $product_id product ID. | |
* @param string $language product language. | |
*/ | |
public function get_wpml_missing_languages( $product_id, $language ) { | |
$other_langs = array(); | |
$active_langs = array(); | |
$languages = apply_filters( 'wpml_active_languages', null ); | |
if ( is_array( $languages ) && count( $languages ) ) { | |
foreach ( $languages as $lang ) { | |
if ( isset( $lang['language_code'] ) && ! empty( $lang['language_code'] ) && $language !== $lang['language_code'] ) { | |
$active_langs[] = $lang['language_code']; | |
} | |
} | |
} | |
$post_langs = array(); | |
$elem_type = apply_filters( 'wpml_element_type', 'product' ); | |
$trans_id = apply_filters( 'wpml_element_trid', false, $product_id, $elem_type ); | |
$translations = apply_filters( 'wpml_get_element_translations', array(), $trans_id, $elem_type ); | |
if ( is_array( $translations ) && count( $translations ) ) { | |
foreach ( $translations as $trans ) { | |
if ( isset( $trans->language_code ) && ! empty( $trans->language_code ) && $language !== $trans->language_code ) { | |
$post_langs[] = $trans->language_code; | |
} | |
} | |
} | |
$other_langs = array_diff( $active_langs, $post_langs ); | |
return implode( ',', $other_langs ); | |
} | |
/** | |
* Update wpml missing languages | |
* | |
* @param string $product_id product ID. | |
* @param string $language product language. | |
* @param string $default_lang site default language. | |
*/ | |
public function update_wpml_missing_languages( $product_id, $language, $default_lang ) { | |
global $wpdb; | |
$original_id = apply_filters( 'wpml_object_id', $product_id, 'product', false, $default_lang ); | |
if ( $original_id ) { | |
$table = $wpdb->prefix . 'commercekit_ajs_product_index'; | |
$other_lang = $wpdb->get_var( $wpdb->prepare( 'SELECT other_lang FROM ' . $table . ' WHERE product_id = %d AND lang = %s', $original_id, $default_lang ) ); // phpcs:ignore | |
if ( ! empty( $other_lang ) ) { | |
$langs = explode( ',', $other_lang ); | |
$key = array_search( $language, $langs, true ); | |
if ( false !== $key && isset( $langs[ $key ] ) ) { | |
unset( $langs[ $key ] ); | |
} | |
$where = array( | |
'product_id' => $original_id, | |
'lang' => $default_lang, | |
); | |
$row = array( | |
'other_lang' => implode( ',', $langs ), | |
); | |
$wpdb->update( $table, $row, $where ); // db call ok; no-cache ok. | |
} | |
} | |
} | |
/** | |
* Get wpml missing language urls | |
* | |
* @param string $permalink product original URL. | |
* @param string $other_langs other languages. | |
*/ | |
public function get_wpml_missing_language_urls( $permalink, $other_langs ) { | |
$other_urls = array(); | |
$other_langs = explode( ',', $other_langs ); | |
if ( count( $other_langs ) ) { | |
foreach ( $other_langs as $lang ) { | |
$other_urls[ $lang ] = apply_filters( 'wpml_permalink', $permalink, $lang ); | |
} | |
} | |
return $other_urls; | |
} | |
/** | |
* Prepare action scheduler for all ajs index | |
*/ | |
public function ajs_prepare_action_scheduler() { | |
global $wpdb; | |
$options = get_option( 'commercekit', array() ); | |
$ajs_active = isset( $options['ajax_search'] ) && 1 === (int) $options['ajax_search'] ? true : false; | |
if ( ! $ajs_active ) { | |
return; | |
} | |
$args = array( | |
'hook' => 'commercegurus_ajs_run_wc_product_index', | |
'per_page' => -1, | |
'group' => 'commercekit', | |
'status' => ActionScheduler_Store::STATUS_RUNNING, | |
); | |
$is_running = false; | |
$action_ids = as_get_scheduled_actions( $args, 'ids' ); | |
if ( count( $action_ids ) ) { | |
$is_running = true; | |
} | |
$args2 = array( | |
'hook' => 'commercegurus_ajs_run_wc_product_index', | |
'per_page' => -1, | |
'group' => 'commercekit', | |
'status' => ActionScheduler_Store::STATUS_PENDING, | |
); | |
$action_ids2 = as_get_scheduled_actions( $args2, 'ids' ); | |
if ( count( $action_ids2 ) ) { | |
if ( ! $is_running && 1 === count( $action_ids2 ) ) { | |
return; | |
} | |
if ( ! $is_running ) { | |
sort( $action_ids2, SORT_NUMERIC ); | |
array_pop( $action_ids2 ); | |
} | |
$query = 'DELETE FROM ' . $wpdb->prefix . 'actionscheduler_actions WHERE action_id IN (' . implode( ',', $action_ids2 ) . ')'; | |
$wpdb->query( $query ); // phpcs:ignore | |
$query2 = 'DELETE FROM ' . $wpdb->prefix . 'actionscheduler_logs WHERE action_id IN (' . implode( ',', $action_ids2 ) . ')'; | |
$wpdb->query( $query2 ); // phpcs:ignore | |
return; | |
} | |
if ( $is_running ) { | |
return; | |
} | |
$ajs_scheduled = isset( $options['generating_ajs'] ) && 1 === (int) $options['generating_ajs'] ? true : false; | |
if ( $ajs_scheduled ) { | |
return; | |
} | |
$interrupt_ajs = isset( $options['interrupt_ajs'] ) && 1 === (int) $options['interrupt_ajs'] ? true : false; | |
if ( $interrupt_ajs ) { | |
return; | |
} | |
$generate_ajs = isset( $_POST['generate_ajs'] ) ? (int) sanitize_text_field( wp_unslash( $_POST['generate_ajs'] ) ) : 0; // phpcs:ignore | |
if ( $generate_ajs ) { | |
return; | |
} | |
$ajs_product_id = isset( $options['generating_ajs_id'] ) ? (int) $options['generating_ajs_id'] : 0; | |
if ( 0 < $ajs_product_id ) { | |
$generating_ajs_done = isset( $options['generating_ajs_done'] ) ? (int) $options['generating_ajs_done'] : 0; | |
if ( $generating_ajs_done ) { | |
$template = "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_type = 'product' AND post_parent = '0' AND post_status = 'publish' AND ID > %d ORDER BY ID ASC"; | |
$query = $wpdb->prepare( $template, $ajs_product_id ); // phpcs:ignore | |
$pending = (int) $wpdb->get_var( $query ); // phpcs:ignore | |
if ( $pending ) { | |
$args3 = array( | |
'hook' => 'commercegurus_ajs_run_wc_product_index', | |
'per_page' => -1, | |
'group' => 'commercekit', | |
'status' => array( ActionScheduler_Store::STATUS_PENDING, ActionScheduler_Store::STATUS_RUNNING ), | |
); | |
$action_ids3 = as_get_scheduled_actions( $args3, 'ids' ); | |
if ( ! count( $action_ids3 ) ) { | |
as_schedule_single_action( time() + 5, 'commercegurus_ajs_run_wc_product_index', array( 'ajs_product_id' => $ajs_product_id ), 'commercekit' ); | |
commercegurus_ajs_log( 'REBUILDING INDEX: creating action for commercegurus_ajs_run_wc_product_index hook with new product_id = ' . $ajs_product_id ); | |
$options['generating_ajs_id'] = $ajs_product_id; | |
$options['generating_ajs'] = 1; | |
$options['generating_ajs_done'] = 0; | |
commercegurus_ajs_log( 'updating generating_ajs_id to ' . $ajs_product_id . ', generating_ajs to 1, generating_ajs_done to 0' ); | |
update_option( 'commercekit', $options, false ); | |
} | |
return; | |
} else { | |
return; | |
} | |
} else { | |
return; | |
} | |
} | |
$args4 = array( | |
'hook' => 'commercegurus_ajs_run_wc_product_index', | |
'per_page' => -1, | |
'group' => 'commercekit', | |
'status' => array( ActionScheduler_Store::STATUS_PENDING, ActionScheduler_Store::STATUS_RUNNING ), | |
); | |
$action_ids4 = as_get_scheduled_actions( $args4, 'ids' ); | |
if ( ! count( $action_ids4 ) ) { | |
as_schedule_single_action( time() + 5, 'commercegurus_ajs_run_wc_product_index', array( 'ajs_product_id' => 0 ), 'commercekit' ); | |
commercegurus_ajs_log( 'BUILDING INDEX: creating action for commercegurus_ajs_run_wc_product_index hook with product_id = 0' ); | |
$options['generating_ajs_id'] = 0; | |
$options['generating_ajs'] = 1; | |
$options['generating_ajs_done'] = 0; | |
commercegurus_ajs_log( 'updating generating_ajs_id to 0, generating_ajs to 1, generating_ajs_done to 0' ); | |
update_option( 'commercekit', $options, false ); | |
} | |
} | |
/** | |
* WooCommerce product ajs index db table | |
* | |
* @param string $args arguments. | |
*/ | |
public function run_wc_product_index( $args ) { | |
global $wpdb; | |
$ajs_product_id = 0; | |
if ( is_numeric( $args ) ) { | |
$ajs_product_id = (int) $args; | |
} elseif ( is_array( $args ) ) { | |
if ( isset( $args[0] ) && is_numeric( $args[0] ) ) { | |
$ajs_product_id = (int) $args[0]; | |
} elseif ( isset( $args['ajs_product_id'] ) && is_numeric( $args['ajs_product_id'] ) ) { | |
$ajs_product_id = (int) $args['ajs_product_id']; | |
} | |
} | |
$this->start_time = time(); | |
$this->memory_limit = $this->get_memory_limit(); | |
$this->execution_time = $this->get_execution_time(); | |
$template = "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'product' AND post_parent = '0' AND post_status = 'publish' AND ID > %d ORDER BY ID ASC"; | |
$query = $wpdb->prepare( $template, $ajs_product_id ); // phpcs:ignore | |
$results = $wpdb->get_col( $query ); // phpcs:ignore | |
if ( count( $results ) ) { | |
$options = get_option( 'commercekit', array() ); | |
$next_job = false; | |
foreach ( $results as $product_id ) { | |
$product = wc_get_product( $product_id ); | |
if ( ! $product ) { | |
continue; | |
} | |
$this->build_ajs_index( $product, 'via Action Scheduler' ); | |
$options['generating_ajs_id'] = $product_id; | |
$options['generate_ajs_time'] = time(); | |
update_option( 'commercekit', $options, false ); | |
if ( $this->memory_exceeded() || $this->time_exceeded() ) { | |
$next_job = true; | |
$ajs_product_id = $product_id; | |
break; | |
} | |
} | |
$completed = true; | |
if ( $next_job && $ajs_product_id ) { | |
$template2 = "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_type = 'product' AND post_parent = '0' AND post_status = 'publish' AND ID > %d "; | |
$query2 = $wpdb->prepare( $template2, $ajs_product_id ); // phpcs:ignore | |
$pending = (int) $wpdb->get_var( $query2 ); // phpcs:ignore | |
if ( $pending ) { | |
as_schedule_single_action( time() + 5, 'commercegurus_ajs_run_wc_product_index', array( 'ajs_product_id' => $ajs_product_id ), 'commercekit' ); | |
commercegurus_ajs_log( 'REBUILDING INDEX: creating action for commercegurus_ajs_run_wc_product_index hook with next product_id = ' . $ajs_product_id ); | |
$completed = false; | |
} | |
} | |
if ( $completed ) { | |
$options['generating_ajs'] = 0; | |
$options['generating_ajs_done'] = 1; | |
update_option( 'commercekit', $options, false ); | |
commercegurus_ajs_log( 'REBUILDING INDEX: complete ajax search index for all products.' ); | |
} | |
} | |
} | |
/** | |
* Get search product ids | |
* | |
* @param string $keywords search words. | |
* @param string $return_all return all product ids. | |
* @param string $language current language code. | |
*/ | |
public function get_search_product_ids( $keywords, $return_all = false, $language = '' ) { | |
global $wpdb; | |
$keywords = trim( $keywords ); | |
if ( empty( $keywords ) ) { | |
if ( $return_all ) { | |
return array( | |
'ids' => array( -1 ), | |
'total' => 0, | |
); | |
} else { | |
return array( | |
'products' => array(), | |
'total' => 0, | |
); | |
} | |
} | |
$options = get_option( 'commercekit', array() ); | |
$table = $wpdb->prefix . 'commercekit_ajs_product_index'; | |
$keys = $this->get_search_parsed_words( $keywords ); | |
$total = count( $keys ); | |
$and_sql = array(); | |
$or_sql = array(); | |
$cgkit_fast_search = isset( $options['ajs_fast_search'] ) && 1 === (int) $options['ajs_fast_search'] ? true : false; | |
if ( 1 === $total ) { | |
$or_sql[] = 'title LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'description LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'short_description LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'attributes LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'product_sku LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'variation_sku LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
} else { | |
foreach ( $keys as $key ) { | |
$or_sql[] = 'title LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'description LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'short_description LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'attributes LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'product_sku LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'variation_sku LIKE \'%' . esc_sql( $key ) . '%\''; | |
} | |
} | |
$and_sql[] = 'is_visible = 1'; | |
$and_sql[] = 'status = \'publish\''; | |
$order_by = $this->get_order_by_string( $keywords, $keys, 'title', 'description' ); | |
if ( isset( $options['ajs_orderby_oos'] ) && 1 === (int) $options['ajs_orderby_oos'] ) { | |
$order_by = 'in_stock DESC' . ( ! empty( $order_by ) ? ', ' . $order_by : '' ); | |
} | |
if ( ( isset( $options['ajs_outofstock'] ) && 1 === (int) $options['ajs_outofstock'] ) || 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) ) { | |
$and_sql[] = 'in_stock = 1'; | |
} | |
$limit = isset( $options['ajs_product_count'] ) && (int) $options['ajs_product_count'] ? (int) $options['ajs_product_count'] : 3; | |
if ( $limit < 1 || $limit > 5 ) { | |
$limit = 3; | |
} | |
$ajs_excludes = isset( $options['ajs_excludes'] ) ? explode( ',', $options['ajs_excludes'] ) : array(); | |
$ajs_excludes = array_filter( $ajs_excludes ); | |
if ( count( $ajs_excludes ) ) { | |
$and_sql[] = 'product_id NOT IN (' . implode( ',', $ajs_excludes ) . ')'; | |
} | |
if ( ! empty( $language ) ) { | |
/* $and_sql[] = '( lang = \'' . esc_sql( $language ) . '\' OR lang = \'\' OR FIND_IN_SET(\'' . esc_sql( $language ) . '\', other_lang) )'; */ | |
$and_sql[] = '( lang = \'' . esc_sql( $language ) . '\' OR lang = \'\' )'; | |
} | |
$cquery = 'SELECT COUNT(product_id) FROM ' . $table . ' WHERE 1 = 1 '; | |
if ( $return_all ) { | |
$query = 'SELECT product_id FROM ' . $table . ' WHERE 1 = 1 '; | |
} else { | |
$query = 'SELECT product_id, title, short_description, product_url, product_img, lang, other_lang, other_urls FROM ' . $table . ' WHERE 1 = 1 '; | |
} | |
if ( count( $or_sql ) ) { | |
$cquery .= ' AND ( ' . implode( ' OR ', $or_sql ) . ' ) '; | |
$query .= ' AND ( ' . implode( ' OR ', $or_sql ) . ' ) '; | |
} | |
if ( count( $and_sql ) ) { | |
$cquery .= ' AND ( ' . implode( ' AND ', $and_sql ) . ' ) '; | |
$query .= ' AND ( ' . implode( ' AND ', $and_sql ) . ' ) '; | |
} | |
if ( ! empty( $order_by ) ) { | |
$query .= ' ORDER BY ' . $order_by; | |
} | |
if ( $return_all ) { | |
$product_ids = $wpdb->get_col( $query ); // phpcs:ignore | |
$ids_count = count( $product_ids ); | |
if ( ! $ids_count ) { | |
$product_ids = array( -1 ); | |
} | |
return array( | |
'ids' => $product_ids, | |
'total' => $ids_count, | |
); | |
} else { | |
$query .= ' LIMIT 0, ' . $limit; | |
$products = $wpdb->get_results( $query ); // phpcs:ignore | |
$total = 0; | |
if ( ! $cgkit_fast_search ) { | |
$total = (int) $wpdb->get_var( $cquery ); // phpcs:ignore | |
} | |
return array( | |
'products' => $products, | |
'total' => $total, | |
); | |
} | |
} | |
/** | |
* Get order by string | |
* | |
* @param string $keywords search words. | |
* @param string $search_terms search words as array. | |
* @param string $title_field title field name. | |
* @param string $content_field content field name. | |
*/ | |
public function get_order_by_string( $keywords, $search_terms, $title_field, $content_field ) { | |
global $wpdb; | |
$search_orderby = ''; | |
$search_orderby_title = array(); | |
if ( count( $search_terms ) ) { | |
foreach ( $search_terms as $term ) { | |
$like = '%' . $wpdb->esc_like( $term ) . '%'; | |
$search_orderby_title[] = $wpdb->prepare( "$title_field LIKE %s", $like ); // phpcs:ignore | |
} | |
} | |
if ( count( $search_terms ) > 0 ) { | |
$num_terms = count( $search_orderby_title ); | |
$like = '%' . $wpdb->esc_like( $keywords ) . '%'; | |
$like2 = $wpdb->esc_like( $keywords ) . '%'; | |
$search_orderby .= '(CASE '; | |
$search_orderby .= $wpdb->prepare( "WHEN $title_field LIKE %s THEN 1 ", $like2 ); // phpcs:ignore | |
$search_orderby .= $wpdb->prepare( "WHEN $title_field LIKE %s THEN 2 ", $like ); // phpcs:ignore | |
if ( $num_terms < 10 && count( $search_terms ) > 1 ) { // make 10 words as sentence. | |
$search_orderby .= 'WHEN ' . implode( ' AND ', $search_orderby_title ) . ' THEN 3 '; | |
if ( $num_terms > 1 ) { | |
$search_orderby .= 'WHEN ' . implode( ' OR ', $search_orderby_title ) . ' THEN 4 '; | |
} | |
} | |
$search_orderby .= $wpdb->prepare( "WHEN $content_field LIKE %s THEN 5 ", $like ); // phpcs:ignore | |
$search_orderby .= 'ELSE 6 END)'; | |
} | |
return $search_orderby; | |
} | |
/** | |
* Get search parsed words | |
* | |
* @param string $keywords search words. | |
*/ | |
public function get_search_parsed_words( $keywords ) { | |
$keywords = stripslashes( $keywords ); | |
$keywords = str_replace( array( "\r", "\n" ), '', $keywords ); | |
if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $keywords, $matches ) ) { | |
$search_words = $this->get_search_parsed_terms( $matches[0] ); | |
if ( empty( $search_words ) || count( $search_words ) > 9 ) { // make 10 words as sentence. | |
$search_words = array( $keywords ); | |
} | |
} else { | |
$search_words = array( $keywords ); | |
} | |
return $search_words; | |
} | |
/** | |
* Get search parsed terms | |
* | |
* @param string $terms search terms. | |
*/ | |
public function get_search_parsed_terms( $terms ) { | |
$final_terms = array(); | |
if ( count( $terms ) ) { | |
foreach ( $terms as $term ) { | |
if ( preg_match( '/^".+"$/', $term ) ) { | |
$term = trim( $term, "\"'" ); | |
} else { | |
$term = trim( $term, "\"' " ); | |
} | |
if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z]$/i', $term ) ) ) { | |
continue; | |
} | |
$final_terms[] = $term; | |
} | |
} | |
return $final_terms; | |
} | |
/** | |
* Get search post ids | |
* | |
* @param string $keywords search words. | |
* @param string $return_all return all product ids. | |
* @param string $language current language code. | |
* @param string $language_plug language plugin. | |
*/ | |
public function get_search_post_ids( $keywords, $return_all = false, $language = '', $language_plug = '' ) { | |
global $wpdb; | |
$keywords = trim( $keywords ); | |
if ( empty( $keywords ) ) { | |
if ( $return_all ) { | |
return array( | |
'ids' => array( -1 ), | |
'total' => 0, | |
); | |
} else { | |
return array( | |
'posts' => array(), | |
'total' => 0, | |
); | |
} | |
} | |
$options = get_option( 'commercekit', array() ); | |
$table = $wpdb->prefix . 'posts'; | |
$keys = $this->get_search_parsed_words( $keywords ); | |
$total = count( $keys ); | |
$and_sql = array(); | |
$or_sql = array(); | |
$cgkit_fast_search = isset( $options['ajs_fast_search'] ) && 1 === (int) $options['ajs_fast_search'] ? true : false; | |
if ( 1 === $total ) { | |
$or_sql[] = 'p.post_title LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'p.post_content LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
$or_sql[] = 'p.post_excerpt LIKE \'%' . esc_sql( $keywords ) . '%\''; | |
} else { | |
foreach ( $keys as $key ) { | |
$or_sql[] = 'p.post_title LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'p.post_content LIKE \'%' . esc_sql( $key ) . '%\''; | |
$or_sql[] = 'p.post_excerpt LIKE \'%' . esc_sql( $key ) . '%\''; | |
} | |
} | |
$and_sql[] = 'p.post_status = \'publish\''; | |
$order_by = $this->get_order_by_string( $keywords, $keys, 'p.post_title', 'p.post_content' ); | |
$limit = isset( $options['ajs_other_count'] ) && (int) $options['ajs_other_count'] ? (int) $options['ajs_other_count'] : 3; | |
if ( $limit < 1 || $limit > 5 ) { | |
$limit = 3; | |
} | |
$ajs_excludes_other = isset( $options['ajs_excludes_other'] ) ? explode( ',', $options['ajs_excludes_other'] ) : array(); | |
$ajs_excludes_other = array_filter( $ajs_excludes_other ); | |
if ( count( $ajs_excludes_other ) ) { | |
$and_sql[] = 'p.ID NOT IN (' . implode( ',', $ajs_excludes_other ) . ')'; | |
} | |
$all_post_types = isset( $options['ajs_other_post_types'] ) ? $options['ajs_other_post_types'] : array(); | |
if ( is_array( $all_post_types ) && count( $all_post_types ) ) { | |
$and_sql[] = 'p.post_type IN (\'' . implode( '\',\'', $all_post_types ) . '\')'; | |
} else { | |
$and_sql[] = 'p.post_type NOT IN (\'product\',\'product_variation\')'; | |
} | |
$left_join = ''; | |
if ( ! empty( $language ) ) { | |
if ( 'wpml' === $language_plug ) { | |
$left_join = ' LEFT JOIN ' . $wpdb->prefix . 'icl_translations AS pl ON pl.element_id = p.ID '; | |
$and_sql[] = 'pl.language_code = \'' . esc_sql( $language ) . '\''; | |
} elseif ( 'polylang' === $language_plug ) { | |
$taxonomy_sql = 'SELECT tt.term_taxonomy_id FROM ' . $wpdb->prefix . 'term_taxonomy AS tt LEFT JOIN ' . $wpdb->prefix . 'terms AS t ON t.term_id = tt.term_id WHERE tt.taxonomy = \'language\' AND t.slug = \'' . esc_sql( $language ) . '\' GROUP BY tt.term_taxonomy_id'; | |
$taxonomy_id = (int) $wpdb->get_var( $taxonomy_sql ); // phpcs:ignore | |
if ( $taxonomy_id ) { | |
$left_join = ' LEFT JOIN ' . $wpdb->prefix . 'term_relationships AS pl ON pl.object_id = p.ID '; | |
$and_sql[] = 'pl.term_taxonomy_id = \'' . $taxonomy_id . '\''; | |
} | |
} | |
} | |
$cquery = 'SELECT COUNT(p.ID) FROM ' . $table . ' AS p ' . $left_join . ' WHERE 1 = 1 '; | |
if ( $return_all ) { | |
$query = 'SELECT p.ID FROM ' . $table . ' AS p ' . $left_join . ' WHERE 1 = 1 '; | |
} else { | |
$query = 'SELECT p.ID, p.post_title, p.post_type, p.post_name FROM ' . $table . ' AS p ' . $left_join . ' WHERE 1 = 1 '; | |
} | |
if ( count( $or_sql ) ) { | |
$cquery .= ' AND ( ' . implode( ' OR ', $or_sql ) . ' ) '; | |
$query .= ' AND ( ' . implode( ' OR ', $or_sql ) . ' ) '; | |
} | |
if ( count( $and_sql ) ) { | |
$cquery .= ' AND ( ' . implode( ' AND ', $and_sql ) . ' ) '; | |
$query .= ' AND ( ' . implode( ' AND ', $and_sql ) . ' ) '; | |
} | |
$query .= ' GROUP BY p.ID '; | |
if ( ! empty( $order_by ) ) { | |
$query .= ' ORDER BY ' . $order_by; | |
} | |
if ( $return_all ) { | |
$post_ids = $wpdb->get_col( $query ); // phpcs:ignore | |
$ids_count = count( $post_ids ); | |
if ( ! $ids_count ) { | |
$post_ids = array( -1 ); | |
} | |
return array( | |
'ids' => $post_ids, | |
'total' => $ids_count, | |
); | |
} else { | |
$query .= ' LIMIT 0, ' . $limit; | |
$posts = $wpdb->get_results( $query ); // phpcs:ignore | |
$total = 0; | |
if ( ! $cgkit_fast_search ) { | |
$total = (int) $wpdb->get_var( $cquery ); // phpcs:ignore | |
} | |
return array( | |
'posts' => $posts, | |
'total' => $total, | |
); | |
} | |
} | |
/** | |
* Check whether server memory exceeded or not. | |
*/ | |
public function memory_exceeded() { | |
$current_memory = memory_get_usage( true ); | |
$return = false; | |
if ( $current_memory >= $this->memory_limit ) { | |
$return = true; | |
} | |
return $return; | |
} | |
/** | |
* Check whether ececution time exceeded or not | |
*/ | |
public function time_exceeded() { | |
$finish = $this->start_time + $this->execution_time; | |
$return = false; | |
if ( time() >= $finish ) { | |
$return = true; | |
} | |
return $return; | |
} | |
/** | |
* Get server memory limit. | |
*/ | |
public function get_memory_limit() { | |
if ( function_exists( 'ini_get' ) ) { | |
$memory_limit = ini_get( 'memory_limit' ); | |
} else { | |
$memory_limit = '128M'; | |
} | |
if ( ! $memory_limit || -1 === (int) $memory_limit ) { | |
$memory_limit = '32G'; | |
} | |
return $this->convert_hr_to_bytes( $memory_limit ); | |
} | |
/** | |
* Convert hr to bytes. | |
* | |
* @param string $value memory limit. | |
*/ | |
public function convert_hr_to_bytes( $value ) { | |
if ( function_exists( 'wp_convert_hr_to_bytes' ) ) { | |
return wp_convert_hr_to_bytes( $value ); | |
} | |
$value = strtolower( trim( $value ) ); | |
$bytes = (int) $value; | |
if ( false !== strpos( $value, 'g' ) ) { | |
$bytes *= GB_IN_BYTES; | |
} elseif ( false !== strpos( $value, 'm' ) ) { | |
$bytes *= MB_IN_BYTES; | |
} elseif ( false !== strpos( $value, 'k' ) ) { | |
$bytes *= KB_IN_BYTES; | |
} | |
$bytes = $bytes * 0.8; | |
return min( $bytes, PHP_INT_MAX ); | |
} | |
/** | |
* Get server execution time. | |
*/ | |
public function get_execution_time() { | |
if ( function_exists( 'ini_get' ) ) { | |
$execution_time = (int) ini_get( 'max_execution_time' ); | |
if ( 0 === $execution_time ) { | |
$execution_time = 300; | |
} | |
if ( $execution_time < 0 ) { | |
$execution_time = 20; | |
} | |
} else { | |
$execution_time = 20; | |
} | |
$execution_time = (int) ( $execution_time * 0.8 ); | |
return $execution_time; | |
} | |
} | |
global $commercekit_ajs_index; | |
$commercekit_ajs_index = new CommerceKit_AJS_Index(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment