PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.14
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.14
1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 1.3.47 1.3.46 1.3.45 All 177 releases
disco / app / Analytics / Services / ProductService.php

ProductService.php in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.4.14, at app/Analytics/Services/ProductService.php

188 lines 6.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php //phpcs:ignore
2
3 /**
4 * ProductService — composes product queries into service-level results.
5 *
6 * @package Disco
7 * @subpackage Disco\App\Analytics\Services
8 * @since 1.3.37
9 */
10
11 namespace Disco\App\Analytics\Services;
12
13 use Disco\App\Analytics\Queries\ProductQuery;
14
15 /**
16 * Provides product analytics at the service layer.
17 *
18 * Provides product analytics for the products list endpoint.
19 * No SQL lives here — all DB work is delegated to ProductQuery.
20 */
21 class ProductService extends BaseService {
22
23 /**
24 * Returns a paginated product performance table for the admin view.
25 *
26 * Only completed orders with disco_campaign meta are included.
27 * Default sort: total_revenue DESC.
28 *
29 * @param array $args date_from, date_to, campaign_id, customer_id, order_id,
30 * orderby, order, page, limit.
31 * @return array { current_period, compare_period, pagination, data }
32 */
33 public static function get_products_table( array $args ): array {
34 $limit = min( absint( $args['limit'] ?? 10 ), 100 );
35 $page = max( 1, absint( $args['page'] ?? 1 ) );
36
37 $date_to = $args['date_to'] ?? '';
38 $date_from = $args['date_from'] ?? '';
39
40 $query = new ProductQuery;
41 $result = $query->get_products_for_table( self::build_table_query_args( $args, $limit, $page ) );
42
43 $data = array();
44
45 foreach ( $result['rows'] as $row ) {
46 $data[] = self::format_product_row( $row );
47 }
48
49 return array(
50 'current_period' => array(
51 'from' => $date_from,
52 'to' => $date_to,
53 ),
54 'compare_period' => self::resolve_compare_period( $date_from, $date_to ),
55 'pagination' => self::build_pagination( $result, $page, $limit ),
56 'data' => $data,
57 );
58 }
59
60 /**
61 * Returns detail for a single product including aggregated metrics, categories, and campaigns.
62 *
63 * @param int $product_id WooCommerce product ID.
64 * @param array $args date_from, date_to.
65 * @return array { current_period, compare_period, data }
66 */
67 public static function get_product( int $product_id, array $args ): array {
68 $date_from = $args['date_from'] ?? '';
69 $date_to = $args['date_to'] ?? '';
70
71 $query = new ProductQuery;
72 $result = $query->get_products_for_table(
73 array(
74 'date_from' => $date_from,
75 'date_to' => $date_to,
76 'product_id' => $product_id,
77 'status' => 'wc-completed',
78 'per_page' => 1,
79 'page' => 1,
80 )
81 );
82
83 $data = null;
84
85 if ( ! empty( $result['rows'] ) ) {
86 $data = self::format_product_row( $result['rows'][0] );
87 }
88
89 return array(
90 'current_period' => array(
91 'from' => $date_from,
92 'to' => $date_to,
93 ),
94 'compare_period' => self::resolve_compare_period( $date_from, $date_to ),
95 'data' => $data,
96 );
97 }
98
99 /**
100 * Maps service-level $args onto the argument array ProductQuery expects.
101 *
102 * Whitelists orderby, normalizes sort direction, casts filter IDs.
103 *
104 * @param array $args Raw service args.
105 * @param int $limit Sanitized per-page limit.
106 * @param int $page Sanitized page number.
107 * @return array Query args for ProductQuery::get_products_for_table().
108 */
109 private static function build_table_query_args( array $args, int $limit, int $page ): array {
110 $orderby_map = array(
111 'total_revenue' => 'revenue',
112 'total_orders' => 'orders_count',
113 'total_customers' => 'customers_count',
114 'total_quantity' => 'total_quantity',
115 );
116 $orderby = $orderby_map[ $args['orderby'] ?? 'total_revenue' ] ?? 'revenue';
117 $order = 'desc';
118
119 if ( strtolower( $args['order'] ?? 'desc' ) === 'asc' ) {
120 $order = 'asc';
121 }
122
123 return array(
124 'date_from' => $args['date_from'] ?? '',
125 'date_to' => $args['date_to'] ?? '',
126 'search' => $args['search'] ?? '',
127 'campaign_id' => ! empty( $args['campaign_id'] ) ? (int) $args['campaign_id'] : 0,
128 'customer_id' => ! empty( $args['customer_id'] ) ? (int) $args['customer_id'] : 0,
129 'order_id' => ! empty( $args['order_id'] ) ? (int) $args['order_id'] : 0,
130 'orderby' => $orderby,
131 'order' => $order,
132 'per_page' => $limit,
133 'page' => $page,
134 'status' => 'wc-completed',
135 );
136 }
137
138 /**
139 * Formats one raw query row into the API response shape.
140 *
141 * Parses categories/campaigns raw strings, resolves the thumbnail,
142 * casts and rounds all metrics.
143 *
144 * @param object $row Raw row from ProductQuery::get_products_for_table().
145 * @return array Formatted product row.
146 * @phpstan-param object{product_id: int|string, parent_product_id: int|string|null, product_name: string|null, unit_price: string|null, categories_raw: string|null, campaigns_raw: string|null, orders_count: int|string, customers_count: int|string, total_quantity: int|string, revenue: string|null} $row
147 */
148 private static function format_product_row( object $row ): array {
149 $product_id = (int) $row->product_id;
150 $parent_product_id = (int) ( $row->parent_product_id ?? $product_id );
151
152 return array(
153 'id' => $product_id,
154 'name' => $row->product_name ?? '',
155 'image' => self::resolve_product_image( $product_id, $parent_product_id ),
156 'unit_price' => round( (float) ( $row->unit_price ?? 0 ), 2 ),
157 'categories' => self::parse_id_name_pairs( (string) ( $row->categories_raw ?? '' ) ),
158 'campaigns' => self::parse_id_name_pairs( (string) ( $row->campaigns_raw ?? '' ) ),
159 'total_orders' => (int) $row->orders_count,
160 'total_customers' => (int) $row->customers_count,
161 'total_quantity' => (int) $row->total_quantity,
162 'total_revenue' => round( (float) $row->revenue, 2 ),
163 );
164 }
165
166 /**
167 * Resolves the product thumbnail URL, falling back to the parent product.
168 *
169 * @param int $product_id Product (or variation) ID.
170 * @param int $parent_product_id Parent product ID for variations.
171 * @return string Thumbnail URL, or empty string when none exists.
172 */
173 private static function resolve_product_image( int $product_id, int $parent_product_id ): string {
174 $img = get_the_post_thumbnail_url( $product_id );
175
176 if ( ! $img ) {
177 $img = get_the_post_thumbnail_url( $parent_product_id );
178 }
179
180 if ( is_string( $img ) ) {
181 return $img;
182 }
183
184 return '';
185 }
186
187 }
188