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 / rest / Analytics / CustomersApi.php

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

285 lines 9.2 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 * Analytics REST API — Customers Endpoints
5 *
6 * @package Disco
7 * @subpackage \Rest\Analytics
8 * @since 1.1.13
9 * @category Rest
10 */
11
12 namespace Disco\Rest\Analytics;
13
14 use Disco\App\Analytics\Services\CustomerService;
15 use WP_REST_Server;
16
17 /**
18 * Class CustomersApi
19 *
20 * Registers and handles the following REST API routes:
21 *
22 * - GET /disco/v1/analytics/customers — Paginated customers who bought via campaigns.
23 * - GET /disco/v1/analytics/customers/{id} — Single customer detail.
24 *
25 * @package Disco
26 * @subpackage \Rest\Analytics
27 * @author Ohidul Islam <wahid0003@gmail.com>
28 * @link https://webappick.com
29 * @license https://opensource.org/licenses/gpl-license.php GNU Public License
30 * @category Rest
31 */
32 class CustomersApi extends Base {
33
34 /**
35 * Registers the REST API routes for customers analytics endpoints.
36 */
37 public function register_routes(): void {
38 // GET /analytics/customers
39 register_rest_route(
40 $this->namespace,
41 '/' . $this->rest_base . '/customers',
42 array(
43 array(
44 'methods' => WP_REST_Server::READABLE,
45 'callback' => array( $this, 'get_customers' ),
46 'permission_callback' => array( $this, 'permissions_check' ),
47 'args' => $this->get_customers_table_params(),
48 ),
49 'schema' => array( $this, 'get_customer_schema' ),
50 )
51 );
52
53 // GET /analytics/customers/{id}
54 register_rest_route(
55 $this->namespace,
56 '/' . $this->rest_base . '/customers/(?P<id>[\d]+)',
57 array(
58 'args' => array(
59 'id' => array(
60 'description' => __( 'WP User ID (customer_id).', 'disco' ),
61 'type' => 'integer',
62 ),
63 ),
64 array(
65 'methods' => WP_REST_Server::READABLE,
66 'callback' => array( $this, 'get_customer' ),
67 'permission_callback' => array( $this, 'permissions_check' ),
68 'args' => $this->get_date_params(),
69 ),
70 'schema' => array( $this, 'get_customer_schema' ),
71 )
72 );
73 }
74
75 // =========================================================================
76 // Callbacks
77 // =========================================================================
78
79 /**
80 * GET /disco/v1/analytics/customers
81 *
82 * Returns a paginated customer table for completed campaign-linked orders.
83 * Includes registered users and guest customers (customer_id=0 grouped by billing_email).
84 * Includes current/compare period metadata and per-customer inline campaign list.
85 *
86 * Query params:
87 * date_from (string Y-m-d) Range start. Default: 28 days ago.
88 * date_to (string Y-m-d) Range end. Default: today.
89 * search (string) Numeric = exact customer ID; text = name/email LIKE search (includes guest billing address).
90 * sort_by (string) Alias for orderby. One of: total_spent (default), orders.
91 * orderby (string) Sort field. One of: total_spent (default), orders.
92 * order (string) Sort direction: asc (default) | desc.
93 * campaign_id (int) Filter to customers who used this campaign.
94 * order_id (int) Filter to the customer from a single order.
95 * page (int ≥ 1) Page number. Default: 1.
96 * limit (int 1–100) Results per page. Default: 10.
97 *
98 * @param \WP_REST_Request $request Incoming REST request.
99 * @return \WP_REST_Response|\WP_Error
100 */
101 // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh, SlevomatCodingStandard.Functions.FunctionLength.FunctionLength
102 public function get_customers( $request ) {
103 $error = $this->validate_date_params( $request );
104
105 if ( is_wp_error( $error ) ) {
106 return $error;
107 }
108
109 $page = 1;
110 $per_page = 10;
111
112 if ( isset( $request['page'] ) ) {
113 $page = absint( $request['page'] );
114 }
115
116 if ( isset( $request['limit'] ) ) {
117 $per_page = min( absint( $request['limit'] ), 100 );
118 }
119
120 $sort_by_map = array(
121 'total_spent' => 'total_spent',
122 'orders' => 'orders',
123 );
124 $sort_by = '';
125 $default_orderby = 'total_spent';
126
127 if ( is_string( $request['sort_by'] ) ) {
128 $sort_by = sanitize_text_field( $request['sort_by'] );
129 }
130
131 if ( is_string( $request['orderby'] ) ) {
132 $default_orderby = sanitize_text_field( $request['orderby'] );
133 }
134
135 $orderby = $sort_by_map[ $sort_by ] ?? $default_orderby;
136
137 $date_to = gmdate( 'Y-m-d' );
138
139 if ( is_string( $request['date_to'] ) && $request['date_to'] ) {
140 $date_to = sanitize_text_field( $request['date_to'] );
141 }
142
143 $date_from = gmdate( 'Y-m-d', strtotime( '-27 days', (int) strtotime( $date_to ) ) );
144
145 if ( is_string( $request['date_from'] ) && $request['date_from'] ) {
146 $date_from = sanitize_text_field( $request['date_from'] );
147 }
148
149 $args = array(
150 'date_from' => $date_from,
151 'date_to' => $date_to,
152 'search' => is_string( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '',
153 'campaign_id' => isset( $request['campaign_id'] ) ? absint( $request['campaign_id'] ) : 0,
154 'order_id' => isset( $request['order_id'] ) ? absint( $request['order_id'] ) : 0,
155 'orderby' => $orderby,
156 'order' => is_string( $request['order'] ) ? sanitize_text_field( $request['order'] ) : 'asc',
157 'page' => $page,
158 'limit' => $per_page,
159 );
160
161 $result = CustomerService::get_customers_table( $args );
162 $pagination = $result['pagination'];
163 $total = (int) ( $pagination['total'] ?? 0 );
164 $total_pages = (int) ( $pagination['pages'] ?? 0 );
165
166 $response_data = array(
167 'current_period' => $result['current_period'],
168 'compare_period' => $result['compare_period'],
169 'data' => $this->add_item_links( $result['data'], 'customers' ),
170 'collection' => $this->build_collection_meta( $total, $per_page, $page ),
171 'links' => $this->build_top_links( $request, 'customers', $total_pages, $page ),
172 );
173
174 $response = rest_ensure_response( $response_data );
175 $response->header( 'X-WP-Total', $total );
176 $response->header( 'X-WP-TotalPages', $total_pages );
177
178 return $response;
179 }
180
181 /**
182 * GET /disco/v1/analytics/customers/{id}
183 *
184 * Returns detail for a single customer including aggregated metrics and campaigns used.
185 *
186 * Path param:
187 * id (int) WP User ID (customer_id).
188 *
189 * Query params:
190 * date_from (string Y-m-d) Range start. Default: all-time.
191 * date_to (string Y-m-d) Range end. Default: all-time.
192 *
193 * @param \WP_REST_Request $request Incoming REST request.
194 * @return \WP_REST_Response|\WP_Error
195 */
196 public function get_customer( $request ) {
197 $error = $this->validate_date_params( $request );
198
199 if ( is_wp_error( $error ) ) {
200 return $error;
201 }
202
203 $args = array(
204 'date_from' => is_string( $request['date_from'] ) ? sanitize_text_field( $request['date_from'] ) : '',
205 'date_to' => is_string( $request['date_to'] ) ? sanitize_text_field( $request['date_to'] ) : '',
206 );
207
208 return rest_ensure_response( CustomerService::get_customer( absint( $request['id'] ), $args ) );
209 }
210
211 // =========================================================================
212 // Schema
213 // =========================================================================
214
215 /**
216 * Retrieves the customer analytics schema, conforming to JSON Schema.
217 *
218 * @return array
219 */
220 public function get_customer_schema() { //phpcs:ignore
221 return array(
222 '$schema' => 'http://json-schema.org/draft-04/schema#',
223 'title' => 'analytics-customer',
224 'type' => 'object',
225 'properties' => array(
226 'id' => array(
227 'description' => __( 'WP User ID.', 'disco' ),
228 'type' => 'integer',
229 'context' => array( 'view' ),
230 'readonly' => true,
231 ),
232 'name' => array(
233 'description' => __( 'Display name.', 'disco' ),
234 'type' => 'string',
235 'context' => array( 'view' ),
236 'readonly' => true,
237 ),
238 'email' => array(
239 'description' => __( 'Email address.', 'disco' ),
240 'type' => 'string',
241 'context' => array( 'view' ),
242 'readonly' => true,
243 ),
244 'avatar' => array(
245 'description' => __( 'Gravatar image URL. Empty string when not available.', 'disco' ),
246 'type' => 'string',
247 'context' => array( 'view' ),
248 'readonly' => true,
249 ),
250 'user_name' => array(
251 'description' => __( 'Login username.', 'disco' ),
252 'type' => 'string',
253 'context' => array( 'view' ),
254 'readonly' => true,
255 ),
256 'state' => array(
257 'description' => __( 'Billing state.', 'disco' ),
258 'type' => 'string',
259 'context' => array( 'view' ),
260 'readonly' => true,
261 ),
262 'campaigns' => array(
263 'description' => __( 'Campaigns used by this customer (id, name, intent).', 'disco' ),
264 'type' => 'array',
265 'context' => array( 'view' ),
266 'readonly' => true,
267 ),
268 'orders' => array(
269 'description' => __( 'Total campaign-linked completed orders.', 'disco' ),
270 'type' => 'integer',
271 'context' => array( 'view' ),
272 'readonly' => true,
273 ),
274 'total_spent' => array(
275 'description' => __( 'Sum of order totals.', 'disco' ),
276 'type' => 'number',
277 'context' => array( 'view' ),
278 'readonly' => true,
279 ),
280 ),
281 );
282 }
283
284 }
285