@mytechtoday/augment-extensions 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/domain-rules/wordpress/README.md +163 -0
- package/augment-extensions/domain-rules/wordpress/module.json +32 -0
- package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
- package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
- package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
- package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
- package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
- package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
- package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
- package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
- package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
- package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
- package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
- package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
- package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
- package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
- package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
- package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
- package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
- package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
- package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
- package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
- package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
- package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
- package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
- package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
- package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
- package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
- package/cli/dist/cli.js +5 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +41 -0
- package/cli/dist/commands/show.js.map +1 -1
- package/modules.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
# REST API Endpoint Example
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This example demonstrates a complete custom REST API endpoint with route registration, permission callback, endpoint handler, sanitization, validation, JSON response, and error handling.
|
|
6
|
+
|
|
7
|
+
**Use Case**: Custom API endpoints for external integrations
|
|
8
|
+
**Complexity**: Medium
|
|
9
|
+
**Prerequisites**: WordPress 5.0+, PHP 7.4+
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Complete Example: "Book Reviews API"
|
|
14
|
+
|
|
15
|
+
A custom REST API for managing book reviews with full CRUD operations, authentication, and validation.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
book-reviews-api/
|
|
23
|
+
├── book-reviews-api.php # Main plugin file
|
|
24
|
+
├── includes/
|
|
25
|
+
│ ├── class-api.php # API controller
|
|
26
|
+
│ ├── class-validator.php # Validation logic
|
|
27
|
+
│ └── class-sanitizer.php # Sanitization logic
|
|
28
|
+
└── readme.txt # Plugin readme
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 1. Main Plugin File
|
|
34
|
+
|
|
35
|
+
### File: `book-reviews-api.php`
|
|
36
|
+
|
|
37
|
+
```php
|
|
38
|
+
<?php
|
|
39
|
+
/**
|
|
40
|
+
* Plugin Name: Book Reviews API
|
|
41
|
+
* Description: Custom REST API for managing book reviews
|
|
42
|
+
* Version: 1.0.0
|
|
43
|
+
* Requires at least: 5.0
|
|
44
|
+
* Requires PHP: 7.4
|
|
45
|
+
* Author: Your Name
|
|
46
|
+
* License: GPL-2.0+
|
|
47
|
+
* Text Domain: book-reviews-api
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
if (!defined('ABSPATH')) {
|
|
51
|
+
exit;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Include dependencies
|
|
55
|
+
require_once plugin_dir_path(__FILE__) . 'includes/class-api.php';
|
|
56
|
+
require_once plugin_dir_path(__FILE__) . 'includes/class-validator.php';
|
|
57
|
+
require_once plugin_dir_path(__FILE__) . 'includes/class-sanitizer.php';
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Initialize the API
|
|
61
|
+
*/
|
|
62
|
+
function bra_init() {
|
|
63
|
+
$api = new Book_Reviews_API();
|
|
64
|
+
$api->register_routes();
|
|
65
|
+
}
|
|
66
|
+
add_action('rest_api_init', 'bra_init');
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 2. API Controller
|
|
72
|
+
|
|
73
|
+
### File: `includes/class-api.php`
|
|
74
|
+
|
|
75
|
+
```php
|
|
76
|
+
<?php
|
|
77
|
+
/**
|
|
78
|
+
* Book Reviews API Controller
|
|
79
|
+
*/
|
|
80
|
+
class Book_Reviews_API {
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Namespace for API routes
|
|
84
|
+
*/
|
|
85
|
+
private $namespace = 'book-reviews/v1';
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Base route
|
|
89
|
+
*/
|
|
90
|
+
private $base = 'reviews';
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Register all routes
|
|
94
|
+
*/
|
|
95
|
+
public function register_routes() {
|
|
96
|
+
// GET - List all reviews
|
|
97
|
+
register_rest_route($this->namespace, '/' . $this->base, array(
|
|
98
|
+
'methods' => WP_REST_Server::READABLE,
|
|
99
|
+
'callback' => array($this, 'get_items'),
|
|
100
|
+
'permission_callback' => '__return_true',
|
|
101
|
+
'args' => $this->get_collection_params(),
|
|
102
|
+
));
|
|
103
|
+
|
|
104
|
+
// POST - Create review
|
|
105
|
+
register_rest_route($this->namespace, '/' . $this->base, array(
|
|
106
|
+
'methods' => WP_REST_Server::CREATABLE,
|
|
107
|
+
'callback' => array($this, 'create_item'),
|
|
108
|
+
'permission_callback' => array($this, 'create_item_permissions_check'),
|
|
109
|
+
'args' => $this->get_endpoint_args_for_item_schema(),
|
|
110
|
+
));
|
|
111
|
+
|
|
112
|
+
// GET - Get single review
|
|
113
|
+
register_rest_route($this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
|
|
114
|
+
'methods' => WP_REST_Server::READABLE,
|
|
115
|
+
'callback' => array($this, 'get_item'),
|
|
116
|
+
'permission_callback' => '__return_true',
|
|
117
|
+
'args' => array(
|
|
118
|
+
'id' => array(
|
|
119
|
+
'validate_callback' => function($param) {
|
|
120
|
+
return is_numeric($param);
|
|
121
|
+
},
|
|
122
|
+
),
|
|
123
|
+
),
|
|
124
|
+
));
|
|
125
|
+
|
|
126
|
+
// PUT/PATCH - Update review
|
|
127
|
+
register_rest_route($this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
|
|
128
|
+
'methods' => WP_REST_Server::EDITABLE,
|
|
129
|
+
'callback' => array($this, 'update_item'),
|
|
130
|
+
'permission_callback' => array($this, 'update_item_permissions_check'),
|
|
131
|
+
'args' => $this->get_endpoint_args_for_item_schema(),
|
|
132
|
+
));
|
|
133
|
+
|
|
134
|
+
// DELETE - Delete review
|
|
135
|
+
register_rest_route($this->namespace, '/' . $this->base . '/(?P<id>[\d]+)', array(
|
|
136
|
+
'methods' => WP_REST_Server::DELETABLE,
|
|
137
|
+
'callback' => array($this, 'delete_item'),
|
|
138
|
+
'permission_callback' => array($this, 'delete_item_permissions_check'),
|
|
139
|
+
));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Get collection of reviews
|
|
144
|
+
*/
|
|
145
|
+
public function get_items($request) {
|
|
146
|
+
$args = array(
|
|
147
|
+
'post_type' => 'book_review',
|
|
148
|
+
'posts_per_page' => $request->get_param('per_page') ?: 10,
|
|
149
|
+
'paged' => $request->get_param('page') ?: 1,
|
|
150
|
+
'orderby' => $request->get_param('orderby') ?: 'date',
|
|
151
|
+
'order' => $request->get_param('order') ?: 'DESC',
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
$query = new WP_Query($args);
|
|
155
|
+
$reviews = array();
|
|
156
|
+
|
|
157
|
+
foreach ($query->posts as $post) {
|
|
158
|
+
$reviews[] = $this->prepare_item_for_response($post);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
$response = rest_ensure_response($reviews);
|
|
162
|
+
|
|
163
|
+
// Add pagination headers
|
|
164
|
+
$response->header('X-WP-Total', $query->found_posts);
|
|
165
|
+
$response->header('X-WP-TotalPages', $query->max_num_pages);
|
|
166
|
+
|
|
167
|
+
return $response;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get single review
|
|
172
|
+
*/
|
|
173
|
+
public function get_item($request) {
|
|
174
|
+
$id = (int) $request->get_param('id');
|
|
175
|
+
$post = get_post($id);
|
|
176
|
+
|
|
177
|
+
if (!$post || $post->post_type !== 'book_review') {
|
|
178
|
+
return new WP_Error(
|
|
179
|
+
'rest_review_not_found',
|
|
180
|
+
__('Review not found.', 'book-reviews-api'),
|
|
181
|
+
array('status' => 404)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
$data = $this->prepare_item_for_response($post);
|
|
186
|
+
return rest_ensure_response($data);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Create new review
|
|
191
|
+
*/
|
|
192
|
+
public function create_item($request) {
|
|
193
|
+
$validator = new Book_Reviews_Validator();
|
|
194
|
+
$sanitizer = new Book_Reviews_Sanitizer();
|
|
195
|
+
|
|
196
|
+
// Validate input
|
|
197
|
+
$validation = $validator->validate_review_data($request->get_params());
|
|
198
|
+
if (is_wp_error($validation)) {
|
|
199
|
+
return $validation;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Sanitize input
|
|
203
|
+
$data = $sanitizer->sanitize_review_data($request->get_params());
|
|
204
|
+
|
|
205
|
+
// Create post
|
|
206
|
+
$post_id = wp_insert_post(array(
|
|
207
|
+
'post_type' => 'book_review',
|
|
208
|
+
'post_title' => $data['title'],
|
|
209
|
+
'post_content' => $data['content'],
|
|
210
|
+
'post_status' => 'publish',
|
|
211
|
+
'post_author' => get_current_user_id(),
|
|
212
|
+
));
|
|
213
|
+
|
|
214
|
+
if (is_wp_error($post_id)) {
|
|
215
|
+
return new WP_Error(
|
|
216
|
+
'rest_review_create_failed',
|
|
217
|
+
__('Failed to create review.', 'book-reviews-api'),
|
|
218
|
+
array('status' => 500)
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Save meta data
|
|
223
|
+
update_post_meta($post_id, '_book_title', $data['book_title']);
|
|
224
|
+
update_post_meta($post_id, '_book_author', $data['book_author']);
|
|
225
|
+
update_post_meta($post_id, '_rating', $data['rating']);
|
|
226
|
+
|
|
227
|
+
$post = get_post($post_id);
|
|
228
|
+
$response = $this->prepare_item_for_response($post);
|
|
229
|
+
|
|
230
|
+
return rest_ensure_response($response);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Update review
|
|
235
|
+
*/
|
|
236
|
+
public function update_item($request) {
|
|
237
|
+
$id = (int) $request->get_param('id');
|
|
238
|
+
$post = get_post($id);
|
|
239
|
+
|
|
240
|
+
if (!$post || $post->post_type !== 'book_review') {
|
|
241
|
+
return new WP_Error(
|
|
242
|
+
'rest_review_not_found',
|
|
243
|
+
__('Review not found.', 'book-reviews-api'),
|
|
244
|
+
array('status' => 404)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
$validator = new Book_Reviews_Validator();
|
|
249
|
+
$sanitizer = new Book_Reviews_Sanitizer();
|
|
250
|
+
|
|
251
|
+
// Validate input
|
|
252
|
+
$validation = $validator->validate_review_data($request->get_params());
|
|
253
|
+
if (is_wp_error($validation)) {
|
|
254
|
+
return $validation;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Sanitize input
|
|
258
|
+
$data = $sanitizer->sanitize_review_data($request->get_params());
|
|
259
|
+
|
|
260
|
+
// Update post
|
|
261
|
+
$updated = wp_update_post(array(
|
|
262
|
+
'ID' => $id,
|
|
263
|
+
'post_title' => $data['title'],
|
|
264
|
+
'post_content' => $data['content'],
|
|
265
|
+
));
|
|
266
|
+
|
|
267
|
+
if (is_wp_error($updated)) {
|
|
268
|
+
return new WP_Error(
|
|
269
|
+
'rest_review_update_failed',
|
|
270
|
+
__('Failed to update review.', 'book-reviews-api'),
|
|
271
|
+
array('status' => 500)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Update meta data
|
|
276
|
+
update_post_meta($id, '_book_title', $data['book_title']);
|
|
277
|
+
update_post_meta($id, '_book_author', $data['book_author']);
|
|
278
|
+
update_post_meta($id, '_rating', $data['rating']);
|
|
279
|
+
|
|
280
|
+
$post = get_post($id);
|
|
281
|
+
$response = $this->prepare_item_for_response($post);
|
|
282
|
+
|
|
283
|
+
return rest_ensure_response($response);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Delete review
|
|
288
|
+
*/
|
|
289
|
+
public function delete_item($request) {
|
|
290
|
+
$id = (int) $request->get_param('id');
|
|
291
|
+
$post = get_post($id);
|
|
292
|
+
|
|
293
|
+
if (!$post || $post->post_type !== 'book_review') {
|
|
294
|
+
return new WP_Error(
|
|
295
|
+
'rest_review_not_found',
|
|
296
|
+
__('Review not found.', 'book-reviews-api'),
|
|
297
|
+
array('status' => 404)
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
$previous = $this->prepare_item_for_response($post);
|
|
302
|
+
$result = wp_delete_post($id, true);
|
|
303
|
+
|
|
304
|
+
if (!$result) {
|
|
305
|
+
return new WP_Error(
|
|
306
|
+
'rest_review_delete_failed',
|
|
307
|
+
__('Failed to delete review.', 'book-reviews-api'),
|
|
308
|
+
array('status' => 500)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return rest_ensure_response(array(
|
|
313
|
+
'deleted' => true,
|
|
314
|
+
'previous' => $previous,
|
|
315
|
+
));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Permission check for creating reviews
|
|
320
|
+
*/
|
|
321
|
+
public function create_item_permissions_check($request) {
|
|
322
|
+
if (!is_user_logged_in()) {
|
|
323
|
+
return new WP_Error(
|
|
324
|
+
'rest_forbidden',
|
|
325
|
+
__('You must be logged in to create reviews.', 'book-reviews-api'),
|
|
326
|
+
array('status' => 401)
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (!current_user_can('publish_posts')) {
|
|
331
|
+
return new WP_Error(
|
|
332
|
+
'rest_forbidden',
|
|
333
|
+
__('You do not have permission to create reviews.', 'book-reviews-api'),
|
|
334
|
+
array('status' => 403)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Permission check for updating reviews
|
|
343
|
+
*/
|
|
344
|
+
public function update_item_permissions_check($request) {
|
|
345
|
+
$id = (int) $request->get_param('id');
|
|
346
|
+
$post = get_post($id);
|
|
347
|
+
|
|
348
|
+
if (!$post) {
|
|
349
|
+
return new WP_Error(
|
|
350
|
+
'rest_review_not_found',
|
|
351
|
+
__('Review not found.', 'book-reviews-api'),
|
|
352
|
+
array('status' => 404)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (!current_user_can('edit_post', $id)) {
|
|
357
|
+
return new WP_Error(
|
|
358
|
+
'rest_forbidden',
|
|
359
|
+
__('You do not have permission to edit this review.', 'book-reviews-api'),
|
|
360
|
+
array('status' => 403)
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Permission check for deleting reviews
|
|
369
|
+
*/
|
|
370
|
+
public function delete_item_permissions_check($request) {
|
|
371
|
+
$id = (int) $request->get_param('id');
|
|
372
|
+
$post = get_post($id);
|
|
373
|
+
|
|
374
|
+
if (!$post) {
|
|
375
|
+
return new WP_Error(
|
|
376
|
+
'rest_review_not_found',
|
|
377
|
+
__('Review not found.', 'book-reviews-api'),
|
|
378
|
+
array('status' => 404)
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (!current_user_can('delete_post', $id)) {
|
|
383
|
+
return new WP_Error(
|
|
384
|
+
'rest_forbidden',
|
|
385
|
+
__('You do not have permission to delete this review.', 'book-reviews-api'),
|
|
386
|
+
array('status' => 403)
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Prepare item for response
|
|
395
|
+
*/
|
|
396
|
+
private function prepare_item_for_response($post) {
|
|
397
|
+
return array(
|
|
398
|
+
'id' => $post->ID,
|
|
399
|
+
'title' => $post->post_title,
|
|
400
|
+
'content' => $post->post_content,
|
|
401
|
+
'book_title' => get_post_meta($post->ID, '_book_title', true),
|
|
402
|
+
'book_author' => get_post_meta($post->ID, '_book_author', true),
|
|
403
|
+
'rating' => (int) get_post_meta($post->ID, '_rating', true),
|
|
404
|
+
'date' => $post->post_date,
|
|
405
|
+
'author' => $post->post_author,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get collection parameters
|
|
411
|
+
*/
|
|
412
|
+
private function get_collection_params() {
|
|
413
|
+
return array(
|
|
414
|
+
'page' => array(
|
|
415
|
+
'description' => __('Current page of the collection.', 'book-reviews-api'),
|
|
416
|
+
'type' => 'integer',
|
|
417
|
+
'default' => 1,
|
|
418
|
+
'sanitize_callback' => 'absint',
|
|
419
|
+
),
|
|
420
|
+
'per_page' => array(
|
|
421
|
+
'description' => __('Maximum number of items per page.', 'book-reviews-api'),
|
|
422
|
+
'type' => 'integer',
|
|
423
|
+
'default' => 10,
|
|
424
|
+
'sanitize_callback' => 'absint',
|
|
425
|
+
),
|
|
426
|
+
'orderby' => array(
|
|
427
|
+
'description' => __('Sort collection by field.', 'book-reviews-api'),
|
|
428
|
+
'type' => 'string',
|
|
429
|
+
'default' => 'date',
|
|
430
|
+
'enum' => array('date', 'title', 'rating'),
|
|
431
|
+
),
|
|
432
|
+
'order' => array(
|
|
433
|
+
'description' => __('Order sort attribute ascending or descending.', 'book-reviews-api'),
|
|
434
|
+
'type' => 'string',
|
|
435
|
+
'default' => 'DESC',
|
|
436
|
+
'enum' => array('ASC', 'DESC'),
|
|
437
|
+
),
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Get endpoint args for item schema
|
|
443
|
+
*/
|
|
444
|
+
private function get_endpoint_args_for_item_schema() {
|
|
445
|
+
return array(
|
|
446
|
+
'title' => array(
|
|
447
|
+
'description' => __('Review title.', 'book-reviews-api'),
|
|
448
|
+
'type' => 'string',
|
|
449
|
+
'required' => true,
|
|
450
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
451
|
+
),
|
|
452
|
+
'content' => array(
|
|
453
|
+
'description' => __('Review content.', 'book-reviews-api'),
|
|
454
|
+
'type' => 'string',
|
|
455
|
+
'required' => true,
|
|
456
|
+
'sanitize_callback' => 'wp_kses_post',
|
|
457
|
+
),
|
|
458
|
+
'book_title' => array(
|
|
459
|
+
'description' => __('Book title.', 'book-reviews-api'),
|
|
460
|
+
'type' => 'string',
|
|
461
|
+
'required' => true,
|
|
462
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
463
|
+
),
|
|
464
|
+
'book_author' => array(
|
|
465
|
+
'description' => __('Book author.', 'book-reviews-api'),
|
|
466
|
+
'type' => 'string',
|
|
467
|
+
'required' => true,
|
|
468
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
469
|
+
),
|
|
470
|
+
'rating' => array(
|
|
471
|
+
'description' => __('Rating (1-5).', 'book-reviews-api'),
|
|
472
|
+
'type' => 'integer',
|
|
473
|
+
'required' => true,
|
|
474
|
+
'minimum' => 1,
|
|
475
|
+
'maximum' => 5,
|
|
476
|
+
'sanitize_callback' => 'absint',
|
|
477
|
+
),
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## 3. Validator Class
|
|
486
|
+
|
|
487
|
+
### File: `includes/class-validator.php`
|
|
488
|
+
|
|
489
|
+
```php
|
|
490
|
+
<?php
|
|
491
|
+
/**
|
|
492
|
+
* Book Reviews Validator
|
|
493
|
+
*/
|
|
494
|
+
class Book_Reviews_Validator {
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Validate review data
|
|
498
|
+
*/
|
|
499
|
+
public function validate_review_data($data) {
|
|
500
|
+
$errors = new WP_Error();
|
|
501
|
+
|
|
502
|
+
// Validate title
|
|
503
|
+
if (empty($data['title'])) {
|
|
504
|
+
$errors->add(
|
|
505
|
+
'missing_title',
|
|
506
|
+
__('Review title is required.', 'book-reviews-api'),
|
|
507
|
+
array('status' => 400)
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Validate content
|
|
512
|
+
if (empty($data['content'])) {
|
|
513
|
+
$errors->add(
|
|
514
|
+
'missing_content',
|
|
515
|
+
__('Review content is required.', 'book-reviews-api'),
|
|
516
|
+
array('status' => 400)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Validate book title
|
|
521
|
+
if (empty($data['book_title'])) {
|
|
522
|
+
$errors->add(
|
|
523
|
+
'missing_book_title',
|
|
524
|
+
__('Book title is required.', 'book-reviews-api'),
|
|
525
|
+
array('status' => 400)
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Validate book author
|
|
530
|
+
if (empty($data['book_author'])) {
|
|
531
|
+
$errors->add(
|
|
532
|
+
'missing_book_author',
|
|
533
|
+
__('Book author is required.', 'book-reviews-api'),
|
|
534
|
+
array('status' => 400)
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Validate rating
|
|
539
|
+
if (!isset($data['rating'])) {
|
|
540
|
+
$errors->add(
|
|
541
|
+
'missing_rating',
|
|
542
|
+
__('Rating is required.', 'book-reviews-api'),
|
|
543
|
+
array('status' => 400)
|
|
544
|
+
);
|
|
545
|
+
} elseif ($data['rating'] < 1 || $data['rating'] > 5) {
|
|
546
|
+
$errors->add(
|
|
547
|
+
'invalid_rating',
|
|
548
|
+
__('Rating must be between 1 and 5.', 'book-reviews-api'),
|
|
549
|
+
array('status' => 400)
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if ($errors->has_errors()) {
|
|
554
|
+
return $errors;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
---
|
|
563
|
+
|
|
564
|
+
## 4. Sanitizer Class
|
|
565
|
+
|
|
566
|
+
### File: `includes/class-sanitizer.php`
|
|
567
|
+
|
|
568
|
+
```php
|
|
569
|
+
<?php
|
|
570
|
+
/**
|
|
571
|
+
* Book Reviews Sanitizer
|
|
572
|
+
*/
|
|
573
|
+
class Book_Reviews_Sanitizer {
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Sanitize review data
|
|
577
|
+
*/
|
|
578
|
+
public function sanitize_review_data($data) {
|
|
579
|
+
return array(
|
|
580
|
+
'title' => sanitize_text_field($data['title']),
|
|
581
|
+
'content' => wp_kses_post($data['content']),
|
|
582
|
+
'book_title' => sanitize_text_field($data['book_title']),
|
|
583
|
+
'book_author' => sanitize_text_field($data['book_author']),
|
|
584
|
+
'rating' => absint($data['rating']),
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
---
|
|
591
|
+
|
|
592
|
+
## 5. Testing Steps
|
|
593
|
+
|
|
594
|
+
### Manual Testing with cURL
|
|
595
|
+
|
|
596
|
+
**1. List all reviews (GET)**
|
|
597
|
+
|
|
598
|
+
```bash
|
|
599
|
+
curl -X GET "https://example.com/wp-json/book-reviews/v1/reviews"
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
**2. Get single review (GET)**
|
|
603
|
+
|
|
604
|
+
```bash
|
|
605
|
+
curl -X GET "https://example.com/wp-json/book-reviews/v1/reviews/123"
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**3. Create review (POST)**
|
|
609
|
+
|
|
610
|
+
```bash
|
|
611
|
+
curl -X POST "https://example.com/wp-json/book-reviews/v1/reviews" \
|
|
612
|
+
-H "Content-Type: application/json" \
|
|
613
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
614
|
+
-d '{
|
|
615
|
+
"title": "Great Book!",
|
|
616
|
+
"content": "This book was amazing...",
|
|
617
|
+
"book_title": "The Great Gatsby",
|
|
618
|
+
"book_author": "F. Scott Fitzgerald",
|
|
619
|
+
"rating": 5
|
|
620
|
+
}'
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
**4. Update review (PUT)**
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
curl -X PUT "https://example.com/wp-json/book-reviews/v1/reviews/123" \
|
|
627
|
+
-H "Content-Type: application/json" \
|
|
628
|
+
-H "Authorization: Bearer YOUR_TOKEN" \
|
|
629
|
+
-d '{
|
|
630
|
+
"title": "Updated Title",
|
|
631
|
+
"content": "Updated content...",
|
|
632
|
+
"book_title": "The Great Gatsby",
|
|
633
|
+
"book_author": "F. Scott Fitzgerald",
|
|
634
|
+
"rating": 4
|
|
635
|
+
}'
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
**5. Delete review (DELETE)**
|
|
639
|
+
|
|
640
|
+
```bash
|
|
641
|
+
curl -X DELETE "https://example.com/wp-json/book-reviews/v1/reviews/123" \
|
|
642
|
+
-H "Authorization: Bearer YOUR_TOKEN"
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
### Testing with JavaScript
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
// Fetch all reviews
|
|
649
|
+
fetch('https://example.com/wp-json/book-reviews/v1/reviews')
|
|
650
|
+
.then(response => response.json())
|
|
651
|
+
.then(data => console.log(data));
|
|
652
|
+
|
|
653
|
+
// Create review
|
|
654
|
+
fetch('https://example.com/wp-json/book-reviews/v1/reviews', {
|
|
655
|
+
method: 'POST',
|
|
656
|
+
headers: {
|
|
657
|
+
'Content-Type': 'application/json',
|
|
658
|
+
'X-WP-Nonce': wpApiSettings.nonce
|
|
659
|
+
},
|
|
660
|
+
body: JSON.stringify({
|
|
661
|
+
title: 'Great Book!',
|
|
662
|
+
content: 'This book was amazing...',
|
|
663
|
+
book_title: 'The Great Gatsby',
|
|
664
|
+
book_author: 'F. Scott Fitzgerald',
|
|
665
|
+
rating: 5
|
|
666
|
+
})
|
|
667
|
+
})
|
|
668
|
+
.then(response => response.json())
|
|
669
|
+
.then(data => console.log(data));
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Automated Testing with PHPUnit
|
|
673
|
+
|
|
674
|
+
```php
|
|
675
|
+
<?php
|
|
676
|
+
class Test_Book_Reviews_API extends WP_UnitTestCase {
|
|
677
|
+
|
|
678
|
+
public function test_get_reviews() {
|
|
679
|
+
$request = new WP_REST_Request('GET', '/book-reviews/v1/reviews');
|
|
680
|
+
$response = rest_do_request($request);
|
|
681
|
+
|
|
682
|
+
$this->assertEquals(200, $response->get_status());
|
|
683
|
+
$this->assertIsArray($response->get_data());
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
public function test_create_review_requires_authentication() {
|
|
687
|
+
$request = new WP_REST_Request('POST', '/book-reviews/v1/reviews');
|
|
688
|
+
$request->set_body_params(array(
|
|
689
|
+
'title' => 'Test Review',
|
|
690
|
+
'content' => 'Test content',
|
|
691
|
+
'book_title' => 'Test Book',
|
|
692
|
+
'book_author' => 'Test Author',
|
|
693
|
+
'rating' => 5,
|
|
694
|
+
));
|
|
695
|
+
|
|
696
|
+
$response = rest_do_request($request);
|
|
697
|
+
|
|
698
|
+
$this->assertEquals(401, $response->get_status());
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
public function test_create_review_validates_rating() {
|
|
702
|
+
wp_set_current_user($this->factory->user->create(array('role' => 'editor')));
|
|
703
|
+
|
|
704
|
+
$request = new WP_REST_Request('POST', '/book-reviews/v1/reviews');
|
|
705
|
+
$request->set_body_params(array(
|
|
706
|
+
'title' => 'Test Review',
|
|
707
|
+
'content' => 'Test content',
|
|
708
|
+
'book_title' => 'Test Book',
|
|
709
|
+
'book_author' => 'Test Author',
|
|
710
|
+
'rating' => 10, // Invalid rating
|
|
711
|
+
));
|
|
712
|
+
|
|
713
|
+
$response = rest_do_request($request);
|
|
714
|
+
|
|
715
|
+
$this->assertEquals(400, $response->get_status());
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
## Best Practices Demonstrated
|
|
723
|
+
|
|
724
|
+
✅ **Proper route registration** - Using `register_rest_route()`
|
|
725
|
+
✅ **Permission callbacks** - Authentication and authorization
|
|
726
|
+
✅ **Validation** - Input validation with error messages
|
|
727
|
+
✅ **Sanitization** - Secure data handling
|
|
728
|
+
✅ **Error handling** - WP_Error for failures
|
|
729
|
+
✅ **Pagination** - Collection parameters and headers
|
|
730
|
+
✅ **HTTP methods** - Proper REST verbs (GET, POST, PUT, DELETE)
|
|
731
|
+
✅ **Response formatting** - Consistent JSON responses
|
|
732
|
+
✅ **Nonce verification** - CSRF protection
|
|
733
|
+
✅ **Capability checks** - WordPress permissions integration
|
|
734
|
+
|