@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.
Files changed (79) hide show
  1. package/augment-extensions/domain-rules/wordpress/README.md +163 -0
  2. package/augment-extensions/domain-rules/wordpress/module.json +32 -0
  3. package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
  4. package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
  5. package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
  6. package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
  7. package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
  8. package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
  9. package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
  10. package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
  11. package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
  12. package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
  13. package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
  14. package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
  15. package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
  16. package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
  17. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
  18. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
  19. package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
  20. package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
  21. package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
  22. package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
  23. package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
  24. package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
  25. package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
  26. package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
  27. package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
  28. package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
  29. package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
  30. package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
  31. package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
  32. package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
  33. package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
  34. package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
  35. package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
  36. package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
  37. package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
  38. package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
  39. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
  40. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
  41. package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
  42. package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
  43. package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
  44. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
  45. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
  46. package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
  47. package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
  48. package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
  49. package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
  50. package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
  51. package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
  52. package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
  53. package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
  54. package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
  55. package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
  56. package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
  57. package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
  58. package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
  59. package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
  60. package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
  61. package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
  62. package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
  63. package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
  64. package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
  65. package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
  66. package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
  67. package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
  68. package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
  69. package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
  70. package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
  71. package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
  72. package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
  73. package/cli/dist/cli.js +5 -1
  74. package/cli/dist/cli.js.map +1 -1
  75. package/cli/dist/commands/show.d.ts.map +1 -1
  76. package/cli/dist/commands/show.js +41 -0
  77. package/cli/dist/commands/show.js.map +1 -1
  78. package/modules.md +52 -0
  79. package/package.json +1 -1
