PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / image-replace / REST / ImageSearch.php

ImageSearch.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at modules/image-replace/REST/ImageSearch.php

170 lines 5.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Image search endpoint for the image-replace module (spec 023-image-replace-module).
5 *
6 * Relocated from includes/API/AIContent.php's search_images()/register_routes() —
7 * behavior byte-identical, same route (`GET /templately/v1/ai-content/images`, kept
8 * literal rather than reconstructed via an `$endpoint` property, since the route path
9 * is a frozen frontend contract — react-src's imageApiHelpers.ts hardcodes it), same
10 * params/validation/sanitization. No custom permission_check needed: the route fell
11 * through to the base API::_permission_check() default in AIContent.php (its custom
12 * override only branches on the ai-update/ai-update-preview routes), so omitting an
13 * override here reproduces the exact same effective behavior.
14 *
15 * @package Templately
16 */
17
18 namespace Templately\Modules\ImageReplace\REST;
19
20 use Templately\API\API;
21 use Templately\Utils\Helper;
22 use Templately\Utils\Response\ResponseNormalizer;
23 use WP_REST_Request;
24
25 class ImageSearch extends API {
26
27 public function register_routes() {
28 $this->get('ai-content/images', [$this, 'search_images'], [
29 'query' => [
30 'required' => false,
31 'sanitize_callback' => 'sanitize_text_field',
32 'validate_callback' => function($param, $request, $key) {
33 return is_string($param) && strlen($param) <= 255;
34 },
35 ],
36 'orientation' => [
37 'required' => false,
38 'default' => 'all',
39 'sanitize_callback' => 'sanitize_text_field',
40 'validate_callback' => function($param, $request, $key) {
41 $allowed_orientations = ['all', 'landscape', 'portrait', 'square'];
42 return in_array($param, $allowed_orientations, true);
43 },
44 ],
45 'size' => [
46 'required' => false,
47 'default' => 'medium',
48 'sanitize_callback' => 'sanitize_text_field',
49 'validate_callback' => function($param, $request, $key) {
50 $allowed_sizes = ['small', 'medium', 'large'];
51 return in_array($param, $allowed_sizes, true);
52 },
53 ],
54 'color' => [
55 'required' => false,
56 'sanitize_callback' => 'sanitize_text_field',
57 'validate_callback' => function($param, $request, $key) {
58 return is_string($param) && strlen($param) <= 50;
59 },
60 ],
61 'page' => [
62 'required' => false,
63 'default' => 1,
64 'sanitize_callback' => 'absint',
65 'validate_callback' => function($param, $request, $key) {
66 return is_numeric($param) && $param > 0 && $param <= 1000;
67 },
68 ],
69 'per_page' => [
70 'required' => false,
71 'default' => 20,
72 'sanitize_callback' => 'absint',
73 'validate_callback' => function($param, $request, $key) {
74 return is_numeric($param) && $param > 0 && $param <= 100;
75 },
76 ],
77 ]);
78 }
79
80 /**
81 * Search images endpoint
82 *
83 * @param WP_REST_Request $request
84 * @return WP_REST_Response|WP_Error
85 */
86 public function search_images(WP_REST_Request $request) {
87 // Get and sanitize parameters
88 $query = $this->get_param('query', '');
89 $orientation = $this->get_param('orientation', 'all');
90 $size = $this->get_param('size', 'medium');
91 $color = $this->get_param('color', '');
92 $page = $this->get_param('page', 1, 'absint');
93 $per_page = $this->get_param('per_page', 20, 'absint');
94
95 // Validate required query parameter
96 if (empty($query)) {
97 return $this->error(
98 'missing_query',
99 __('Search query is required.', 'templately'),
100 'search_images',
101 400
102 );
103 }
104
105 // Prepare API request parameters
106 $api_params = [
107 'query' => urlencode($query),
108 'page' => $page,
109 'per_page' => $per_page,
110 ];
111
112 // Add optional parameters if provided
113 if ($orientation !== 'all') {
114 $api_params['orientation'] = $orientation;
115 }
116
117 if (!empty($size)) {
118 $api_params['size'] = $size;
119 }
120
121 if (!empty($color)) {
122 $api_params['color'] = $color;
123 }
124
125 // Make API request to external image service
126 $extra_headers = [
127 'Content-Type' => 'application/json',
128 ];
129
130 $response = Helper::make_api_get_request('v2/images', $api_params, $extra_headers, 30);
131
132 // 043 FR-003/FR-007 — one normalizer covers transport failure, non-200,
133 // undecodable body and error status in a single pass.
134 //
135 // This endpoint used to flatten a 422 to "External API returned error
136 // code: 422" and throw away `errors: { query: [ "Search query is
137 // required." ] }` — the user saw a number instead of the sentence telling
138 // them what to fix. `fields` is now carried through to the client.
139 $normalized = ResponseNormalizer::normalize($response, ['side_effects' => false]);
140
141 if ($normalized->is_error()) {
142 $error = $normalized->error();
143
144 return Helper::error(
145 $error->code(),
146 $error->message(),
147 'search_images',
148 $error->status(),
149 ['fields' => $error->fields(), 'context' => $error->context()]
150 );
151 }
152
153 // Extract nested data from the response
154 $response_data = $normalized->payload() ?: [];
155 $images = $response_data['images'] ?? [];
156 $total_results = $response_data['total_results'] ?? 0;
157 $current_page = $response_data['page'] ?? $page;
158 $per_page_count = $response_data['per_page'] ?? $per_page;
159
160 // Return successful response with properly mapped data
161 return $this->success([
162 'images' => $images,
163 'total' => $total_results,
164 'page' => $current_page,
165 'per_page' => $per_page_count,
166 'total_pages' => $total_results > 0 ? ceil($total_results / $per_page_count) : 0,
167 ]);
168 }
169 }
170