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 / CampaignsApi.php

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

318 lines 10.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 * Analytics REST API — Campaigns 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\CampaignService;
15 use WP_REST_Server;
16
17 /**
18 * Class CampaignsApi
19 *
20 * Registers and handles the following REST API routes:
21 *
22 * - GET /disco/v1/analytics/campaigns — Paginated campaign list.
23 * - GET /disco/v1/analytics/campaigns/{id} — Single campaign KPIs + chart.
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 CampaignsApi extends Base {
33
34 /**
35 * Registers the REST API routes for campaign analytics endpoints.
36 */
37 public function register_routes(): void {
38 // GET /analytics/campaigns
39 register_rest_route(
40 $this->namespace,
41 '/' . $this->rest_base . '/campaigns',
42 array(
43 array(
44 'methods' => WP_REST_Server::READABLE,
45 'callback' => array( $this, 'get_campaigns' ),
46 'permission_callback' => array( $this, 'permissions_check' ),
47 'args' => $this->get_campaigns_params(),
48 ),
49 'schema' => array( $this, 'get_campaign_schema' ),
50 )
51 );
52
53 // GET /analytics/campaigns/{id}
54 register_rest_route(
55 $this->namespace,
56 '/' . $this->rest_base . '/campaigns/(?P<id>[\d]+)',
57 array(
58 'args' => array(
59 'id' => array(
60 'description' => __( 'Campaign ID.', 'disco' ),
61 'type' => 'integer',
62 ),
63 ),
64 array(
65 'methods' => WP_REST_Server::READABLE,
66 'callback' => array( $this, 'get_campaign' ),
67 'permission_callback' => array( $this, 'permissions_check' ),
68 'args' => $this->get_date_params(),
69 ),
70 'schema' => array( $this, 'get_campaign_schema' ),
71 )
72 );
73 }
74
75 // =========================================================================
76 // Callbacks
77 // =========================================================================
78
79 /**
80 * GET /disco/v1/analytics/campaigns
81 *
82 * Returns a paginated list of campaigns with aggregated order metrics.
83 *
84 * Query params:
85 * date_from (string Y-m-d) Range start. Default: all-time.
86 * date_to (string Y-m-d) Range end. Default: all-time.
87 * search (string) Numeric = exact campaign ID; text = name LIKE search.
88 * sort_by (string) Sort field. One of: orders, customers, revenue (default).
89 * page (int ≥ 1) Page number. Default: 1.
90 * limit (int 1–100) Results per page. Default: 10.
91 *
92 * @param \WP_REST_Request $request Incoming REST request.
93 * @return \WP_REST_Response|\WP_Error
94 */
95 // phpcs:ignore SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
96 public function get_campaigns( $request ) {
97 $error = $this->validate_date_params( $request );
98
99 if ( is_wp_error( $error ) ) {
100 return $error;
101 }
102
103 $page = 1;
104 $per_page = 10;
105
106 if ( isset( $request['page'] ) ) {
107 $page = absint( $request['page'] );
108 }
109
110 if ( isset( $request['limit'] ) ) {
111 $per_page = min( absint( $request['limit'] ), 100 );
112 }
113
114 $date_to = gmdate( 'Y-m-d' );
115
116 if ( is_string( $request['date_to'] ) && $request['date_to'] ) {
117 $date_to = sanitize_text_field( $request['date_to'] );
118 }
119
120 $date_from = gmdate( 'Y-m-d', strtotime( '-27 days', (int) strtotime( $date_to ) ) );
121
122 if ( is_string( $request['date_from'] ) && $request['date_from'] ) {
123 $date_from = sanitize_text_field( $request['date_from'] );
124 }
125
126 $args = array(
127 'date_from' => $date_from,
128 'date_to' => $date_to,
129 'search' => is_string( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '',
130 'customer_id' => isset( $request['customer_id'] ) ? absint( $request['customer_id'] ) : 0,
131 'sort_by' => is_string( $request['sort_by'] ) ? sanitize_text_field( $request['sort_by'] ) : 'revenue',
132 'order' => is_string( $request['order'] ) ? strtoupper( sanitize_text_field( $request['order'] ) ) : 'DESC',
133 'page' => $page,
134 'per_page' => $per_page,
135 );
136
137 $result = CampaignService::get_campaigns( $args );
138 $pagination = $result['pagination'];
139 $total = (int) ( $pagination['total'] ?? 0 );
140 $total_pages = (int) ( $pagination['total_pages'] ?? 0 );
141
142 $response_data = array(
143 'current_period' => $result['current_period'],
144 'compare_period' => $result['compare_period'],
145 'data' => $this->add_item_links( $result['data'], 'campaigns', 'campaign_id' ),
146 'collection' => $this->build_collection_meta( $total, $per_page, $page ),
147 'links' => $this->build_top_links( $request, 'campaigns', $total_pages, $page ),
148 );
149
150 $response = rest_ensure_response( $response_data );
151 $response->header( 'X-WP-Total', $total );
152 $response->header( 'X-WP-TotalPages', $total_pages );
153
154 return $response;
155 }
156
157 /**
158 * GET /disco/v1/analytics/campaigns/{id}
159 *
160 * Returns aggregated KPIs, a daily chart, and top-10 products for a single campaign.
161 * Includes campaigns that have been soft-deleted (is_deleted=true) so historical
162 * data is never lost.
163 *
164 * Path param:
165 * id (int) Campaign ID.
166 *
167 * Query params:
168 * date_from (string Y-m-d) Range start. Default: all-time.
169 * date_to (string Y-m-d) Range end. Default: all-time.
170 *
171 * @param \WP_REST_Request $request Incoming REST request.
172 * @return \WP_REST_Response|\WP_Error
173 */
174 public function get_campaign( $request ) {
175 $error = $this->validate_date_params( $request );
176
177 if ( is_wp_error( $error ) ) {
178 return $error;
179 }
180
181 $args = array(
182 'date_from' => is_string( $request['date_from'] ) ? sanitize_text_field( $request['date_from'] ) : '',
183 'date_to' => is_string( $request['date_to'] ) ? sanitize_text_field( $request['date_to'] ) : '',
184 );
185
186 $data = CampaignService::get_campaign( absint( $request['id'] ), $args );
187
188 if ( is_wp_error( $data ) ) {
189 return $data;
190 }
191
192 return rest_ensure_response( $data );
193 }
194
195 // =========================================================================
196 // Param Builders
197 // =========================================================================
198
199 /**
200 * Returns params for the campaign list endpoint.
201 */
202 private function get_campaigns_params(): array {
203 return array_merge(
204 $this->get_date_params(),
205 array(
206 'search' => array(
207 'description' => __( 'Search term. Numeric = exact ID; text = name LIKE search.', 'disco' ),
208 'type' => 'string',
209 'sanitize_callback' => 'sanitize_text_field',
210 ),
211 'customer_id' => array(
212 'description' => __( 'Filter by customer (WP user) ID.', 'disco' ),
213 'type' => 'integer',
214 'sanitize_callback' => 'absint',
215 ),
216 'sort_by' => array(
217 'description' => __( 'Sort field.', 'disco' ),
218 'type' => 'string',
219 'enum' => array( 'orders', 'customers', 'revenue' ),
220 'default' => 'revenue',
221 'sanitize_callback' => 'sanitize_text_field',
222 ),
223 'order' => array(
224 'description' => __( 'Sort direction.', 'disco' ),
225 'type' => 'string',
226 'enum' => array( 'asc', 'desc' ),
227 'default' => 'desc',
228 'sanitize_callback' => 'sanitize_text_field',
229 ),
230 'page' => array(
231 'description' => __( 'Page number.', 'disco' ),
232 'type' => 'integer',
233 'default' => 1,
234 'minimum' => 1,
235 'sanitize_callback' => 'absint',
236 ),
237 'limit' => array(
238 'description' => __( 'Results per page (max 100).', 'disco' ),
239 'type' => 'integer',
240 'default' => 10,
241 'minimum' => 1,
242 'maximum' => 100,
243 'sanitize_callback' => 'absint',
244 ),
245 )
246 );
247 }
248
249 // =========================================================================
250 // Schema
251 // =========================================================================
252
253 /**
254 * Retrieves the campaign analytics schema, conforming to JSON Schema.
255 *
256 * @return array
257 */
258 public function get_campaign_schema() { //phpcs:ignore
259 return array(
260 '$schema' => 'http://json-schema.org/draft-04/schema#',
261 'title' => 'analytics-campaign',
262 'type' => 'object',
263 'properties' => array(
264 'campaign_id' => array(
265 'description' => __( 'Campaign ID.', 'disco' ),
266 'type' => 'integer',
267 'context' => array( 'view' ),
268 'readonly' => true,
269 ),
270 'campaign_name' => array(
271 'description' => __( 'Campaign name. "Unknown" if deleted.', 'disco' ),
272 'type' => 'string',
273 'context' => array( 'view' ),
274 'readonly' => true,
275 ),
276 'intent' => array(
277 'description' => __( 'Campaign intent. "Unknown" if deleted.', 'disco' ),
278 'type' => 'string',
279 'context' => array( 'view' ),
280 'readonly' => true,
281 ),
282 'valid_date' => array(
283 'description' => __( 'Validity date range (from ~ to). "Unknown" if deleted.', 'disco' ),
284 'type' => 'string',
285 'context' => array( 'view' ),
286 'readonly' => true,
287 ),
288 'orders' => array(
289 'description' => __( 'Total orders using this campaign.', 'disco' ),
290 'type' => 'integer',
291 'context' => array( 'view' ),
292 'readonly' => true,
293 ),
294 'customers' => array(
295 'description' => __( 'Distinct customers.', 'disco' ),
296 'type' => 'integer',
297 'context' => array( 'view' ),
298 'readonly' => true,
299 ),
300 'revenue' => array(
301 'description' => __( 'Total revenue from this campaign.', 'disco' ),
302 'type' => 'number',
303 'context' => array( 'view' ),
304 'readonly' => true,
305 ),
306 'status' => array(
307 'description' => __( 'Campaign status: active, inactive, or deleted.', 'disco' ),
308 'type' => 'string',
309 'enum' => array( 'active', 'inactive', 'deleted' ),
310 'context' => array( 'view' ),
311 'readonly' => true,
312 ),
313 ),
314 );
315 }
316
317 }
318