class-custom-columns-manager.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. <?php
  2. // Manages custom columns in the WooCommerce orders list (HPOS and legacy).
  3. defined('ABSPATH') || exit;
  4. class Custom_Columns_Manager {
  5. /**
  6. * Per-request cache of resolved WC_Order objects keyed by order ID.
  7. * Used only on the legacy CPT path where WP passes us the order ID;
  8. * under HPOS WC already hands us the WC_Order instance directly.
  9. * @var array<int,WC_Order|false>
  10. */
  11. private $order_cache = array();
  12. public function __construct() {
  13. add_filter('manage_woocommerce_page_wc-orders_columns', array($this, 'add_custom_shop_order_column'));
  14. add_action('manage_woocommerce_page_wc-orders_custom_column', array($this, 'add_custom_shop_order_column_content'), 10, 2);
  15. }
  16. public function add_custom_shop_order_column($columns) {
  17. $new_columns = array();
  18. foreach ($columns as $column_name => $column_info) {
  19. $new_columns[$column_name] = $column_info;
  20. if ($column_name === 'order_total') {
  21. $new_columns['to_print_date'] = __('To Print Date', 'studiou-wc-ord-print-statuses');
  22. $new_columns['in_print_date'] = __('In Print Date', 'studiou-wc-ord-print-statuses');
  23. $new_columns['external_order_number'] = __('External Order Number', 'studiou-wc-ord-print-statuses');
  24. }
  25. }
  26. return $new_columns;
  27. }
  28. /**
  29. * Render cell content. Under HPOS WC passes the WC_Order object as the
  30. * second argument; under legacy CPT it passes the post ID. Use the
  31. * object directly when WC hands us one (skipping the wc_get_order
  32. * lookup entirely), and memoise by ID for the legacy path so we don't
  33. * re-resolve the same order for each of the three custom columns.
  34. */
  35. public function add_custom_shop_order_column_content($column, $order_or_id) {
  36. if ($order_or_id instanceof WC_Abstract_Order) {
  37. $order = $order_or_id;
  38. } else {
  39. $id = (int) $order_or_id;
  40. if (!array_key_exists($id, $this->order_cache)) {
  41. $this->order_cache[$id] = wc_get_order($id);
  42. }
  43. $order = $this->order_cache[$id];
  44. }
  45. if (!$order) {
  46. echo '—';
  47. return;
  48. }
  49. switch ($column) {
  50. case 'to_print_date':
  51. echo $this->format_meta_date($order->get_meta('to_print_date'));
  52. break;
  53. case 'in_print_date':
  54. echo $this->format_meta_date($order->get_meta('in_print_date'));
  55. break;
  56. case 'external_order_number':
  57. $value = $order->get_meta('external_ref_ord_no');
  58. echo $value !== '' ? esc_html($value) : '—';
  59. break;
  60. }
  61. }
  62. /**
  63. * Format a MySQL datetime meta value for display.
  64. *
  65. * The stored value (written by current_time('mysql')) is already in WP
  66. * local time. Previously this method used strtotime()/date_i18n() which
  67. * applied the timezone offset twice on hosts where the PHP server
  68. * timezone differs from the WP site timezone (e.g. UTC server +
  69. * Europe/Prague site → display drifted by 1–2 hours).
  70. *
  71. * We now parse the string as WP-local time explicitly, then format via
  72. * wp_date() which respects WP timezone. This keeps locale-aware
  73. * formatting while eliminating the offset drift.
  74. */
  75. private function format_meta_date($value) {
  76. if (empty($value)) {
  77. return '—';
  78. }
  79. $value = trim((string) $value);
  80. // Accept both 'YYYY-MM-DD HH:MM:SS' and 'YYYY-MM-DDTHH:MM[:SS]'.
  81. $normalised = str_replace('T', ' ', $value);
  82. if (!preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2})?$/', $normalised)) {
  83. return '—';
  84. }
  85. if (strlen($normalised) === 16) { // missing seconds
  86. $normalised .= ':00';
  87. }
  88. try {
  89. $dt = new DateTimeImmutable($normalised, wp_timezone());
  90. } catch (Exception $e) {
  91. return '—';
  92. }
  93. $fmt = get_option('date_format') . ' ' . get_option('time_format');
  94. return esc_html(wp_date($fmt, $dt->getTimestamp()));
  95. }
  96. }