class-db-manager.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. <?php
  2. /**
  3. * Database Manager Class - Handles all database operations (HPOS Compatible)
  4. *
  5. * @package StudiouWCCustomReports
  6. */
  7. if (!defined('ABSPATH')) {
  8. exit;
  9. }
  10. class StudiouWC_DB_Manager {
  11. public function get_product_categories_summary($date_from, $date_to, $statuses, $per_page, $paged, $orderby, $order) {
  12. global $wpdb;
  13. if (empty($statuses)) {
  14. return array();
  15. }
  16. $settings_manager = new StudiouWC_Settings_Manager();
  17. $settings = $settings_manager->get_settings();
  18. $default_property = $settings['default_property'] ?? '';
  19. $statuses_placeholder = implode(',', array_fill(0, count($statuses), '%s'));
  20. $offset = ($paged - 1) * $per_page;
  21. // Check if HPOS is enabled
  22. if ($this->is_hpos_enabled()) {
  23. $sql = $this->get_hpos_categories_query($statuses_placeholder);
  24. } else {
  25. $sql = $this->get_legacy_categories_query($statuses_placeholder);
  26. }
  27. // Add ordering
  28. $sql .= $this->get_order_clause($orderby, $order);
  29. $sql .= " LIMIT %d OFFSET %d";
  30. $query_params = array_merge(
  31. array($date_from, $date_to),
  32. $statuses,
  33. array($per_page, $offset)
  34. );
  35. $results = $wpdb->get_results($wpdb->prepare($sql, $query_params), ARRAY_A);
  36. // Get property counts for each category if property is set
  37. foreach ($results as &$result) {
  38. $result['property_counts'] = array();
  39. if (!empty($default_property)) {
  40. $result['property_counts'] = $this->get_property_counts_for_category(
  41. $result['category'],
  42. $date_from,
  43. $date_to,
  44. $statuses,
  45. $default_property
  46. );
  47. }
  48. }
  49. return $results;
  50. }
  51. public function get_property_counts_for_category($category, $date_from, $date_to, $statuses, $property) {
  52. global $wpdb;
  53. if (empty($statuses) || empty($property)) {
  54. return array();
  55. }
  56. $statuses_placeholder = implode(',', array_fill(0, count($statuses), '%s'));
  57. $meta_key = 'attribute_pa_' . $property; // WooCommerce stores variation attributes as meta
  58. // Get order items for this category and date range, then check their variation meta
  59. if ($this->is_hpos_enabled()) {
  60. $base_sql = "
  61. SELECT oi.product_id, oi.variation_id, oi.product_qty, pm.meta_value as attr_value
  62. FROM {$wpdb->prefix}wc_orders o
  63. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON o.id = oi.order_id
  64. INNER JOIN {$wpdb->posts} p_main ON (
  65. CASE
  66. WHEN oi.variation_id > 0 THEN
  67. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  68. ELSE oi.product_id
  69. END
  70. ) = p_main.ID
  71. INNER JOIN {$wpdb->term_relationships} tr_cat ON p_main.ID = tr_cat.object_id
  72. INNER JOIN {$wpdb->term_taxonomy} tt_cat ON tr_cat.term_taxonomy_id = tt_cat.term_taxonomy_id
  73. INNER JOIN {$wpdb->terms} t_cat ON tt_cat.term_id = t_cat.term_id
  74. LEFT JOIN {$wpdb->postmeta} pm ON (
  75. CASE
  76. WHEN oi.variation_id > 0 THEN oi.variation_id
  77. ELSE oi.product_id
  78. END
  79. ) = pm.post_id AND pm.meta_key = %s
  80. WHERE o.date_created_gmt >= %s
  81. AND o.date_created_gmt <= %s
  82. AND o.status IN ($statuses_placeholder)
  83. AND tt_cat.taxonomy = 'product_cat'
  84. AND t_cat.name = %s
  85. AND pm.meta_value IS NOT NULL
  86. AND pm.meta_value != ''
  87. ";
  88. } else {
  89. $base_sql = "
  90. SELECT oi.product_id, oi.variation_id, oi.product_qty, pm.meta_value as attr_value
  91. FROM {$wpdb->posts} p_order
  92. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON p_order.ID = oi.order_id
  93. INNER JOIN {$wpdb->posts} p_main ON (
  94. CASE
  95. WHEN oi.variation_id > 0 THEN
  96. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  97. ELSE oi.product_id
  98. END
  99. ) = p_main.ID
  100. INNER JOIN {$wpdb->term_relationships} tr_cat ON p_main.ID = tr_cat.object_id
  101. INNER JOIN {$wpdb->term_taxonomy} tt_cat ON tr_cat.term_taxonomy_id = tt_cat.term_taxonomy_id
  102. INNER JOIN {$wpdb->terms} t_cat ON tt_cat.term_id = t_cat.term_id
  103. LEFT JOIN {$wpdb->postmeta} pm ON (
  104. CASE
  105. WHEN oi.variation_id > 0 THEN oi.variation_id
  106. ELSE oi.product_id
  107. END
  108. ) = pm.post_id AND pm.meta_key = %s
  109. WHERE p_order.post_date >= %s
  110. AND p_order.post_date <= %s
  111. AND p_order.post_status IN ($statuses_placeholder)
  112. AND p_order.post_type = 'shop_order'
  113. AND tt_cat.taxonomy = 'product_cat'
  114. AND t_cat.name = %s
  115. AND pm.meta_value IS NOT NULL
  116. AND pm.meta_value != ''
  117. ";
  118. }
  119. $query_params = array_merge(
  120. array($meta_key, $date_from, $date_to),
  121. $statuses,
  122. array($category)
  123. );
  124. // Debug: Log the query for troubleshooting
  125. if (defined('WP_DEBUG') && WP_DEBUG) {
  126. error_log('[Studiou Debug] Updated property counts query: ' . $wpdb->prepare($base_sql, $query_params));
  127. }
  128. $results = $wpdb->get_results($wpdb->prepare($base_sql, $query_params), ARRAY_A);
  129. if (defined('WP_DEBUG') && WP_DEBUG) {
  130. error_log('[Studiou Debug] Found ' . count($results) . ' items with attribute values');
  131. error_log('[Studiou Debug] Sample results: ' . print_r(array_slice($results, 0, 3), true));
  132. }
  133. // Count quantities by attribute value
  134. $property_counts = array();
  135. foreach ($results as $result) {
  136. $attr_value = $result['attr_value'];
  137. $qty = $result['product_qty'];
  138. if (!empty($attr_value)) {
  139. if (!isset($property_counts[$attr_value])) {
  140. $property_counts[$attr_value] = 0;
  141. }
  142. $property_counts[$attr_value] += $qty;
  143. }
  144. }
  145. if (defined('WP_DEBUG') && WP_DEBUG) {
  146. error_log('[Studiou Debug] Final property counts: ' . print_r($property_counts, true));
  147. }
  148. return $property_counts;
  149. }
  150. public function get_total_items($date_from, $date_to, $statuses) {
  151. global $wpdb;
  152. if (empty($statuses)) {
  153. return 0;
  154. }
  155. $statuses_placeholder = implode(',', array_fill(0, count($statuses), '%s'));
  156. if ($this->is_hpos_enabled()) {
  157. $sql = $this->get_hpos_total_items_query($statuses_placeholder);
  158. } else {
  159. $sql = $this->get_legacy_total_items_query($statuses_placeholder);
  160. }
  161. $query_params = array_merge(
  162. array($date_from, $date_to),
  163. $statuses
  164. );
  165. return $wpdb->get_var($wpdb->prepare($sql, $query_params));
  166. }
  167. /**
  168. * Get product attributes (not meta keys) for dropdown
  169. */
  170. public function get_product_properties() {
  171. global $wpdb;
  172. // Get WooCommerce product attributes
  173. $attributes = $wpdb->get_results("
  174. SELECT attribute_name, attribute_label
  175. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  176. ORDER BY attribute_label
  177. ", ARRAY_A);
  178. $properties = array();
  179. foreach ($attributes as $attribute) {
  180. $properties[$attribute['attribute_name']] = $attribute['attribute_label'];
  181. }
  182. return $properties;
  183. }
  184. /**
  185. * Get property values for a specific attribute
  186. */
  187. public function get_property_values($property) {
  188. global $wpdb;
  189. if (empty($property)) {
  190. return array();
  191. }
  192. // Build taxonomy name for WooCommerce attributes
  193. $taxonomy = 'pa_' . $property;
  194. // Get terms for this attribute taxonomy
  195. $sql = "
  196. SELECT t.name, t.slug
  197. FROM {$wpdb->terms} t
  198. INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
  199. WHERE tt.taxonomy = %s
  200. ORDER BY t.name
  201. ";
  202. $results = $wpdb->get_results($wpdb->prepare($sql, $taxonomy), ARRAY_A);
  203. $values = array();
  204. foreach ($results as $result) {
  205. $values[$result['slug']] = $result['name'];
  206. }
  207. return $values;
  208. }
  209. /**
  210. * Get attribute name from attribute slug
  211. */
  212. public function get_attribute_name($attribute_slug) {
  213. global $wpdb;
  214. $result = $wpdb->get_var($wpdb->prepare("
  215. SELECT attribute_label
  216. FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
  217. WHERE attribute_name = %s
  218. ", $attribute_slug));
  219. return $result ? $result : $attribute_slug;
  220. }
  221. /**
  222. * Check if HPOS (High-Performance Order Storage) is enabled
  223. */
  224. private function is_hpos_enabled() {
  225. return class_exists('Automattic\WooCommerce\Utilities\OrderUtil') &&
  226. \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled();
  227. }
  228. /**
  229. * Get HPOS compatible query for categories summary
  230. */
  231. private function get_hpos_categories_query($statuses_placeholder) {
  232. global $wpdb;
  233. return "
  234. SELECT
  235. t.name as category,
  236. COUNT(DISTINCT o.id) as orders_count,
  237. SUM(oi.product_net_revenue) as total_price
  238. FROM {$wpdb->prefix}wc_orders o
  239. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON o.id = oi.order_id
  240. INNER JOIN {$wpdb->posts} p ON oi.product_id = p.ID OR oi.variation_id = p.ID
  241. INNER JOIN {$wpdb->term_relationships} tr ON (
  242. CASE
  243. WHEN oi.variation_id > 0 THEN
  244. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  245. ELSE oi.product_id
  246. END
  247. ) = tr.object_id
  248. INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
  249. INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
  250. WHERE o.date_created_gmt >= %s
  251. AND o.date_created_gmt <= %s
  252. AND o.status IN ($statuses_placeholder)
  253. AND tt.taxonomy = 'product_cat'
  254. GROUP BY t.term_id, t.name
  255. ";
  256. }
  257. /**
  258. * Get legacy query for categories summary (pre-HPOS)
  259. */
  260. private function get_legacy_categories_query($statuses_placeholder) {
  261. global $wpdb;
  262. return "
  263. SELECT
  264. t.name as category,
  265. COUNT(DISTINCT p_order.ID) as orders_count,
  266. SUM(oi.product_net_revenue) as total_price
  267. FROM {$wpdb->posts} p_order
  268. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON p_order.ID = oi.order_id
  269. INNER JOIN {$wpdb->posts} p ON oi.product_id = p.ID OR oi.variation_id = p.ID
  270. INNER JOIN {$wpdb->term_relationships} tr ON (
  271. CASE
  272. WHEN oi.variation_id > 0 THEN
  273. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  274. ELSE oi.product_id
  275. END
  276. ) = tr.object_id
  277. INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
  278. INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
  279. WHERE p_order.post_date >= %s
  280. AND p_order.post_date <= %s
  281. AND p_order.post_status IN ($statuses_placeholder)
  282. AND p_order.post_type = 'shop_order'
  283. AND tt.taxonomy = 'product_cat'
  284. GROUP BY t.term_id, t.name
  285. ";
  286. }
  287. /**
  288. * Get HPOS compatible query for property counts
  289. */
  290. private function get_hpos_property_counts_query($statuses_placeholder) {
  291. global $wpdb;
  292. return "
  293. SELECT
  294. t_attr.slug as property_value,
  295. SUM(oi.product_qty) as count
  296. FROM {$wpdb->prefix}wc_orders o
  297. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON o.id = oi.order_id
  298. INNER JOIN {$wpdb->posts} p_main ON (
  299. CASE
  300. WHEN oi.variation_id > 0 THEN
  301. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  302. ELSE oi.product_id
  303. END
  304. ) = p_main.ID
  305. INNER JOIN {$wpdb->term_relationships} tr_cat ON p_main.ID = tr_cat.object_id
  306. INNER JOIN {$wpdb->term_taxonomy} tt_cat ON tr_cat.term_taxonomy_id = tt_cat.term_taxonomy_id
  307. INNER JOIN {$wpdb->terms} t_cat ON tt_cat.term_id = t_cat.term_id
  308. INNER JOIN {$wpdb->term_relationships} tr_attr ON (
  309. CASE
  310. WHEN oi.variation_id > 0 THEN oi.variation_id
  311. ELSE oi.product_id
  312. END
  313. ) = tr_attr.object_id
  314. INNER JOIN {$wpdb->term_taxonomy} tt_attr ON tr_attr.term_taxonomy_id = tt_attr.term_taxonomy_id
  315. INNER JOIN {$wpdb->terms} t_attr ON tt_attr.term_id = t_attr.term_id
  316. WHERE o.date_created_gmt >= %s
  317. AND o.date_created_gmt <= %s
  318. AND o.status IN ($statuses_placeholder)
  319. AND tt_cat.taxonomy = 'product_cat'
  320. AND t_cat.name = %s
  321. AND tt_attr.taxonomy = %s
  322. GROUP BY t_attr.term_id, t_attr.slug
  323. ";
  324. }
  325. /**
  326. * Get legacy query for property counts (pre-HPOS)
  327. */
  328. private function get_legacy_property_counts_query($statuses_placeholder) {
  329. global $wpdb;
  330. return "
  331. SELECT
  332. t_attr.slug as property_value,
  333. SUM(oi.product_qty) as count
  334. FROM {$wpdb->posts} p_order
  335. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON p_order.ID = oi.order_id
  336. INNER JOIN {$wpdb->posts} p_main ON (
  337. CASE
  338. WHEN oi.variation_id > 0 THEN
  339. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  340. ELSE oi.product_id
  341. END
  342. ) = p_main.ID
  343. INNER JOIN {$wpdb->term_relationships} tr_cat ON p_main.ID = tr_cat.object_id
  344. INNER JOIN {$wpdb->term_taxonomy} tt_cat ON tr_cat.term_taxonomy_id = tt_cat.term_taxonomy_id
  345. INNER JOIN {$wpdb->terms} t_cat ON tt_cat.term_id = t_cat.term_id
  346. INNER JOIN {$wpdb->term_relationships} tr_attr ON (
  347. CASE
  348. WHEN oi.variation_id > 0 THEN oi.variation_id
  349. ELSE oi.product_id
  350. END
  351. ) = tr_attr.object_id
  352. INNER JOIN {$wpdb->term_taxonomy} tt_attr ON tr_attr.term_taxonomy_id = tt_attr.term_taxonomy_id
  353. INNER JOIN {$wpdb->terms} t_attr ON tt_attr.term_id = t_attr.term_id
  354. WHERE p_order.post_date >= %s
  355. AND p_order.post_date <= %s
  356. AND p_order.post_status IN ($statuses_placeholder)
  357. AND p_order.post_type = 'shop_order'
  358. AND tt_cat.taxonomy = 'product_cat'
  359. AND t_cat.name = %s
  360. AND tt_attr.taxonomy = %s
  361. GROUP BY t_attr.term_id, t_attr.slug
  362. ";
  363. }
  364. /**
  365. * Get HPOS compatible query for total items
  366. */
  367. private function get_hpos_total_items_query($statuses_placeholder) {
  368. global $wpdb;
  369. return "
  370. SELECT COUNT(DISTINCT t.term_id)
  371. FROM {$wpdb->prefix}wc_orders o
  372. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON o.id = oi.order_id
  373. INNER JOIN {$wpdb->posts} p ON oi.product_id = p.ID OR oi.variation_id = p.ID
  374. INNER JOIN {$wpdb->term_relationships} tr ON (
  375. CASE
  376. WHEN oi.variation_id > 0 THEN
  377. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  378. ELSE oi.product_id
  379. END
  380. ) = tr.object_id
  381. INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
  382. INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
  383. WHERE o.date_created_gmt >= %s
  384. AND o.date_created_gmt <= %s
  385. AND o.status IN ($statuses_placeholder)
  386. AND tt.taxonomy = 'product_cat'
  387. ";
  388. }
  389. /**
  390. * Get legacy query for total items (pre-HPOS)
  391. */
  392. private function get_legacy_total_items_query($statuses_placeholder) {
  393. global $wpdb;
  394. return "
  395. SELECT COUNT(DISTINCT t.term_id)
  396. FROM {$wpdb->posts} p_order
  397. INNER JOIN {$wpdb->prefix}wc_order_product_lookup oi ON p_order.ID = oi.order_id
  398. INNER JOIN {$wpdb->posts} p ON oi.product_id = p.ID OR oi.variation_id = p.ID
  399. INNER JOIN {$wpdb->term_relationships} tr ON (
  400. CASE
  401. WHEN oi.variation_id > 0 THEN
  402. (SELECT post_parent FROM {$wpdb->posts} WHERE ID = oi.variation_id)
  403. ELSE oi.product_id
  404. END
  405. ) = tr.object_id
  406. INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
  407. INNER JOIN {$wpdb->terms} t ON tt.term_id = t.term_id
  408. WHERE p_order.post_date >= %s
  409. AND p_order.post_date <= %s
  410. AND p_order.post_status IN ($statuses_placeholder)
  411. AND p_order.post_type = 'shop_order'
  412. AND tt.taxonomy = 'product_cat'
  413. ";
  414. }
  415. public function get_order_customers_list($date_from, $date_to, $statuses, $per_page, $paged, $orderby, $order) {
  416. global $wpdb;
  417. if (empty($statuses)) {
  418. return array();
  419. }
  420. $statuses_placeholder = implode(',', array_fill(0, count($statuses), '%s'));
  421. $offset = ($paged - 1) * $per_page;
  422. if ($this->is_hpos_enabled()) {
  423. $sql = $this->get_hpos_customers_query($statuses_placeholder);
  424. } else {
  425. $sql = $this->get_legacy_customers_query($statuses_placeholder);
  426. }
  427. $sql .= $this->get_customers_order_clause($orderby, $order);
  428. $sql .= " LIMIT %d OFFSET %d";
  429. $query_params = array_merge(
  430. array($date_from, $date_to),
  431. $statuses,
  432. array($per_page, $offset)
  433. );
  434. return $wpdb->get_results($wpdb->prepare($sql, $query_params), ARRAY_A);
  435. }
  436. public function get_order_customers_total_items($date_from, $date_to, $statuses) {
  437. global $wpdb;
  438. if (empty($statuses)) {
  439. return 0;
  440. }
  441. $statuses_placeholder = implode(',', array_fill(0, count($statuses), '%s'));
  442. if ($this->is_hpos_enabled()) {
  443. $sql = "
  444. SELECT COUNT(DISTINCT addr.email)
  445. FROM {$wpdb->prefix}wc_orders o
  446. INNER JOIN {$wpdb->prefix}wc_order_addresses addr ON o.id = addr.order_id AND addr.address_type = 'billing'
  447. WHERE o.date_created_gmt >= %s
  448. AND o.date_created_gmt <= %s
  449. AND o.status IN ($statuses_placeholder)
  450. AND addr.email != ''
  451. ";
  452. } else {
  453. $sql = "
  454. SELECT COUNT(DISTINCT pm_email.meta_value)
  455. FROM {$wpdb->posts} p
  456. INNER JOIN {$wpdb->postmeta} pm_email ON p.ID = pm_email.post_id AND pm_email.meta_key = '_billing_email'
  457. WHERE p.post_date >= %s
  458. AND p.post_date <= %s
  459. AND p.post_status IN ($statuses_placeholder)
  460. AND p.post_type = 'shop_order'
  461. AND pm_email.meta_value != ''
  462. ";
  463. }
  464. $query_params = array_merge(
  465. array($date_from, $date_to),
  466. $statuses
  467. );
  468. return $wpdb->get_var($wpdb->prepare($sql, $query_params));
  469. }
  470. private function get_hpos_customers_query($statuses_placeholder) {
  471. global $wpdb;
  472. return "
  473. SELECT
  474. MAX(CONCAT(addr.first_name, ' ', addr.last_name)) as customer_name,
  475. addr.email as customer_email,
  476. MAX(addr.phone) as customer_phone,
  477. COUNT(DISTINCT o.id) as orders_count,
  478. SUM(o.total_amount) as total_price
  479. FROM {$wpdb->prefix}wc_orders o
  480. INNER JOIN {$wpdb->prefix}wc_order_addresses addr ON o.id = addr.order_id AND addr.address_type = 'billing'
  481. WHERE o.date_created_gmt >= %s
  482. AND o.date_created_gmt <= %s
  483. AND o.status IN ($statuses_placeholder)
  484. AND addr.email != ''
  485. GROUP BY addr.email
  486. ";
  487. }
  488. private function get_legacy_customers_query($statuses_placeholder) {
  489. global $wpdb;
  490. return "
  491. SELECT
  492. MAX(CONCAT(pm_fn.meta_value, ' ', pm_ln.meta_value)) as customer_name,
  493. pm_email.meta_value as customer_email,
  494. MAX(pm_phone.meta_value) as customer_phone,
  495. COUNT(DISTINCT p.ID) as orders_count,
  496. SUM(CAST(pm_total.meta_value AS DECIMAL(10,2))) as total_price
  497. FROM {$wpdb->posts} p
  498. INNER JOIN {$wpdb->postmeta} pm_email ON p.ID = pm_email.post_id AND pm_email.meta_key = '_billing_email'
  499. LEFT JOIN {$wpdb->postmeta} pm_fn ON p.ID = pm_fn.post_id AND pm_fn.meta_key = '_billing_first_name'
  500. LEFT JOIN {$wpdb->postmeta} pm_ln ON p.ID = pm_ln.post_id AND pm_ln.meta_key = '_billing_last_name'
  501. LEFT JOIN {$wpdb->postmeta} pm_phone ON p.ID = pm_phone.post_id AND pm_phone.meta_key = '_billing_phone'
  502. LEFT JOIN {$wpdb->postmeta} pm_total ON p.ID = pm_total.post_id AND pm_total.meta_key = '_order_total'
  503. WHERE p.post_date >= %s
  504. AND p.post_date <= %s
  505. AND p.post_status IN ($statuses_placeholder)
  506. AND p.post_type = 'shop_order'
  507. AND pm_email.meta_value != ''
  508. GROUP BY pm_email.meta_value
  509. ";
  510. }
  511. private function get_customers_order_clause($orderby, $order) {
  512. $order = ($order === 'DESC') ? 'DESC' : 'ASC';
  513. switch ($orderby) {
  514. case 'customer_name':
  515. return " ORDER BY customer_name $order";
  516. case 'customer_email':
  517. return " ORDER BY customer_email $order";
  518. case 'orders_count':
  519. return " ORDER BY orders_count $order";
  520. case 'total_price':
  521. return " ORDER BY total_price $order";
  522. default:
  523. return " ORDER BY customer_name $order";
  524. }
  525. }
  526. private function get_order_clause($orderby, $order) {
  527. $order = ($order === 'DESC') ? 'DESC' : 'ASC';
  528. switch ($orderby) {
  529. case 'orders_count':
  530. return " ORDER BY orders_count $order";
  531. case 'total_price':
  532. return " ORDER BY total_price $order";
  533. case 'category':
  534. default:
  535. return " ORDER BY t.name $order";
  536. }
  537. }
  538. }