Skip to content

Commit 484e14e

Browse files
committed
Add API endpoint for Product Brands
1 parent d5bfd1d commit 484e14e

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

includes/API.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public function register_routes(): void {
9797
'customers' => API\Customers_Controller::class,
9898
'product_tags' => API\Product_Tags_Controller::class,
9999
'product_categories' => API\Product_Categories_Controller::class,
100+
'product_brands' => API\Product_Brands_Controller::class,
100101
'taxes' => API\Taxes_Controller::class,
101102
'shipping_methods' => API\Shipping_Methods_Controller::class,
102103
'tax_classes' => API\Tax_Classes_Controller::class,
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
namespace WCPOS\WooCommercePOS\API;
4+
5+
\defined( 'ABSPATH' ) || die;
6+
7+
if ( ! class_exists( 'WC_REST_Product_Brands_Controller' ) ) {
8+
return;
9+
}
10+
11+
use Exception;
12+
use WC_REST_Product_Brands_Controller;
13+
use WCPOS\WooCommercePOS\Logger;
14+
use WP_REST_Request;
15+
use WP_REST_Response;
16+
17+
/**
18+
* Product Brands controller class.
19+
*
20+
* @NOTE: methods not prefixed with wcpos_ will override WC_REST_Product_Brands_Controller methods
21+
*/
22+
class Product_Brands_Controller extends WC_REST_Product_Brands_Controller {
23+
use Traits\Uuid_Handler;
24+
use Traits\WCPOS_REST_API;
25+
26+
/**
27+
* Endpoint namespace.
28+
*
29+
* @var string
30+
*/
31+
protected $namespace = 'wcpos/v1';
32+
33+
/**
34+
* Store the request object for use in lifecycle methods.
35+
*
36+
* @var WP_REST_Request
37+
*/
38+
protected $wcpos_request;
39+
40+
/**
41+
* Dispatch request to parent controller, or override if needed.
42+
*
43+
* @param mixed $dispatch_result Dispatch result, will be used if not empty.
44+
* @param WP_REST_Request $request Request used to generate the response.
45+
* @param string $route Route matched for the request.
46+
* @param array $handler Route handler used for the request.
47+
*/
48+
public function wcpos_dispatch_request( $dispatch_result, WP_REST_Request $request, $route, $handler ) {
49+
$this->wcpos_request = $request;
50+
51+
add_filter( 'woocommerce_rest_prepare_product_brand', array( $this, 'wcpos_product_brands_response' ), 10, 3 );
52+
add_filter( 'woocommerce_rest_product_brand_query', array( $this, 'wcpos_product_brand_query' ), 10, 2 );
53+
54+
/*
55+
* Check if the request is for all brands and if the 'posts_per_page' is set to -1.
56+
* Optimised query for getting all brand IDs.
57+
*/
58+
if ( -1 == $request->get_param( 'posts_per_page' ) && null !== $request->get_param( 'fields' ) ) {
59+
return $this->wcpos_get_all_posts( $request );
60+
}
61+
62+
return $dispatch_result;
63+
}
64+
65+
/**
66+
* Filter the brand response.
67+
*
68+
* @param WP_REST_Response $response The response object.
69+
* @param object $item The original term object.
70+
* @param WP_REST_Request $request Request object.
71+
*
72+
* @return WP_REST_Response $response The response object.
73+
*/
74+
public function wcpos_product_brands_response( WP_REST_Response $response, object $item, WP_REST_Request $request ): WP_REST_Response {
75+
$data = $response->get_data();
76+
77+
// Make sure the term has a uuid
78+
$data['uuid'] = $this->get_term_uuid( $item );
79+
80+
// Reset the new response data
81+
$response->set_data( $data );
82+
83+
return $response;
84+
}
85+
86+
/**
87+
* Filter the brand query.
88+
*
89+
* @param array $args Query arguments.
90+
* @param WP_REST_Request $request Request object.
91+
*/
92+
public function wcpos_product_brand_query( array $args, WP_REST_Request $request ): array {
93+
// Check for wcpos_include/wcpos_exclude parameter.
94+
if ( isset( $request['wcpos_include'] ) || isset( $request['wcpos_exclude'] ) ) {
95+
// Add a custom WHERE clause to the query.
96+
add_filter( 'terms_clauses', array( $this, 'wcpos_terms_clauses_include_exclude' ), 10, 3 );
97+
}
98+
99+
return $args;
100+
}
101+
102+
/**
103+
* Filters the terms query SQL clauses.
104+
*
105+
* @param string[] $clauses {
106+
* Associative array of the clauses for the query.
107+
*
108+
* @var string The SELECT clause of the query.
109+
* @var string The JOIN clause of the query.
110+
* @var string The WHERE clause of the query.
111+
* @var string The DISTINCT clause of the query.
112+
* @var string The ORDER BY clause of the query.
113+
* @var string The ORDER clause of the query.
114+
* @var string The LIMIT clause of the query.
115+
* }
116+
*
117+
* @param string[] $taxonomies An array of taxonomy names.
118+
* @param array $args An array of term query arguments.
119+
*
120+
* @return string[] $clauses
121+
*/
122+
public function wcpos_terms_clauses_include_exclude( array $clauses, array $taxonomies, array $args ) {
123+
global $wpdb;
124+
125+
// Handle 'wcpos_include'
126+
if ( ! empty( $this->wcpos_request['wcpos_include'] ) ) {
127+
$include_ids = array_map( 'intval', $this->wcpos_request['wcpos_include'] );
128+
$ids_format = implode( ',', array_fill( 0, \count( $include_ids ), '%d' ) );
129+
$clauses['where'] .= $wpdb->prepare( " AND t.term_id IN ($ids_format) ", $include_ids );
130+
}
131+
132+
// Handle 'wcpos_exclude'
133+
if ( ! empty( $this->wcpos_request['wcpos_exclude'] ) ) {
134+
$exclude_ids = array_map( 'intval', $this->wcpos_request['wcpos_exclude'] );
135+
$ids_format = implode( ',', array_fill( 0, \count( $exclude_ids ), '%d' ) );
136+
$clauses['where'] .= $wpdb->prepare( " AND t.term_id NOT IN ($ids_format) ", $exclude_ids );
137+
}
138+
139+
return $clauses;
140+
}
141+
142+
/**
143+
* Returns array of all product brand ids.
144+
*
145+
* @param WP_REST_Request $request Full details about the request.
146+
*
147+
* @return WP_Error|WP_REST_Response
148+
*/
149+
public function wcpos_get_all_posts( $request ) {
150+
// Start timing execution.
151+
$start_time = microtime( true );
152+
$modified_after = $request->get_param( 'modified_after' );
153+
154+
$args = array(
155+
'taxonomy' => 'product_brand',
156+
'hide_empty' => false,
157+
'fields' => 'ids',
158+
);
159+
160+
try {
161+
/**
162+
* @TODO - terms don't have a modified date, it would be good to add a term_meta for last_update
163+
* - ideally WooCommerce would provide a modified_after filter for terms
164+
* - for now we'll just return empty for modified terms
165+
*/
166+
$results = $modified_after ? array() : get_terms( $args );
167+
168+
// Format the response.
169+
$formatted_results = array_map(
170+
function ( $id ) {
171+
return array( 'id' => (int) $id );
172+
},
173+
$results
174+
);
175+
176+
// Get the total number of brands for the given criteria.
177+
$total = \count( $formatted_results );
178+
179+
// Collect execution time and server load.
180+
$execution_time = microtime( true ) - $start_time;
181+
$execution_time_ms = number_format( $execution_time * 1000, 2 );
182+
$server_load = $this->get_server_load();
183+
184+
$response = rest_ensure_response( $formatted_results );
185+
$response->header( 'X-WP-Total', (int) $total );
186+
$response->header( 'X-Execution-Time', $execution_time_ms . ' ms' );
187+
$response->header( 'X-Server-Load', json_encode( $server_load ) );
188+
189+
return $response;
190+
} catch ( Exception $e ) {
191+
Logger::log( 'Error fetching product brand IDs: ' . $e->getMessage() );
192+
193+
return new \WP_Error(
194+
'woocommerce_pos_rest_cannot_fetch',
195+
'Error fetching product brand IDs.',
196+
array( 'status' => 500 )
197+
);
198+
}
199+
}
200+
}

0 commit comments

Comments
 (0)