_data ) { $defaults = array( 'products' => array(), 'coupons' => array(), 'customer' => array(), 'attribution' => array(), ); $order_id = $order_data['order_id']; $orders_data[ $key ]['extended_info'] = isset( $mapped_data[ $order_id ] ) ? array_merge( $defaults, $mapped_data[ $order_id ] ) : $defaults; if ( $order_data['customer_id'] && isset( $mapped_customers[ $order_data['customer_id'] ] ) ) { $orders_data[ $key ]['extended_info']['customer'] = $mapped_customers[ $order_data['customer_id'] ]; } $name_order_id = $order_id; if ( isset( $customer_details[ $order_id ] ) && 'shop_order_refund' === $customer_details[ $order_id ]['order_type'] && $order_data['parent_id'] ) { $name_order_id = $order_data['parent_id']; } if ( isset( $customer_details[ $name_order_id ] ) && 0 === (int) $customer_details[ $name_order_id ]['customer_id'] ) { $orders_data[ $key ]['extended_info']['customer']['first_name'] = (string) $customer_details[ $name_order_id ]['first_name']; $orders_data[ $key ]['extended_info']['customer']['last_name'] = (string) $customer_details[ $name_order_id ]['last_name']; } $source_type = $order_attributions[ $order_id ]['_wc_order_attribution_source_type'] ?? ''; $utm_source = $order_attributions[ $order_id ]['_wc_order_attribution_utm_source'] ?? ''; $orders_data[ $key ]['extended_info']['attribution']['origin'] = $this->get_origin_label( $source_type, $utm_source ); } } /** * Returns oreders that have a parent id * * @param array $orders Orders array. * @return array */ protected function get_orders_with_parent_id( $orders ) { $related_orders = array(); foreach ( $orders as $order ) { if ( '0' !== $order['parent_id'] ) { $related_orders[ $order['parent_id'] ] = $order; } } return $related_orders; } /** * Returns the same array index by a given key * * @param array $array Array to be looped over. * @param string $key Key of values used for new array. * @return array */ protected function map_array_by_key( $array, $key ) { $mapped = array(); foreach ( $array as $item ) { $mapped[ $item[ $key ] ] = $item; } return $mapped; } /** * Get product IDs, names, and quantity from order IDs. * * @param array $order_ids Array of order IDs. * @return array */ protected function get_products_by_order_ids( $order_ids ) { global $wpdb; $order_product_lookup_table = $wpdb->prefix . 'wc_order_product_lookup'; $included_order_ids = implode( ',', $order_ids ); /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ $products = $wpdb->get_results( "SELECT order_id, product_id, variation_id, post_title as product_name, product_qty as product_quantity FROM {$wpdb->posts} JOIN {$order_product_lookup_table} ON {$wpdb->posts}.ID = ( CASE WHEN variation_id > 0 THEN variation_id ELSE product_id END ) WHERE order_id IN ({$included_order_ids}) AND product_qty > 0 ", ARRAY_A ); /* phpcs:enable */ return $products; } /** * Get customer data from Order data. * * @param array $orders Array of orders data. * @return array */ protected function get_customers_by_orders( $orders ) { global $wpdb; $customer_lookup_table = $wpdb->prefix . 'wc_customer_lookup'; $customer_ids = array(); foreach ( $orders as $order ) { if ( $order['customer_id'] ) { $customer_ids[] = intval( $order['customer_id'] ); } } if ( empty( $customer_ids ) ) { return array(); } /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ $customer_ids = implode( ',', $customer_ids ); $customers = $wpdb->get_results( "SELECT * FROM {$customer_lookup_table} WHERE customer_id IN ({$customer_ids})", ARRAY_A ); /* phpcs:enable */ return $customers; } /** * Get the customer ID and customer name stored on each order. * * The name falls back from billing to shipping per field, matching how * Automattic\WooCommerce\Admin\Overrides\Order builds the customer lookup row. Without that * fallback an order that only has a shipping name would lose the name it used to display. * * @param int[] $order_ids Order IDs. * @return array */ private function get_order_customer_details_by_order_ids( $order_ids ) { global $wpdb; $order_ids = array_values( array_unique( array_filter( array_map( 'absint', $order_ids ) ) ) ); if ( empty( $order_ids ) ) { return array(); } $included_order_ids = implode( ',', $order_ids ); if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { $orders_table = OrdersTableDataStore::get_orders_table_name(); $addresses_table = OrdersTableDataStore::get_addresses_table_name(); /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ return $wpdb->get_results( "SELECT orders.id AS order_id, orders.type AS order_type, orders.customer_id, COALESCE( NULLIF( billing.first_name, '' ), shipping.first_name, '' ) AS first_name, COALESCE( NULLIF( billing.last_name, '' ), shipping.last_name, '' ) AS last_name FROM {$orders_table} orders LEFT JOIN {$addresses_table} billing ON orders.id = billing.order_id AND billing.address_type = 'billing' LEFT JOIN {$addresses_table} shipping ON orders.id = shipping.order_id AND shipping.address_type = 'shipping' WHERE orders.id IN ({$included_order_ids})", ARRAY_A ); /* phpcs:enable */ } /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ return $wpdb->get_results( "SELECT orders.ID AS order_id, orders.post_type AS order_type, MAX( CASE WHEN order_meta.meta_key = '_customer_user' THEN order_meta.meta_value END ) AS customer_id, COALESCE( NULLIF( MAX( CASE WHEN order_meta.meta_key = '_billing_first_name' THEN order_meta.meta_value END ), '' ), MAX( CASE WHEN order_meta.meta_key = '_shipping_first_name' THEN order_meta.meta_value END ), '' ) AS first_name, COALESCE( NULLIF( MAX( CASE WHEN order_meta.meta_key = '_billing_last_name' THEN order_meta.meta_value END ), '' ), MAX( CASE WHEN order_meta.meta_key = '_shipping_last_name' THEN order_meta.meta_value END ), '' ) AS last_name FROM {$wpdb->posts} orders LEFT JOIN {$wpdb->postmeta} order_meta ON orders.ID = order_meta.post_id AND order_meta.meta_key IN ( '_customer_user', '_billing_first_name', '_billing_last_name', '_shipping_first_name', '_shipping_last_name' ) WHERE orders.ID IN ({$included_order_ids}) GROUP BY orders.ID, orders.post_type", ARRAY_A ); /* phpcs:enable */ } /** * Get coupon information from order IDs. * * @param array $order_ids Array of order IDs. * @return array */ protected function get_coupons_by_order_ids( $order_ids ) { global $wpdb; $order_coupon_lookup_table = $wpdb->prefix . 'wc_order_coupon_lookup'; $included_order_ids = implode( ',', $order_ids ); /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ $coupons = $wpdb->get_results( "SELECT order_id, coupon_id, post_title as coupon_code FROM {$wpdb->posts} JOIN {$order_coupon_lookup_table} ON {$order_coupon_lookup_table}.coupon_id = {$wpdb->posts}.ID WHERE order_id IN ({$included_order_ids}) ", ARRAY_A ); /* phpcs:enable */ return $coupons; } /** * Get order attributions data from order IDs. * * @param array $order_ids Array of order IDs. * @return array */ protected function get_order_attributions_by_order_ids( $order_ids ) { global $wpdb; $order_meta_table = OrdersTableDataStore::get_meta_table_name(); $included_order_ids = implode( ',', array_map( 'absint', $order_ids ) ); if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ $order_attributions_meta = $wpdb->get_results( "SELECT order_id, meta_key, meta_value FROM $order_meta_table WHERE order_id IN ({$included_order_ids}) AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' ) ", ARRAY_A ); /* phpcs:enable */ } else { /* phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared */ $order_attributions_meta = $wpdb->get_results( "SELECT post_id as order_id, meta_key, meta_value FROM $wpdb->postmeta WHERE post_id IN ({$included_order_ids}) AND meta_key IN ( '_wc_order_attribution_source_type', '_wc_order_attribution_utm_source' ) ", ARRAY_A ); /* phpcs:enable */ } $order_attributions = array(); foreach ( $order_attributions_meta as $meta ) { if ( ! isset( $order_attributions[ $meta['order_id'] ] ) ) { $order_attributions[ $meta['order_id'] ] = array(); } $order_attributions[ $meta['order_id'] ][ $meta['meta_key'] ] = $meta['meta_value']; } return $order_attributions; } /** * Get all statuses that have been synced. * * @return string[] Unique order statuses. */ public static function get_all_statuses() { global $wpdb; $statuses = wp_cache_get( self::ORDERS_STATUSES_ALL_CACHE_KEY, 'woocommerce_analytics' ); if ( false === $statuses ) { $table_name = self::get_db_table_name(); $statuses = $wpdb->get_col( $wpdb->prepare( 'SELECT DISTINCT status FROM %i', $table_name ) ); wp_cache_set( self::ORDERS_STATUSES_ALL_CACHE_KEY, $statuses, 'woocommerce_analytics', YEAR_IN_SECONDS ); } return $statuses; } /** * Ensure the order status will present in `get_all_statuses` call result. * * @internal * @param int $order_id Order ID. * @return void */ public static function maybe_update_order_statuses_cache( $order_id ) { $order = wc_get_order( $order_id ); if ( $order ) { $status = self::normalize_order_status( $order->get_status() ); $statuses = self::get_all_statuses(); if ( ! in_array( $status, $statuses, true ) ) { $statuses[] = $status; wp_cache_set( self::ORDERS_STATUSES_ALL_CACHE_KEY, $statuses, 'woocommerce_analytics', YEAR_IN_SECONDS ); } } } /** * Ensure the order status will present in `get_all_statuses` call result. * * @deprecated 10.3.0 Use maybe_update_order_statuses_cache(). * @param int $order_id Order ID. * @return void */ public static function maybe_update_order_statuses_transient( $order_id ) { wc_deprecated_function( __METHOD__, '10.3.0', __CLASS__ . '::maybe_update_order_statuses_cache()' ); self::maybe_update_order_statuses_cache( $order_id ); } /** * Initialize query objects. */ protected function initialize_queries() { $this->clear_all_clauses(); $this->subquery = new SqlQuery( $this->context . '_subquery' ); $this->subquery->add_sql_clause( 'select', self::get_db_table_name() . '.order_id' ); $this->subquery->add_sql_clause( 'from', self::get_db_table_name() ); } }