prefix . 'bwf_contact'; } /** * Get first contact id * * @param $id * * @return string|null */ public static function get_first_contact_id( $id = 0 ) { global $wpdb; $where = $id ? "AND id > $id" : ''; $query = 'SELECT MIN(id) from ' . self::_table() . ' WHERE 1=1 ' . $where; return $wpdb->get_var( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } public static function get_contacts( $search = '', $limit = 25, $offset = 0, $normalized_filters = array(), $additional_info = array(), $filter_match = ' AND ' ) { $grab_totals = is_array( $additional_info ) && isset( $additional_info['grab_totals'] ) && true === $additional_info['grab_totals']; $only_count = is_array( $additional_info ) && isset( $additional_info['only_count'] ) && true === $additional_info['only_count']; /** Fallback for limit */ if ( false === $only_count && empty( $limit ) ) { $limit = 25; } global $wpdb; self::set_log( array( 'search' => $search, 'limit' => $limit, 'offset' => $offset, 'normalized_filters' => $normalized_filters, 'additional_info' => $additional_info, 'filter_match' => $filter_match, ) ); /** Get Contacts */ $sql_queries = self::_get_contacts_sql( $search, $limit, $offset, $normalized_filters, $additional_info, $filter_match ); self::set_log( 'contact sql: ' . $sql_queries['base'] ); self::log(); $total_count = ''; if ( (bool) $grab_totals || (bool) $only_count ) { /** Total Count (For Pagination) */ $total_count = $wpdb->get_var( $sql_queries['total'] ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( (bool) $only_count ) { return array( 'contacts' => array(), 'total' => $total_count, ); } } $contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /** In case there is DB error and no contacts */ if ( empty( $contacts ) && ! empty( $wpdb->last_error ) ) { BWFAN_Common::log_test_data( $wpdb->last_error, 'collation-issue', true ); BWFAN_Fix_Collation::maybe_fix_collation_issue(); $contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } if ( empty( $contacts ) ) { $error_text = ob_get_clean(); return ! empty( $error_text ) ? $error_text : array(); } /** Set Contacts in Cache */ $ins = new WooFunnels_Contact(); foreach ( $contacts as $contact ) { $ins->set_cache_object( 'cid', $contact['id'], (object) $contact ); } unset( $ins ); return array( 'contacts' => $contacts, 'total' => $total_count, ); } public static function _get_contacts_sql( $search = '', $limit = 25, $offset = 0, $filters = array(), $additional_info = array(), $filter_match = ' AND ', $column_preference = false ) { $should_send_wc = is_array( $additional_info ) && isset( $additional_info['customer_data'] ) && true === $additional_info['customer_data']; $should_send_cf = is_array( $additional_info ) && isset( $additional_info['grab_custom_fields'] ) && true === $additional_info['grab_custom_fields']; $contact_mode = is_array( $additional_info ) && isset( $additional_info['fetch_base'] ) ? absint( $additional_info['fetch_base'] ) : 1; $exclude_unsubs = is_array( $additional_info ) && isset( $additional_info['exclude_unsubs'] ) && true === $additional_info['exclude_unsubs']; $order_by = is_array( $additional_info ) && isset( $additional_info['order_by'] ) && ! empty( $additional_info['order_by'] ) ? $additional_info['order_by'] : 'last_modified'; $order = is_array( $additional_info ) && isset( $additional_info['order'] ) && ! empty( $additional_info['order'] ) ? $additional_info['order'] : 'DESC'; $start_id = is_array( $additional_info ) && isset( $additional_info['start_id'] ) && ! empty( $additional_info['start_id'] ) ? absint( $additional_info['start_id'] ) : 0; $end_id = is_array( $additional_info ) && isset( $additional_info['end_id'] ) && ! empty( $additional_info['end_id'] ) ? absint( $additional_info['end_id'] ) : 0; $exclude_end_id = is_array( $additional_info ) && isset( $additional_info['exclude_end_id'] ) && true === $additional_info['exclude_end_id']; $exclude_ids = is_array( $additional_info ) && isset( $additional_info['exclude_ids'] ) && is_array( $additional_info['exclude_ids'] ) ? $additional_info['exclude_ids'] : array(); $exclude_ids = array_map( 'absint', $exclude_ids ); $exclude_ids = implode( ',', $exclude_ids ); $include_ids = is_array( $additional_info ) && isset( $additional_info['include_ids'] ) && is_array( $additional_info['include_ids'] ) ? $additional_info['include_ids'] : array(); $include_ids = array_map( 'absint', $include_ids ); $include_ids = implode( ',', $include_ids ); global $wpdb; $contact_table = $wpdb->prefix . 'bwf_contact'; $customer_table = $wpdb->prefix . 'bwf_wc_customers'; $contact_fields_table = $wpdb->prefix . 'bwf_contact_fields'; /** Extract status filter, and unset the status filter if unsubscribe value */ $status_filter = array(); if ( ! empty( $filters ) && isset( $filters['c'] ) && ! empty( $filters['c'] ) ) { /** Extract the status filter */ $status_filter = array_filter( $filters['c'], function ( $v ) { return ( isset( $v['key'] ) && 'status' === $v['key'] ); }, ARRAY_FILTER_USE_BOTH ); /** Remove un-subscribe status filter from Contact (c) filters */ if ( ! empty( $status_filter ) ) { $status_filter = reset( $status_filter ); $filters['c'] = array_filter( $filters['c'], function ( $v ) { return ( 'status' !== $v['key'] || 3 !== absint( $v['value'] ) ); }, ARRAY_FILTER_USE_BOTH ); } /** Unset contact filters array if there are no items, to prevent buggy SQL ANDs */ if ( empty( $filters['c'] ) ) { unset( $filters['c'] ); } } /** Contact (wp) and Customer (wc) SQL Queries */ $filter_query = self::_get_filters_sql( $filters, $filter_match ); /** Columns needed and JOINS */ $wp_wc_columns = $should_send_wc ? 'DISTINCT c.*, wc.aov, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'DISTINCT c.*'; $join_wc_query = ( isset( $filters['wc'] ) || $should_send_wc ) ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : ''; $join_cf_query = ''; /** * $should_send_cf - passed from contact export as need to show the data from contact field table */ if ( $should_send_cf || isset( $filters['cm'] ) ) { $cf_columns = self::_get_contact_fields_columns_sql(); if ( ! empty( $cf_columns ) ) { $wp_wc_columns = $should_send_cf ? $wp_wc_columns . ", " . $cf_columns : $wp_wc_columns; $join_cf_query = "LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid"; } } if ( isset( $filters['custom'] ) ) { $join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] ); } $group_by_query = ''; if ( isset( $filters['custom'] ) ) { $group_by_query = apply_filters( 'bwfan_contact_sql_group_by_query', $group_by_query, $filters['custom'] ); $group_by_query = ! empty( $group_by_query ) ? 'GROUP BY ' . $group_by_query : ''; } /** Checking column preferences */ if ( true !== $column_preference ) { /** Columns needed and JOINS */ $wp_wc_columns = $should_send_wc ? 'DISTINCT c.*, wc.aov, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'DISTINCT c.*'; $join_wc_query = ( isset( $filters['wc'] ) || $should_send_wc ) ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : ''; $join_cf_query = ''; if ( $should_send_cf || isset( $filters['cm'] ) ) { $contact_field_columns = self::_get_contact_fields_columns_sql(); $wp_wc_columns = $should_send_cf && ! empty( $contact_field_columns ) ? "$wp_wc_columns, " . $contact_field_columns : $wp_wc_columns; $join_cf_query = "LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid"; } if ( isset( $filters['custom'] ) ) { $join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] ); } } else { $join_cf_query = ''; if ( isset( $filters['custom'] ) ) { $join_cf_query = apply_filters( 'bwfan_contact_sql_join_query', $join_cf_query, $filters['custom'] ); } } /** Implode Filters SQL */ $filter_query = implode( $filter_match, $filter_query ); $filter_query = ! empty( $filter_query ) ? "AND ( $filter_query ) " : ''; /** Filter for final WHERE SQL */ $filter_query = apply_filters( 'bwfan_contact_sql_final_where_query', $filter_query ); $search_query = ''; if ( ! empty( trim( $search ) ) ) { $search = trim( $search ); /** Search Contact by f_name, l_name, contact_no (phone), email */ $search_query = "AND ( c.email like '%" . esc_sql( $search ) . "%' OR c.f_name like '%" . esc_sql( $search ) . "%' OR c.l_name LIKE '%" . esc_sql( $search ) . "%' OR c.contact_no LIKE '%" . esc_sql( $search ) . "%' )"; /** Get f_name and l_name from search string and append in query */ if ( false !== strpos( $search, ' ' ) ) { $search_arr = explode( ' ', $search ); $first_name = isset( $search_arr[0] ) ? $search_arr[0] : ''; $last_name = ! empty( end( $search_arr ) ) ? end( $search_arr ) : ''; $search_query .= ! empty( $first_name ) && ! empty( $last_name ) ? " OR ( c.f_name like '%" . esc_sql( $first_name ) . "%' AND c.l_name like '%" . esc_sql( $last_name ) . "%' )" : ''; } } /** Check if one of Email or Phone must not empty */ $empty_email_check = ( 2 === $contact_mode ) ? "AND ( c.contact_no != '' AND c.contact_no IS NOT NULL )" : "AND ( c.email != '' AND c.email IS NOT NULL )"; /** Excludes Un-subscribers or not */ $unsubs_query = self::_get_unsubscribers_query( $status_filter, $exclude_unsubs ); /** Order, Order By, Limit, Offset */ $order_column_alias = in_array( $order_by, BWFCRM_Filters::$wc_filters ) ? 'wc' : 'c'; $order_by_query = "ORDER BY {$order_column_alias}.{$order_by} {$order}"; $pagination_query = empty( $limit ) ? '' : "limit $offset, $limit"; /** Exclude Contacts from this query */ $exclude_ids_query = ! empty( $exclude_ids ) ? "AND c.id NOT IN ({$exclude_ids})" : ''; /** Include Contacts into this query */ $include_ids_query = ! empty( $include_ids ) ? "AND c.id IN ({$include_ids})" : ''; /** Start ID and End ID queries */ $start_id_query = ! empty( $start_id ) ? "AND c.id > $start_id" : ''; $end_id_operator = ( true === $exclude_end_id ) ? '<' : '<='; $end_id_query = ! empty( $end_id ) ? "AND c.id $end_id_operator $end_id" : ''; if ( true === $column_preference ) { $queries = self::get_column_preferences_query( $join_cf_query, $join_wc_query, $status_filter, $filters, $additional_info ); $base_query = $queries['base_query'] . " $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query $group_by_query $order_by_query $pagination_query"; $total_count_query = $queries['count_query'] . " $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query "; return array( 'base' => $base_query, 'total' => $total_count_query, 'needs_to_added' => $queries['needs_to_added'] ); } $base_query = "SELECT $wp_wc_columns FROM $contact_table as c $join_wc_query $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query $group_by_query $order_by_query $pagination_query"; $total_count_query = "SELECT COUNT(DISTINCT c.id) FROM $contact_table as c $join_wc_query $join_cf_query WHERE 1=1 $empty_email_check $exclude_ids_query $include_ids_query $start_id_query $end_id_query $filter_query $search_query $unsubs_query"; return array( 'base' => $base_query, 'total' => $total_count_query, ); } public static function _get_unsubscribers_query( $filter, $exclude_unsubs = false ) { $exclude_unsub_query = " NOT EXISTS "; $include_unsub_query = " EXISTS "; global $wpdb; $email_query = "(SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub WHERE c.email = unsub.recipient )"; $contact_no_query = "(SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub1 WHERE c.contact_no = unsub1.recipient )"; if ( true === $exclude_unsubs ) { return "AND " . "( $exclude_unsub_query $email_query OR $exclude_unsub_query $contact_no_query )"; } /** Has to be valid filter */ if ( ! is_array( $filter ) || ! isset( $filter['rule'] ) ) { return ''; } /** For subscribed contacts */ if ( true === $exclude_unsubs ) { return "AND " . "( $exclude_unsub_query $email_query AND $exclude_unsub_query $contact_no_query )"; } $filter_value = isset( $filter['value'] ) ? absint( $filter['value'] ) : ''; /** when "status is not unsubscribed" */ if ( 'is_not' === $filter['rule'] && 3 === $filter_value ) { return "AND " . "( $exclude_unsub_query $email_query AND $exclude_unsub_query $contact_no_query )"; } /** Include Un-subscribers when "status is unsubscribed" */ if ( 'is' === $filter['rule'] && 3 === $filter_value ) { return "AND " . "( $include_unsub_query $email_query OR $include_unsub_query $contact_no_query )"; } /** Exclude Un-subscribers if any status other than 3 (unsubscribed) and rule = 'is' */ if ( 'is' === $filter['rule'] && in_array( $filter_value, array( 0, 1, 2 ) ) ) { return "AND " . "( $exclude_unsub_query $email_query AND $exclude_unsub_query $contact_no_query )"; } return ''; } public static function _get_filters_sql( $filters, $filter_match ) { $filter_query = array(); foreach ( $filters as $filter_group_key => $filter_group ) { foreach ( $filter_group as $filter ) { /** Skip if filter doesn't have any or empty value */ if ( ! isset( $filter['value'] ) ) { continue; } switch ( $filter['type'] ) { case BWFCRM_Filters::$TYPE_JSON_ARRAY: self::_set_json_array_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_STRING: self::_set_string_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_STRING_EXACT: self::_set_string_exact_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_DATE: self::_set_date_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_BOOL: self::_set_bool_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_NUMBER: self::_set_number_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_NUMBER_EXACT: self::_set_number_exact_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_CURRENCY: self::_set_currency_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_DATE_RELATIVE: self::_set_date_relative_filter_sql( $filter, $filter_group_key ); break; case BWFCRM_Filters::$TYPE_CUSTOM_FILTER: self::_set_custom_filter_sql( $filter, $filter_group_key ); break; } } if ( ! empty( self::$filter_queries ) ) { /** remove duplicate filter queries */ self::$filter_queries = array_unique( self::$filter_queries ); $filter_query[] = implode( $filter_match, self::$filter_queries ); self::$filter_queries = array(); } } return $filter_query; } public static function _get_contact_fields_columns_sql() { $contact_fields = array_merge( array_keys( BWFCRM_Fields::$contact_fields ), array_keys( BWFCRM_Fields::$contact_address_fields ) ); $db_fields = BWFAN_Model_Fields::get_fields_by_multiple_slugs( $contact_fields ); if ( empty( $db_fields ) ) { return ''; } $columns = []; foreach ( $db_fields as $slug => $db_field ) { $columns[] = "cm.f{$db_field['ID']} AS '$slug'"; } return implode( ', ', $columns ); } public static function _set_json_array_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; /** Checking if value is not array and group key is from custom field */ if ( ! is_array( $filter_value ) || 'cm' === $filter_group_key ) { $filter_value = array( $filter_value ); } foreach ( $filter_value as $val_item ) { $val_item = ! is_array( $val_item ) ? explode( ',', $val_item ) : $val_item; $val_item = array_map( 'trim', $val_item ); $key = $filter['key']; if ( 'is_blank' === $filter['rule'] ) { self::$filter_queries[] = "( {$filter_group_key}.{$key} IS NULL OR {$filter_group_key}.{$key} LIKE '[]' )"; continue; } if ( 'is_not_blank' === $filter['rule'] ) { self::$filter_queries[] = "( {$filter_group_key}.{$key} IS NOT NULL AND {$filter_group_key}.{$key} NOT LIKE '[]' )"; continue; } $rule = in_array( $filter['rule'], array( 'contains', 'is', 'any', 'all' ) ) ? 'LIKE' : 'NOT LIKE'; $sub_rule = ( 'LIKE' === $rule ) && ( 'all' !== $filter['rule'] ) ? 'OR' : 'AND'; $val_item = array_map( function ( $val ) use ( $rule, $key, $sub_rule, $filter_group_key ) { return "$filter_group_key.$key $rule '%\"$val\"%'"; }, $val_item ); $val_item = array_filter( $val_item ); $val_item = implode( " {$sub_rule} ", $val_item ); $if_column_exists = ( 'NOT LIKE' === $rule ) ? "{$filter_group_key}.{$key} IS NULL OR" : ''; $val_item = ! empty( $if_column_exists ) ? "($if_column_exists ($val_item))" : "($val_item)"; /** Group query set, not need to go further, so continue */ self::$filter_queries[] = $val_item; } } public static function _set_string_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; $filter_rule = $filter['rule']; $filter_key = $filter['key']; if ( ( 'country' === $filter_key || 'state' === $filter_key ) && strpos( $filter_value, ',' ) ) { $filter_value = explode( ',', $filter_value ); } switch ( $filter_rule ) { case 'is': $filter_rule = is_array( $filter_value ) ? 'IN' : '='; break; case 'is_not': $filter_rule = is_array( $filter_value ) ? 'NOT IN' : '!='; break; case 'contains': $filter_rule = 'LIKE'; $filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) { $fil_val = trim( $fil_val ); return "{$filter_group_key}.{$filter_key} LIKE '%$fil_val%'"; }, $filter_value ) : "%{$filter_value}%"; break; case 'not_contains': $filter_rule = 'NOT LIKE'; $filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) { $fil_val = trim( $fil_val ); return "{$filter_group_key}.{$filter_key} NOT LIKE '%$fil_val%'"; }, $filter_value ) : "%{$filter_value}%"; break; case 'starts_with': $filter_rule = 'LIKE'; $filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) { $fil_val = trim( $fil_val ); return "{$filter_group_key}.{$filter_key} LIKE '$fil_val%'"; }, $filter_value ) : "{$filter_value}%"; break; case 'ends_with': $filter_rule = 'LIKE'; $filter_value = is_array( $filter_value ) ? array_map( function ( $fil_val ) use ( $filter_group_key, $filter_key ) { $fil_val = trim( $fil_val ); return "{$filter_group_key}.{$filter_key} LIKE '%$fil_val'"; }, $filter_value ) : "%{$filter_value}"; break; case 'is_blank': $filter_rule = '='; $filter_value = ''; break; case 'is_not_blank': $filter_rule = '!='; $filter_value = ''; break; default: return; } $if_column_exists = ''; if ( in_array( $filter['rule'], array( 'not_contains', 'is_not', 'is_blank' ) ) ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR"; } if ( 'is_not_blank' === $filter['rule'] ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NOT NULL AND"; } if ( 'state' === $filter_key && is_array( $filter_value ) && in_array( $filter['rule'], array( 'contains', 'not_contains', 'starts_with', 'ends_with' ), true ) ) { $filter_value = array_filter( $filter_value ); $sub_rule = ( 'LIKE' === $filter_rule ) ? 'OR' : 'AND'; $filter_value = implode( " {$sub_rule} ", $filter_value ); self::$filter_queries[] = "($if_column_exists $filter_value)"; return; } if ( is_array( $filter_value ) ) { $filter_value = "('" . implode( "','", array_map( 'trim', $filter_value ) ) . "')"; } else { $filter_value = "'$filter_value'"; } self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value)"; } public static function _set_string_exact_filter_sql( $filter, $filter_group_key ) { self::_set_string_filter_sql( $filter, $filter_group_key ); } public static function _set_number_exact_filter_sql( $filter, $filter_group_key ) { self::_set_number_filter_sql( $filter, $filter_group_key ); } public static function _set_date_filter_sql( $filter, $filter_group_key, $null_checking = true ) { $filter_key = $filter['key']; if ( empty( $filter['value'] ) ) { return ''; } if ( is_array( $filter['value'] ) && 1 === count( $filter['value'] ) ) { $filter['value'] = $filter['value'][0]; } if ( is_array( $filter['value'] ) ) { /** For 'between' rule */ $filter_before_date = bwfcrm_maybe_get_datetime( $filter['value'][0] ); $filter_after_date = bwfcrm_maybe_get_datetime( $filter['value'][1] ); $filter_before_date = date( 'Y-m-d', $filter_before_date->getTimestamp() ); $filter_after_date = date( 'Y-m-d', $filter_after_date->getTimestamp() ); $filter_before_date = "$filter_before_date 00:00:00"; $filter_after_date = "$filter_after_date 23:59:59"; $filter_rule = '='; } else { $filter_value = bwfcrm_maybe_get_datetime( $filter['value'] ); $filter_date = date( 'Y-m-d', $filter_value->getTimestamp() ); $filter_before_date = "$filter_date 00:00:00"; $filter_after_date = "$filter_date 23:59:59"; $filter_rule = ( 'is' === $filter['rule'] ) ? '=' : ( 'before' === $filter['rule'] ? '<' : '>' ); } $where = ''; if ( isset( $filter_rule ) && '=' !== $filter_rule ) { /** For 'more_than' and 'less_than' rules */ $date_value = ( ( '>' === $filter_rule ) ? $filter_after_date : $filter_before_date ); $where = "($filter_group_key.$filter_key $filter_rule '$date_value')"; } else { /** For 'is' and 'between' rules */ $where = "($filter_group_key.$filter_key >= '$filter_before_date' AND $filter_group_key.$filter_key <= '$filter_after_date')"; } self::$filter_queries[] = $where; return $where; } public static function _set_bool_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; $filter_key = $filter['key']; switch ( $filter_key ) { case 'status': $filter_value = ( ( 'yes' === $filter_value ) ? 1 : 0 ); self::$filter_queries[] = "$filter_group_key.$filter_key = $filter_value"; return; case 'has_purchased': if ( 'yes' === $filter_value ) { self::$filter_queries[] = 'wc.total_order_count > 0'; } else { self::$filter_queries[] = '(wc.total_order_count = 0 OR wc.total_order_count IS NULL )'; } return; case 'has_used_any_coupons': if ( 'yes' === $filter_value ) { self::$filter_queries[] = "(wc.used_coupons IS NOT NULL AND wc.used_coupons != '' AND wc.used_coupons != '[]')"; } else { self::$filter_queries[] = "(wc.used_coupons IS NULL OR wc.used_coupons = '' OR wc.used_coupons = '[]')"; } return; } } public static function _set_number_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; $filter_rule = $filter['rule']; $filter_key = $filter['key']; /** Only on Status filter */ /** Include Un-subscribers if any status other than 3 (unsubscribed) and rule = 'is_not' */ $include_unsub_query_or = ''; if ( 'status' === $filter_key && 'is_not' === $filter_rule && in_array( $filter_value, array( 0, 1, 2 ) ) ) { /** Using OR query because contact maybe not fall within status (0,1,2), but is present in unsubscribed table */ global $wpdb; $include_unsub_query_or = "OR EXISTS (SELECT 1 FROM {$wpdb->prefix}bwfan_message_unsubscribe AS unsub WHERE c.email = unsub.recipient )"; } switch ( $filter_rule ) { case 'is': $filter_rule = '='; break; case 'is_not': $filter_rule = '!='; break; case 'more_than': $filter_rule = '>'; break; case 'more_than_equal': $filter_rule = '>='; break; case 'less_than': $filter_rule = '<'; break; case 'less_then_equal': $filter_rule = '<='; break; case 'is_blank': $filter_rule = '='; $filter_value = 0; break; case 'is_not_blank': $filter_rule = '!='; $filter_value = 0; break; } $if_column_exists = ''; if ( in_array( $filter['rule'], array( 'less_than', 'is_not', 'is_blank', 'less_than_equal' ) ) ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR"; } if ( 'is_not_blank' === $filter['rule'] ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NOT NULL AND"; } /** check for property in case value 0 and rule is equal */ if ( in_array( $filter['rule'], array( 'less_than', 'is' ) ) && 0 === absint( $filter_value ) ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR"; } /** check for property in case value 0 and rule is not equal */ if ( 'is_not' === $filter['rule'] && 0 === absint( $filter_value ) ) { $if_column_exists = ''; } if ( ! is_array( $filter_value ) ) { self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value $include_unsub_query_or)"; return; } foreach ( $filter_value as $values ) { $values = trim( $values ); self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $values $include_unsub_query_or)"; } } public static function _set_currency_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; $filter_rule = $filter['rule']; $filter_key = $filter['key']; switch ( $filter_rule ) { case 'more_than': $filter_rule = '>'; break; case 'more_than_equal': $filter_rule = '>='; break; case 'less_than': $filter_rule = '<'; break; case 'less_then_equal': $filter_rule = '<='; break; default: return; } $if_column_exists = ''; if ( in_array( $filter['rule'], array( 'less_than', 'less_than_equal' ) ) ) { $if_column_exists = "{$filter_group_key}.{$filter_key} IS NULL OR"; } if ( ! is_array( $filter_value ) ) { self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $filter_value)"; return; } foreach ( $filter_value as $values ) { $values = trim( $values ); self::$filter_queries[] = "($if_column_exists $filter_group_key.$filter_key $filter_rule $values)"; } } public static function _set_date_relative_filter_sql( $filter, $filter_group_key ) { $filter_key = $filter['key']; if ( empty( $filter['value'] ) ) { return; } $filter_value = ''; if ( ! is_array( $filter['value'] ) ) { $filter_value = absint( $filter['value'] ); $date = $datetime = new DateTime( 'now', wp_timezone() ); $date->modify( "-$filter_value days" ); $filter_value = $date->format( 'Y-m-d' ); } else { $filter_value = array(); $from = absint( $filter['value'][0] ); $date = $datetime = new DateTime( 'now', wp_timezone() ); $date->modify( "-$from days" ); $filter_value[] = $date->format( 'Y-m-d' ); $to = absint( $filter['value'][1] ); $date = $datetime = new DateTime( 'now', wp_timezone() ); $date->modify( "-$to days" ); $filter_value[] = $date->format( 'Y-m-d' ); } switch ( $filter['rule'] ) { case 'over': $filter_rule = '<'; break; case 'past': $filter_rule = '>='; break; case 'between': $filter_rule = 'between'; break; default: return; } if ( ! is_array( $filter_value ) ) { self::$filter_queries[] = "($filter_group_key.$filter_key $filter_rule '$filter_value')"; } else { self::$filter_queries[] = "($filter_group_key.$filter_key >= '" . $filter_value[1] . "' AND $filter_group_key.$filter_key <= '" . $filter_value[0] . "')"; } } public static function _set_custom_filter_sql( $filter, $filter_group_key ) { $filter_value = $filter['value']; $filter_rule = $filter['rule']; $filter_key = $filter['key']; $filter_query = apply_filters( 'bwfan_contact_sql_where_query', '', $filter_key, $filter_rule, $filter_value ); if ( empty( $filter_query ) ) { return; } self::$filter_queries[] = $filter_query; } public static function get_automations( $contact_id ) { if ( 0 === $contact_id ) { return array(); } global $wpdb; $query = "Select automation_id, time from {$wpdb->prefix}bwfan_contact_automations where contact_id ='" . $contact_id . "' ORDER BY time DESC"; $contact_automations = $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( empty( $contact_automations ) ) { return array(); } return $contact_automations; } /** * @param $contact_id * * @return bool */ public static function delete_contact( $contact_id ) { global $wpdb; $contact_table = $wpdb->prefix . 'bwf_contact'; $contact_meta_table = $wpdb->prefix . 'bwf_contact_meta'; $contact_customer_table = $wpdb->prefix . 'bwf_wc_customers'; $contact_fields_table = $wpdb->prefix . 'bwf_contact_fields'; $contact_note_table = $wpdb->prefix . 'bwfan_contact_note'; $contact_automations_table = $wpdb->prefix . 'bwfan_contact_automations'; $contact_conversions_table = $wpdb->prefix . 'bwfan_conversions'; $contact_engagement_table = $wpdb->prefix . 'bwfan_engagement_tracking'; $contact_engagement_meta_table = $wpdb->prefix . 'bwfan_engagement_trackingmeta'; $contact_unsubscribe_table = $wpdb->prefix . 'bwfan_message_unsubscribe'; $automation_contact = $wpdb->prefix . 'bwfan_automation_contact'; $automation_complete_contact = $wpdb->prefix . 'bwfan_automation_complete_contact'; $automation_contact_trail = $wpdb->prefix . 'bwfan_automation_contact_trail'; $contact_email = $wpdb->get_col( "select email from {$contact_table} where id=$contact_id" ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $contact_phone = $wpdb->get_col( "select contact_no from {$contact_table} where id=$contact_id" ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $delete_contact = $wpdb->delete( $contact_table, array( 'id' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( ! $delete_contact ) { /** If no contact then return false */ return false; } $wpdb->delete( $contact_meta_table, array( 'contact_id' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $contact_customer_table, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $contact_fields_table, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $contact_note_table, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /** check if the automation run for v1 */ if ( BWFAN_Common::is_automation_v1_active() ) { $wpdb->delete( $contact_automations_table, array( 'contact_id' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } $wpdb->delete( $contact_conversions_table, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /** Delete complete and active Automation by contact id */ $wpdb->delete( $automation_contact, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $automation_complete_contact, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $automation_contact_trail, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '_woofunnel_cid' AND meta_value = $contact_id" ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( "DELETE FROM {$wpdb->prefix}postmeta WHERE meta_key LIKE '_woofunnel_custid' AND meta_value = $contact_id" ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $engagement_meta_query = "DELETE $contact_engagement_meta_table FROM $contact_engagement_meta_table INNER JOIN $contact_engagement_table ON $contact_engagement_table.id = $contact_engagement_meta_table.eid WHERE $contact_engagement_table.cid = $contact_id"; /** delete from engagement meta and engagement table */ $wpdb->query( $engagement_meta_query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->delete( $contact_engagement_table, array( 'cid' => $contact_id ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /** delete from the unsubscribe table */ if ( is_array( $contact_email ) && ! empty( $contact_email[0] ) ) { $wpdb->delete( $contact_unsubscribe_table, array( 'recipient' => $contact_email[0] ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } if ( is_array( $contact_phone ) && ! empty( $contact_phone[0] ) ) { $wpdb->delete( $contact_unsubscribe_table, array( 'recipient' => $contact_phone[0] ) ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } return true; } /** * @param $contact_ids * * @return bool */ public static function delete_multiple_contacts( $contact_ids ) { global $wpdb; /** Remove Contacts from every other place */ $contact_table = $wpdb->prefix . 'bwf_contact'; $contact_meta_table = $wpdb->prefix . 'bwf_contact_meta'; $contact_customer_table = $wpdb->prefix . 'bwf_wc_customers'; $contact_fields_table = $wpdb->prefix . 'bwf_contact_fields'; $ids = implode( ',', array_map( 'absint', $contact_ids ) ); $status = true; foreach ( $contact_ids as $contact_id ) { $status = self::delete_contact( $contact_id ); } return $status; } public static function get_contact_aov( $contact_id ) { global $wpdb; if ( empty( $contact_id ) ) { return 0; } $contact_id = intval( $contact_id ); $contact_orders_total_results = []; if ( method_exists( 'BWF_WC_Compatibility', 'is_hpos_enabled' ) && BWF_WC_Compatibility::is_hpos_enabled() ) { $contact_orders_total_query = "SELECT count(wc_stats.order_id) as orders, SUM(wc_stats.total_sales) as total FROM {$wpdb->prefix}wc_order_stats as wc_stats LEFT JOIN {$wpdb->prefix}wc_orders_meta pm ON wc_stats.order_id =pm.order_id WHERE wc_stats.status = 'wc-completed' and pm.meta_key='_woofunnel_cid' and pm.meta_value=$contact_id"; $contact_orders_total_results = $wpdb->get_results( $contact_orders_total_query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( empty( $contact_orders_total_results ) ) { return 0; } $aov_contact_order_total = 0; if ( ! empty( $contact_orders_total_results[0]['orders'] ) && ! empty( $contact_orders_total_results[0]['total'] ) ) { $aov_contact_order_total = ( $contact_orders_total_results[0]['total'] / $contact_orders_total_results[0]['orders'] ); } return $aov_contact_order_total; } if ( empty( $contact_orders_total_results ) ) { $contact_orders_total_query = "SELECT count(wc_stats.order_id) as orders, SUM(wc_stats.total_sales) as total FROM {$wpdb->prefix}wc_order_stats as wc_stats LEFT JOIN {$wpdb->prefix}postmeta pm ON wc_stats.order_id =pm.post_id WHERE wc_stats.status = 'wc-completed' and pm.meta_key='_woofunnel_cid' and pm.meta_value=$contact_id"; $contact_orders_total_results = $wpdb->get_results( $contact_orders_total_query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } if ( empty( $contact_orders_total_results ) ) { return 0; } $aov_contact_order_total = 0; if ( ! empty( $contact_orders_total_results[0]['orders'] ) && ! empty( $contact_orders_total_results[0]['total'] ) ) { $aov_contact_order_total = ( $contact_orders_total_results[0]['total'] / $contact_orders_total_results[0]['orders'] ); } return $aov_contact_order_total; } /** * @param $contact_id * * @return array|object */ public static function get_bump_details( $contact_id ) { if ( empty( $contact_id ) ) { return array(); } global $wpdb; $bump_data['accepted'] = 0; $bump_data['revenue'] = 0; $bump_query = "SELECT bump.bid as 'object_id', bump.total as 'total_revenue', 'bump' as 'type' FROM {$wpdb->prefix}wfob_stats AS bump WHERE bump.cid='" . $contact_id . "' AND bump.converted=1"; $bump_results = $wpdb->get_results( $bump_query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( empty( $bump_results ) ) { return $bump_data; } $bump_data['accepted'] = count( $bump_results ); $total_revenue_array = array_column( $bump_results, 'total_revenue' ); $bump_data['revenue'] = number_format( array_sum( $total_revenue_array ), 2 ); return $bump_data; } /** * @param $contact_id * * @return array|object */ public static function get_upstroke_details( $contact_id ) { if ( empty( $contact_id ) ) { return array(); } global $wpdb; $upstroke_data['accepted'] = 0; $upstroke_query = "SELECT event.action_type_id,event.value FROM {$wpdb->prefix}wfocu_event as event LEFT JOIN {$wpdb->prefix}wfocu_session as session ON event.sess_id = session.id WHERE (event.action_type_id = 4) AND session.cid='" . $contact_id . "'"; $upstroke_result = $wpdb->get_results( $upstroke_query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( empty( $upstroke_result ) ) { return $upstroke_data; } $upstroke_data['accepted'] = count( $upstroke_result ); $total_revenue_array = array_column( $upstroke_result, 'value' ); $upstroke_data['revenue'] = array_sum( $total_revenue_array ); return $upstroke_data; } /** * @param $contact_id * @param $note_id * * @return bool */ public static function delete_notes( $contact_id, $note_id ) { if ( empty( $contact_id ) ) { return false; } global $wpdb; $contact_note_table = $wpdb->prefix . 'bwfan_contact_note'; $where = array(); if ( empty( $note_id ) ) { $where['cid'] = $contact_id; } else { $where['id'] = $note_id; $where['cid'] = $contact_id; } $delete_contact_notes = $wpdb->delete( $contact_note_table, $where ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( ! $delete_contact_notes ) { return false; } return true; } /** * @param $contact_id * * @return mixed */ public static function get_checkout_details( $contact_id ) { global $wpdb; $query = "SELECT count(aero.wfacp_id) as total_orders, sum(aero.total_revenue) as 'total_revenue' FROM " . $wpdb->prefix . 'bwf_contact' . ' AS contact JOIN ' . $wpdb->prefix . 'wfacp_stats' . " AS aero ON contact.id=aero.cid WHERE aero.cid=$contact_id ORDER BY aero.date DESC"; $checkout_data = $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $return = array(); $return['orders'] = ( isset( $checkout_data[0] ) && isset( $checkout_data[0]['total_orders'] ) ) ? absint( $checkout_data[0]['total_orders'] ) : 0; $return['revenue'] = ( isset( $checkout_data[0] ) && isset( $checkout_data[0]['total_revenue'] ) ) ? $checkout_data[0]['total_revenue'] : 0; return $return; } /** * @param $contact_id * * @return int|mixed */ public static function get_optin_details( $contact_id ) { global $wpdb; $optin_submission = 0; $query = 'SELECT count(optin.id) as total_submission FROM ' . $wpdb->prefix . 'bwf_contact' . ' AS contact JOIN ' . $wpdb->prefix . 'bwf_optin_entries' . " AS optin ON contact.id=optin.cid WHERE optin.cid=$contact_id ORDER BY optin.date DESC"; $optin_data = $wpdb->get_results( $query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( ! isset( $optin_data[0]['total_submission'] ) || empty( $optin_data[0]['total_submission'] ) ) { return $optin_submission; } return $optin_data[0]['total_submission']; } /** * @param int $limit * @param int $offset * @param false $wc_data_send * * @return array */ public static function get_recent_contacts( $limit = 10, $offset = 0, $wc_data_send = false ) { $additional_info = array( 'order_by' => 'creation_date', 'order' => 'DESC', 'exclude_unsubs' => true, 'customer_data' => $wc_data_send, ); // $saved_last_id = get_option( 'bwfan_show_contacts_from' ); // if ( ! empty( $saved_last_id ) ) { // $additional_info['start_id'] = $saved_last_id; // } return self::get_contacts( '', $limit, $offset, array(), $additional_info ); } /** * @param int $limit * @param int $offset * @param false $wc_data_send * * @return array */ public static function get_recent_unsubscribers( $limit = 10, $offset = 0, $wc_data_send = false ) { return self::get_contacts( '', $limit, $offset, array(), array( 'order_by' => 'last_modified', 'order' => 'DESC', 'exclude_unsubs' => false, 'customer_data' => $wc_data_send, ) ); } /** * @param int $limit * @param int $offset * @param false $wc_get_data * * @return array */ public static function get_recent_abandoned( $limit = 10, $offset = 0, $wc_get_data = false ) { global $wpdb; $contact_table = $wpdb->prefix . 'bwf_contact'; $cart_table = $wpdb->prefix . 'bwfan_abandonedcarts'; $customer_table = $wpdb->prefix . 'bwf_wc_customers'; $wp_wc_columns = $wc_get_data ? 'c.*, wc.id as customer_id, wc.l_order_date, wc.f_order_date, wc.total_order_count, wc.total_order_value, wc.purchased_products, wc.purchased_products_cats, wc.purchased_products_tags, wc.used_coupons' : 'c.*'; $join_wc_query = $wc_get_data ? "LEFT JOIN $customer_table as wc ON c.id = wc.cid" : ''; $cart_join = "LEFT JOIN $contact_table as c ON ab.email = c.email"; $base_query = "SELECT $wp_wc_columns FROM $cart_table as ab $cart_join $join_wc_query WHERE 1=1 and ab.status IN (0,1,3,4) and c.id IS NOT NULL ORDER BY ab.last_modified DESC LIMIT $offset,$limit"; $contacts = $wpdb->get_results( $base_query, ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching if ( ! is_array( $contacts ) || 0 === count( $contacts ) ) { return array( 'contacts' => array(), 'total' => 0, ); } $total_count = count( $contacts ); return array( 'contacts' => $contacts, 'total' => $total_count, ); } public static function get_last_contact_id() { global $wpdb; $table_name = self::_table(); $query = "SELECT max(id) as last_id FROM $table_name"; return $wpdb->get_var( $query ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } public static function set_log( $log ) { if ( empty( $log ) ) { return; } self::$logs[] = array( 't' => microtime( true ), 'd' => $log, ); } public static function log() { if ( ! is_array( self::$logs ) || 0 === count( self::$logs ) ) { return; } if ( false === apply_filters( 'bwfan_allow_contact_query_logging', false ) ) { return; } add_filter( 'bwfan_before_making_logs', '__return_true' ); BWFAN_Core()->logger->log( print_r( self::$logs, true ), 'fka-contacts-query' ); self::$logs = []; } /** * Get contacts with column preferences * */ public static function get_contact_listing( $search = '', $limit = 25, $offset = 0, $normalized_filters = array(), $additional_info = array(), $filter_match = ' AND ' ) { global $wpdb; /** Get Contacts */ $sql_queries = self::_get_contacts_sql( $search, $limit, $offset, $normalized_filters, $additional_info, $filter_match, true ); $contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching /** In case there is DB error and no contacts */ if ( empty( $contacts ) && ! empty( $wpdb->last_error ) ) { BWFAN_Common::log_test_data( $wpdb->last_error, 'collation-issue', true ); BWFAN_Fix_Collation::maybe_fix_collation_issue(); $contacts = $wpdb->get_results( $sql_queries['base'], ARRAY_A ); //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching } $contacts = self::prepared_data( $contacts, $sql_queries['needs_to_added'] ); return array( 'contacts' => $contacts, 'total' => $wpdb->get_var( $sql_queries['total'] ) //phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching ); } /** * Preparing the data for get contacts with column preference * * @param $contacts * @param $columns_needs_to_add * * @return array|void[] */ public static function prepared_data( $contacts, $columns_needs_to_add ) { if ( empty( $contacts ) ) { return []; } return array_map( function ( $contact ) use ( $columns_needs_to_add ) { foreach ( $contact as $slug => $value ) { $date_columns = [ 'dob', 'last-login', 'last-open', 'last-click', 'last-sent' ]; if ( false !== strpos( $slug, 'date' ) || in_array( $slug, $date_columns, true ) ) { $contact[ $slug ] = self::get_formatted_date( $value ); continue; } if ( false !== strpos( $slug, 'bwf_cf' ) ) { $field_id = str_replace( 'bwf_cf', '', $slug ); $field_type = BWFAN_Model_Fields::get_field_type( $field_id ); switch ( absint( $field_type ) ) { case BWFCRM_Fields::$TYPE_DATE : $value = self::get_formatted_date( $value ); break; case BWFCRM_Fields::$TYPE_CHECKBOX : $options = ! empty( $value ) ? json_decode( $value, true ) : []; $value = []; if ( ! empty( $options ) ) { foreach ( $options as $option ) { $value[] = [ 'id' => $option, 'name' => $option, ]; } } break; } $contact[ $slug ] = $value; } } /** if contact is un-subscribed then status set as 3 */ if ( isset( $contact['status'] ) ) { $email = isset( $contact['email'] ) ? $contact['email'] : ''; $contact_no = isset( $contact['contact_no'] ) ? $contact['contact_no'] : ''; $is_unsubscribed = self::is_contact_unsubscribed( $email, $contact_no ); $contact['status'] = ! empty( $is_unsubscribed ) ? 3 : $contact['status']; } /** Get Purchased Products titles */ if ( isset( $contact['purchased_products'] ) ) { $purchased_products = ! empty( $contact['purchased_products'] ) ? json_decode( $contact['purchased_products'], true ) : []; $contact['purchased_products'] = bwfan_is_woocommerce_active() ? array_filter( array_map( function ( $product_id ) { $product = wc_get_product( $product_id ); if ( ! $product instanceof WC_Product ) { return false; } $pid = $product->is_type( 'variation' ) ? $product->get_parent_id() : $product->get_id(); return ! $product->is_type( 'variable' ) ? array( 'id' => $pid, 'name' => $product->get_name( 'edit' ), 'link' => get_edit_post_link( $pid ), ) : false; }, array_unique( $purchased_products ) ) ) : []; } /** Get Purchased Products Cats */ if ( isset( $contact['purchased_products_cats'] ) ) { $purchased_products_cats = ! empty( $contact['purchased_products_cats'] ) ? json_decode( $contact['purchased_products_cats'], true ) : []; $purchased_products_cats = array_map( function ( $term_id ) { $cat = get_term( absint( $term_id ) ); return $cat instanceof WP_Term ? array( 'id' => $cat->term_id, 'name' => $cat->name, 'link' => get_edit_term_link( $cat ), ) : false; }, array_unique( $purchased_products_cats ) ); $contact['purchased_products_cats'] = array_values( array_filter( $purchased_products_cats ) ); } /** Get Purchased Products Tags */ if ( isset( $contact['purchased_products_tags'] ) ) { $purchased_products_tags = ! empty( $contact['purchased_products_tags'] ) ? json_decode( $contact['purchased_products_tags'], true ) : []; $purchased_products_tags = array_map( function ( $term_id ) { $tag = get_term( absint( $term_id ) ); return $tag instanceof WP_Term ? array( 'id' => $tag->term_id, 'name' => $tag->name, 'link' => get_edit_term_link( $tag ), ) : false; }, array_unique( $purchased_products_tags ) ); $contact['purchased_products_tags'] = array_values( array_filter( $purchased_products_tags ) ); } /** Get Used coupons */ if ( isset( $contact['used_coupons'] ) ) { $contact['used_coupons'] = isset( $contact['used_coupons'] ) ? json_decode( $contact['used_coupons'], true ) : []; $contact['used_coupons'] = array_map( function ( $coupon ) { return [ 'id' => $coupon, 'name' => $coupon, ]; }, $contact['used_coupons'] ); } /** Get list and tags */ if ( isset( $contact['tags'] ) ) { $tags = json_decode( $contact['tags'], true ); $contact['tags'] = ! empty( $tags ) ? BWFCRM_Tag::get_tags( $tags ) : []; } if ( isset( $contact['lists'] ) ) { $lists = json_decode( $contact['lists'], true ); $contact['lists'] = ! empty( $lists ) ? BWFCRM_Lists::get_lists( $lists ) : []; } if ( bwfcrm_is_woocommerce_active() && isset( $contact['total_order_value'] ) ) { $contact['total_order_value'] = ! empty( $contact['total_order_value'] ) ? wc_price( $contact['total_order_value'] ) : ''; } if ( bwfcrm_is_woocommerce_active() && isset( $contact['aov'] ) ) { $contact['aov'] = ! empty( $contact['aov'] ) ? wc_price( $contact['aov'] ) : ''; } if ( ! empty( $columns_needs_to_add ) && is_array( $columns_needs_to_add ) ) { foreach ( $columns_needs_to_add as $column ) { $value = ''; switch ( true ) { case 'has_purchased' === $column : $value = ! empty( $contact['purchased_products'] ); break; case 'has_used_any_coupons' === $column : $value = ! empty( $contact['used_coupons'] ); break; } $contact[ $column ] = $value; } } return apply_filters( 'bwfcrm_modify_contact_columns_preferences_data', $contact ); }, $contacts ); } public static function get_formatted_date( $date ) { if ( empty( $date ) || ! strtotime( $date ) ) { return ''; } $format = get_option( 'date_format', 'Y-m-d' ); return date( $format, strtotime( $date ) ); } /** * Get query for get contacts with columns preference */ public static function get_column_preferences_query( $join_cf_query, $join_wc_query, $status_filter, $filters, $additional_info ) { global $wpdb; $contact_table = $wpdb->prefix . 'bwf_contact'; $customer_table = $wpdb->prefix . 'bwf_wc_customers'; $contact_fields_table = $wpdb->prefix . 'bwf_contact_fields'; $unsubscribe_table = $wpdb->prefix . 'bwfan_message_unsubscribe'; $columns = self::get_columns_for_query(); $contact_details = isset( $columns['contact_details'] ) && is_array( $columns['contact_details'] ) ? $columns['contact_details'] : []; $segments = isset( $columns['segments'] ) && is_array( $columns['segments'] ) ? $columns['segments'] : []; $custom_fields = isset( $columns['contact_custom_fields'] ) && is_array( $columns['contact_custom_fields'] ) ? $columns['contact_custom_fields'] : []; $wc_columns = isset( $columns['woocommerce'] ) && is_array( $columns['woocommerce'] ) ? $columns['woocommerce'] : []; $geography = isset( $columns['geography'] ) && is_array( $columns['geography'] ) ? $columns['geography'] : []; $engagement = isset( $columns['engagement'] ) && is_array( $columns['engagement'] ) ? $columns['engagement'] : []; $total_columns = array_merge( $contact_details, $custom_fields ); $total_columns = array_merge( $total_columns, $segments ); $total_columns = array_merge( $total_columns, $wc_columns ); $total_columns = array_merge( $total_columns, $geography ); $total_columns = array_merge( $total_columns, $engagement ); $default_columns = "c.id,c.f_name,c.l_name,c.email,c.contact_no,c.creation_date"; $join_query = ''; /** If Join is already in custom filter query */ if ( false === strpos( $join_cf_query, 'bwf_contact_fields' ) ) { $join_query = " LEFT JOIN $contact_fields_table as `cm` ON c.id = cm.cid "; } if ( false === strpos( $join_cf_query, 'bwf_wc_customers' ) ) { $join_query .= ( ! empty( $wc_columns ) || isset( $filters['wc'] ) ) || ( empty( $wc_columns ) && ! empty( $join_wc_query ) ) ? " LEFT JOIN $customer_table as wc ON c.id = wc.cid" : ''; } $query_columns = apply_filters( 'bwfcrm_contact_columns_preferences', array( 'total_columns' => $total_columns, 'join_query' => $join_query ), $columns, $join_cf_query ); $join_query = isset( $query_columns['join_query'] ) ? $query_columns['join_query'] : $join_query; $total_columns = isset( $query_columns['total_columns'] ) ? $query_columns['total_columns'] : $total_columns; $total_columns = isset( $total_columns ) ? implode( ',', array_filter( $total_columns ) ) : ''; $total_columns = ! empty( $total_columns ) ? $default_columns . ',' . $total_columns : $default_columns; return [ 'base_query' => "SELECT $total_columns FROM $contact_table AS c $join_query ", 'count_query' => "SELECT COUNT(DISTINCT c.id) FROM $contact_table as c $join_query ", 'needs_to_added' => $columns['need_to_add'] ]; } /** * Get preferences columns for query * */ public static function get_columns_for_query() { $saved_format = BWFAN_Common::get_contact_columns(); if ( empty( $saved_format ) ) { return [ 'need_to_add' => [] ]; } $columns = []; $modified_columns = [ 'has_purchased' => 'purchased_products', 'has_used_any_coupons' => 'used_coupons', ]; $system_fields = [ 'address-1', 'address-2', 'city', 'postcode', 'company', 'gender', 'dob', 'link-trigger-click', 'last-login', 'last-open', 'last-click', 'last-sent' ]; $need_to_add_columns = []; $added_columns = []; foreach ( $saved_format as $fields ) { foreach ( $fields as $slug => $field ) { if ( 'label' === $slug || 'groupKey' === $slug || 'groupLabel' === $slug ) { continue; } /** If system and default field is not exists in DB */ if ( in_array( $slug, $system_fields, true ) && empty( BWFCRM_Fields::get_field_id_by_slug( $slug ) ) ) { continue; } /** Get need to add and modify columns */ if ( in_array( $slug, array_keys( $modified_columns ), true ) ) { $need_to_add_columns[] = $slug; } /** Default columns so no need to add */ if ( in_array( $slug, [ 'f_name', 'l_name', 'email', 'creation_date', 'contact_no' ], true ) ) { continue; } switch ( true ) { case 'contact_details' === $fields['groupKey'] || 'segments' === $fields['groupKey']: if ( 'creation_days' === $slug ) { $slug = " DATEDIFF(now(), c.creation_date) as `creation_days` "; break; } if ( 'company' === $slug || 'dob' === $slug || 'gender' === $slug ) { $slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`"; break; } $slug = "c." . $slug; break; case 'geography' === $fields['groupKey'] : if ( 'state' !== $slug && 'country' !== $slug ) { $slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`"; } else { $slug = "c." . $slug; } break; case 'engagement' === $fields['groupKey'] : if ( 'links' === $slug ) { $slug = 'link-trigger-click'; } $slug = "cm.f" . BWFCRM_Fields::get_field_id_by_slug( $slug ) . " AS `$slug`"; break; case 'woocommerce' === $fields['groupKey'] : if ( 'l_order_days' === $slug ) { $slug = ! in_array( $slug, $added_columns ) ? " DATEDIFF(now(), wc.l_order_date) as `l_order_days` " : ''; break; } if ( 'f_order_days' === $slug ) { $slug = ! in_array( $slug, $added_columns ) ? " DATEDIFF(now(), wc.f_order_date) as `f_order_days` " : ''; break; } if ( isset( $modified_columns[ $slug ] ) ) { $slug = $modified_columns[ $slug ]; } $slug = ! in_array( $slug, $added_columns ) ? "wc." . $slug : ''; break; case 'contact_custom_fields' === $fields['groupKey'] : $custom_field_slug = str_replace( 'bwf_cf', '', $slug ); /** if column is not exists in bwf_contact_fields table */ if ( empty( BWF_Model_Contact_Fields::column_already_exists( $custom_field_slug ) ) ) { $slug = ''; break; } $slug = "cm.f" . $custom_field_slug . " AS `$slug`"; break; } if ( empty( $slug ) ) { continue; } $added_columns[] = $slug; $columns[ $fields['groupKey'] ][] = apply_filters( 'bwfcrm_contact_columns_group', $slug, $fields ); } } $columns['need_to_add'] = $need_to_add_columns; return $columns; } /** * Check contact email in unsubscribe table * * @param $email * @param $contact_no * * @return array|false|object|string|null */ public static function is_contact_unsubscribed( $email, $contact_no ) { if ( empty( $email ) && empty( $contact_no ) ) { return false; } $data = [ 'recipient' => [ $email, $contact_no ], ]; return BWFAN_Model_Message_Unsubscribe::get_message_unsubscribe_row( $data ); } } }