@@ -0,0 +1,629 @@
1
+ # AJAX Handlers
2
+
3
+ ## Overview
4
+
5
+ This guide covers WordPress AJAX implementation for plugins including script enqueuing, localization, nonce verification, capability checks, and JSON responses.
6
+
7
+ ---
8
+
9
+ ## Basic AJAX Setup
10
+
11
+ ### Enqueue Script with AJAX Data
12
+
13
+ ```php
14
+ <?php
15
+ /**
16
+ * Enqueue AJAX script
17
+ */
18
+ function my_plugin_enqueue_ajax_script() {
19
+ wp_enqueue_script(
20
+ 'my-plugin-ajax',
21
+ plugins_url( 'js/ajax-handler.js', __FILE__ ),
22
+ array( 'jquery' ),
23
+ '1.0.0',
24
+ true
25
+ );
26
+
27
+ // Localize script with AJAX URL and nonce
28
+ wp_localize_script( 'my-plugin-ajax', 'myPluginAjax', array(
29
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
30
+ 'nonce' => wp_create_nonce( 'my_plugin_ajax_nonce' ),
31
+ ) );
32
+ }
33
+ add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue_ajax_script' );
34
+ ```
35
+
36
+ ### JavaScript AJAX Request
37
+
38
+ **js/ajax-handler.js:**
39
+ ```javascript
40
+ jQuery(document).ready(function($) {
41
+ $('#my-button').on('click', function(e) {
42
+ e.preventDefault();
43
+
44
+ var data = {
45
+ action: 'my_plugin_ajax_action',
46
+ security: myPluginAjax.nonce,
47
+ item_id: $(this).data('item-id'),
48
+ value: $('#my-input').val()
49
+ };
50
+
51
+ $.ajax({
52
+ url: myPluginAjax.ajaxurl,
53
+ type: 'POST',
54
+ data: data,
55
+ beforeSend: function() {
56
+ $('#my-button').prop('disabled', true).text('Loading...');
57
+ },
58
+ success: function(response) {
59
+ if (response.success) {
60
+ console.log('Success:', response.data);
61
+ alert(response.data.message);
62
+ } else {
63
+ console.error('Error:', response.data);
64
+ alert('Error: ' + response.data.message);
65
+ }
66
+ },
67
+ error: function(xhr, status, error) {
68
+ console.error('AJAX Error:', error);
69
+ alert('An error occurred. Please try again.');
70
+ },
71
+ complete: function() {
72
+ $('#my-button').prop('disabled', false).text('Submit');
73
+ }
74
+ });
75
+ });
76
+ });
77
+ ```
78
+
79
+ ### PHP AJAX Handler
80
+
81
+ ```php
82
+ <?php
83
+ /**
84
+ * AJAX handler for logged-in users
85
+ */
86
+ function my_plugin_ajax_handler() {
87
+ // Verify nonce
88
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
89
+
90
+ // Check user capability
91
+ if ( ! current_user_can( 'edit_posts' ) ) {
92
+ wp_send_json_error( array(
93
+ 'message' => __( 'You do not have permission to perform this action.', 'my-plugin' ),
94
+ ) );
95
+ }
96
+
97
+ // Sanitize input
98
+ $item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
99
+ $value = isset( $_POST['value'] ) ? sanitize_text_field( $_POST['value'] ) : '';
100
+
101
+ // Validate input
102
+ if ( ! $item_id || empty( $value ) ) {
103
+ wp_send_json_error( array(
104
+ 'message' => __( 'Invalid input data.', 'my-plugin' ),
105
+ ) );
106
+ }
107
+
108
+ // Process request
109
+ $result = update_post_meta( $item_id, '_my_custom_field', $value );
110
+
111
+ if ( $result ) {
112
+ wp_send_json_success( array(
113
+ 'message' => __( 'Data saved successfully.', 'my-plugin' ),
114
+ 'item_id' => $item_id,
115
+ 'value' => $value,
116
+ ) );
117
+ } else {
118
+ wp_send_json_error( array(
119
+ 'message' => __( 'Failed to save data.', 'my-plugin' ),
120
+ ) );
121
+ }
122
+ }
123
+ add_action( 'wp_ajax_my_plugin_ajax_action', 'my_plugin_ajax_handler' );
124
+ ```
125
+
126
+ ---
127
+
128
+ ## AJAX for Logged-In and Non-Logged-In Users
129
+
130
+ ### Handler for Both User Types
131
+
132
+ ```php
133
+ <?php
134
+ /**
135
+ * AJAX handler for logged-in users
136
+ */
137
+ function my_plugin_public_ajax_handler() {
138
+ // Verify nonce
139
+ check_ajax_referer( 'my_plugin_public_nonce', 'security' );
140
+
141
+ // Sanitize input
142
+ $search_term = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
143
+
144
+ // Query posts
145
+ $args = array(
146
+ 'post_type' => 'post',
147
+ 'posts_per_page' => 10,
148
+ 's' => $search_term,
149
+ );
150
+
151
+ $query = new WP_Query( $args );
152
+
153
+ $results = array();
154
+ if ( $query->have_posts() ) {
155
+ while ( $query->have_posts() ) {
156
+ $query->the_post();
157
+ $results[] = array(
158
+ 'id' => get_the_ID(),
159
+ 'title' => get_the_title(),
160
+ 'url' => get_permalink(),
161
+ );
162
+ }
163
+ wp_reset_postdata();
164
+ }
165
+
166
+ wp_send_json_success( array(
167
+ 'results' => $results,
168
+ 'count' => count( $results ),
169
+ ) );
170
+ }
171
+ // For logged-in users
172
+ add_action( 'wp_ajax_my_plugin_public_action', 'my_plugin_public_ajax_handler' );
173
+ // For non-logged-in users
174
+ add_action( 'wp_ajax_nopriv_my_plugin_public_action', 'my_plugin_public_ajax_handler' );
175
+
176
+ ---
177
+
178
+ ## Advanced AJAX Patterns
179
+
180
+ ### File Upload via AJAX
181
+
182
+ **JavaScript:**
183
+ ```javascript
184
+ jQuery(document).ready(function($) {
185
+ $('#upload-form').on('submit', function(e) {
186
+ e.preventDefault();
187
+
188
+ var formData = new FormData();
189
+ formData.append('action', 'my_plugin_upload_file');
190
+ formData.append('security', myPluginAjax.nonce);
191
+ formData.append('file', $('#file-input')[0].files[0]);
192
+
193
+ $.ajax({
194
+ url: myPluginAjax.ajaxurl,
195
+ type: 'POST',
196
+ data: formData,
197
+ processData: false,
198
+ contentType: false,
199
+ success: function(response) {
200
+ if (response.success) {
201
+ console.log('File uploaded:', response.data.file_url);
202
+ }
203
+ }
204
+ });
205
+ });
206
+ });
207
+ ```
208
+
209
+ **PHP:**
210
+ ```php
211
+ <?php
212
+ /**
213
+ * Handle file upload via AJAX
214
+ */
215
+ function my_plugin_upload_file_handler() {
216
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
217
+
218
+ if ( ! current_user_can( 'upload_files' ) ) {
219
+ wp_send_json_error( array(
220
+ 'message' => __( 'You do not have permission to upload files.', 'my-plugin' ),
221
+ ) );
222
+ }
223
+
224
+ if ( empty( $_FILES['file'] ) ) {
225
+ wp_send_json_error( array(
226
+ 'message' => __( 'No file uploaded.', 'my-plugin' ),
227
+ ) );
228
+ }
229
+
230
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
231
+
232
+ $uploaded_file = wp_handle_upload( $_FILES['file'], array( 'test_form' => false ) );
233
+
234
+ if ( isset( $uploaded_file['error'] ) ) {
235
+ wp_send_json_error( array(
236
+ 'message' => $uploaded_file['error'],
237
+ ) );
238
+ }
239
+
240
+ wp_send_json_success( array(
241
+ 'message' => __( 'File uploaded successfully.', 'my-plugin' ),
242
+ 'file_url' => $uploaded_file['url'],
243
+ 'file_path' => $uploaded_file['file'],
244
+ ) );
245
+ }
246
+ add_action( 'wp_ajax_my_plugin_upload_file', 'my_plugin_upload_file_handler' );
247
+ ```
248
+
249
+ ### Long-Running Process with Progress Updates
250
+
251
+ **JavaScript:**
252
+ ```javascript
253
+ function processLongRunningTask() {
254
+ var totalItems = 100;
255
+ var batchSize = 10;
256
+ var processed = 0;
257
+
258
+ function processBatch() {
259
+ $.ajax({
260
+ url: myPluginAjax.ajaxurl,
261
+ type: 'POST',
262
+ data: {
263
+ action: 'my_plugin_process_batch',
264
+ security: myPluginAjax.nonce,
265
+ offset: processed,
266
+ limit: batchSize
267
+ },
268
+ success: function(response) {
269
+ if (response.success) {
270
+ processed += response.data.processed;
271
+ var progress = Math.round((processed / totalItems) * 100);
272
+
273
+ $('#progress-bar').css('width', progress + '%');
274
+ $('#progress-text').text(progress + '%');
275
+
276
+ if (processed < totalItems) {
277
+ processBatch(); // Process next batch
278
+ } else {
279
+ alert('Processing complete!');
280
+ }
281
+ }
282
+ }
283
+ });
284
+ }
285
+
286
+ processBatch();
287
+ }
288
+ ```
289
+
290
+ **PHP:**
291
+ ```php
292
+ <?php
293
+ /**
294
+ * Process batch of items
295
+ */
296
+ function my_plugin_process_batch_handler() {
297
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
298
+
299
+ if ( ! current_user_can( 'manage_options' ) ) {
300
+ wp_send_json_error( array(
301
+ 'message' => __( 'Insufficient permissions.', 'my-plugin' ),
302
+ ) );
303
+ }
304
+
305
+ $offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 0;
306
+ $limit = isset( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 10;
307
+
308
+ // Get items to process
309
+ $items = get_posts( array(
310
+ 'post_type' => 'my_custom_post_type',
311
+ 'posts_per_page' => $limit,
312
+ 'offset' => $offset,
313
+ ) );
314
+
315
+ $processed = 0;
316
+ foreach ( $items as $item ) {
317
+ // Process each item
318
+ update_post_meta( $item->ID, '_processed', true );
319
+ $processed++;
320
+ }
321
+
322
+ wp_send_json_success( array(
323
+ 'processed' => $processed,
324
+ 'offset' => $offset + $processed,
325
+ ) );
326
+ }
327
+ add_action( 'wp_ajax_my_plugin_process_batch', 'my_plugin_process_batch_handler' );
328
+ ```
329
+
330
+ ### Heartbeat API Integration
331
+
332
+ ```php
333
+ <?php
334
+ /**
335
+ * Modify Heartbeat API data
336
+ */
337
+ function my_plugin_heartbeat_received( $response, $data ) {
338
+ if ( isset( $data['my_plugin_check'] ) ) {
339
+ // Get real-time data
340
+ $response['my_plugin_data'] = array(
341
+ 'notifications' => get_user_meta( get_current_user_id(), 'unread_notifications', true ),
342
+ 'timestamp' => current_time( 'timestamp' ),
343
+ );
344
+ }
345
+
346
+ return $response;
347
+ }
348
+ add_filter( 'heartbeat_received', 'my_plugin_heartbeat_received', 10, 2 );
349
+ ```
350
+
351
+ **JavaScript:**
352
+ ```javascript
353
+ jQuery(document).on('heartbeat-send', function(event, data) {
354
+ data.my_plugin_check = true;
355
+ });
356
+
357
+ jQuery(document).on('heartbeat-tick', function(event, data) {
358
+ if (data.my_plugin_data) {
359
+ console.log('Notifications:', data.my_plugin_data.notifications);
360
+ $('#notification-count').text(data.my_plugin_data.notifications);
361
+ }
362
+ });
363
+ ```
364
+
365
+ ---
366
+
367
+ ## Error Handling
368
+
369
+ ### Comprehensive Error Handling
370
+
371
+ **JavaScript:**
372
+ ```javascript
373
+ function handleAjaxRequest(data) {
374
+ $.ajax({
375
+ url: myPluginAjax.ajaxurl,
376
+ type: 'POST',
377
+ data: data,
378
+ timeout: 30000, // 30 seconds
379
+ success: function(response) {
380
+ if (response.success) {
381
+ // Handle success
382
+ console.log('Success:', response.data);
383
+ } else {
384
+ // Handle error from server
385
+ console.error('Server error:', response.data);
386
+ showErrorMessage(response.data.message || 'An error occurred');
387
+ }
388
+ },
389
+ error: function(xhr, status, error) {
390
+ // Handle AJAX error
391
+ if (status === 'timeout') {
392
+ showErrorMessage('Request timed out. Please try again.');
393
+ } else if (status === 'abort') {
394
+ showErrorMessage('Request was aborted.');
395
+ } else {
396
+ showErrorMessage('Network error: ' + error);
397
+ }
398
+ }
399
+ });
400
+ }
401
+
402
+ function showErrorMessage(message) {
403
+ $('#error-container').html('<div class="error">' + message + '</div>').show();
404
+ }
405
+ ```
406
+
407
+ **PHP:**
408
+ ```php
409
+ <?php
410
+ /**
411
+ * AJAX handler with comprehensive error handling
412
+ */
413
+ function my_plugin_safe_ajax_handler() {
414
+ try {
415
+ // Verify nonce
416
+ if ( ! check_ajax_referer( 'my_plugin_ajax_nonce', 'security', false ) ) {
417
+ throw new Exception( __( 'Security check failed.', 'my-plugin' ) );
418
+ }
419
+
420
+ // Check capability
421
+ if ( ! current_user_can( 'edit_posts' ) ) {
422
+ throw new Exception( __( 'Insufficient permissions.', 'my-plugin' ) );
423
+ }
424
+
425
+ // Validate input
426
+ $item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
427
+ if ( ! $item_id ) {
428
+ throw new Exception( __( 'Invalid item ID.', 'my-plugin' ) );
429
+ }
430
+
431
+ // Process request
432
+ $result = my_plugin_process_item( $item_id );
433
+
434
+ if ( is_wp_error( $result ) ) {
435
+ throw new Exception( $result->get_error_message() );
436
+ }
437
+
438
+ wp_send_json_success( array(
439
+ 'message' => __( 'Item processed successfully.', 'my-plugin' ),
440
+ 'data' => $result,
441
+ ) );
442
+
443
+ } catch ( Exception $e ) {
444
+ wp_send_json_error( array(
445
+ 'message' => $e->getMessage(),
446
+ 'code' => $e->getCode(),
447
+ ) );
448
+ }
449
+ }
450
+ add_action( 'wp_ajax_my_plugin_safe_action', 'my_plugin_safe_ajax_handler' );
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Best Practices
456
+
457
+ ### Security
458
+
459
+ 1. **Always verify nonces**: Use `check_ajax_referer()` in every AJAX handler
460
+ 2. **Check capabilities**: Verify user has permission to perform the action
461
+ 3. **Sanitize input**: Use appropriate sanitization functions for all input
462
+ 4. **Escape output**: Use `esc_html()`, `esc_url()`, etc. when outputting data
463
+ 5. **Validate data**: Check that required data is present and valid
464
+
465
+ ### Performance
466
+
467
+ 1. **Limit query results**: Don't return large datasets in AJAX responses
468
+ 2. **Use caching**: Cache expensive queries with transients
469
+ 3. **Batch processing**: Break long-running tasks into smaller batches
470
+ 4. **Debounce requests**: Prevent rapid-fire AJAX requests
471
+ 5. **Set timeouts**: Configure appropriate timeout values
472
+
473
+ ### User Experience
474
+
475
+ 1. **Show loading states**: Disable buttons and show loading indicators
476
+ 2. **Provide feedback**: Show success/error messages to users
477
+ 3. **Handle errors gracefully**: Display user-friendly error messages
478
+ 4. **Prevent duplicate requests**: Disable submit buttons during processing
479
+ 5. **Use progress indicators**: For long-running operations
480
+
481
+ ### Code Organization
482
+
483
+ 1. **Separate concerns**: Keep JavaScript and PHP handlers focused
484
+ 2. **Use action names consistently**: Follow naming convention `{plugin}_{action}`
485
+ 3. **Group related handlers**: Organize AJAX handlers in dedicated files
486
+ 4. **Document handlers**: Add PHPDoc blocks explaining what each handler does
487
+ 5. **Use classes**: For complex plugins, use OOP to organize AJAX handlers
488
+
489
+ ---
490
+
491
+ ## Common Pitfalls
492
+
493
+ ### ❌ DON'T
494
+
495
+ ```php
496
+ // Don't skip nonce verification
497
+ function bad_ajax_handler() {
498
+ // No nonce check - BAD!
499
+ $data = $_POST['data'];
500
+ update_option( 'my_option', $data );
501
+ wp_send_json_success();
502
+ }
503
+
504
+ // Don't skip capability checks
505
+ function bad_ajax_handler2() {
506
+ check_ajax_referer( 'my_nonce', 'security' );
507
+ // No capability check - BAD!
508
+ wp_delete_post( $_POST['post_id'], true );
509
+ wp_send_json_success();
510
+ }
511
+
512
+ // Don't forget to sanitize
513
+ function bad_ajax_handler3() {
514
+ check_ajax_referer( 'my_nonce', 'security' );
515
+ $value = $_POST['value']; // Not sanitized - BAD!
516
+ update_post_meta( $post_id, 'key', $value );
517
+ wp_send_json_success();
518
+ }
519
+
520
+ // Don't echo and then use wp_send_json
521
+ function bad_ajax_handler4() {
522
+ echo 'Processing...'; // BAD!
523
+ wp_send_json_success( array( 'message' => 'Done' ) );
524
+ }
525
+ ```
526
+
527
+ ### ✅ DO
528
+
529
+ ```php
530
+ // Always verify nonce
531
+ function good_ajax_handler() {
532
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
533
+
534
+ // Check capability
535
+ if ( ! current_user_can( 'edit_posts' ) ) {
536
+ wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
537
+ }
538
+
539
+ // Sanitize input
540
+ $data = sanitize_text_field( $_POST['data'] );
541
+
542
+ // Process and respond
543
+ update_option( 'my_option', $data );
544
+ wp_send_json_success( array( 'message' => 'Saved successfully' ) );
545
+ }
546
+ add_action( 'wp_ajax_my_plugin_action', 'good_ajax_handler' );
547
+ ```
548
+
549
+ ---
550
+
551
+ ## Complete Example
552
+
553
+ ### Plugin File Structure
554
+
555
+ ```
556
+ my-plugin/
557
+ ├── my-plugin.php
558
+ ├── js/
559
+ │ └── ajax-handler.js
560
+ └── includes/
561
+ └── class-ajax-handler.php
562
+ ```
563
+
564
+ ### Class-Based AJAX Handler
565
+
566
+ **includes/class-ajax-handler.php:**
567
+ ```php
568
+ <?php
569
+ class My_Plugin_Ajax_Handler {
570
+
571
+ public function __construct() {
572
+ add_action( 'wp_ajax_my_plugin_save_data', array( $this, 'save_data' ) );
573
+ add_action( 'wp_ajax_my_plugin_load_data', array( $this, 'load_data' ) );
574
+ }
575
+
576
+ public function save_data() {
577
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
578
+
579
+ if ( ! current_user_can( 'edit_posts' ) ) {
580
+ wp_send_json_error( array(
581
+ 'message' => __( 'Insufficient permissions.', 'my-plugin' ),
582
+ ) );
583
+ }
584
+
585
+ $item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
586
+ $value = isset( $_POST['value'] ) ? sanitize_text_field( $_POST['value'] ) : '';
587
+
588
+ if ( ! $item_id || empty( $value ) ) {
589
+ wp_send_json_error( array(
590
+ 'message' => __( 'Invalid data.', 'my-plugin' ),
591
+ ) );
592
+ }
593
+
594
+ $result = update_post_meta( $item_id, '_my_custom_field', $value );
595
+
596
+ if ( $result ) {
597
+ wp_send_json_success( array(
598
+ 'message' => __( 'Data saved.', 'my-plugin' ),
599
+ 'item_id' => $item_id,
600
+ ) );
601
+ } else {
602
+ wp_send_json_error( array(
603
+ 'message' => __( 'Failed to save.', 'my-plugin' ),
604
+ ) );
605
+ }
606
+ }
607
+
608
+ public function load_data() {
609
+ check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
610
+
611
+ $item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
612
+
613
+ if ( ! $item_id ) {
614
+ wp_send_json_error( array(
615
+ 'message' => __( 'Invalid item ID.', 'my-plugin' ),
616
+ ) );
617
+ }
618
+
619
+ $value = get_post_meta( $item_id, '_my_custom_field', true );
620
+
621
+ wp_send_json_success( array(
622
+ 'value' => $value,
623
+ ) );
624
+ }
625
+ }
626
+
627
+ new My_Plugin_Ajax_Handler();
628
+ ```
629
+