# WordPress Best Practices Guide ## Overview This guide documents WordPress best practices as implemented in the HVAC Community Events plugin. Following these practices ensures maintainability, security, and compatibility with WordPress core and other plugins. ## JavaScript and jQuery ### ✅ DO: Use WordPress jQuery Patterns ```javascript // CORRECT - WordPress standard pattern jQuery(document).ready(function($) { 'use strict'; // $ is now available within this scope $('.my-element').addClass('active'); }); ``` ### ❌ DON'T: Create Complex Compatibility Layers ```javascript // WRONG - Unnecessary complexity (function($) { if (typeof $ === 'undefined' && typeof window.jQuery !== 'undefined') { $ = window.jQuery; } // Complex error handling for basic jQuery operations })(jQuery); ``` ### Key Principles 1. WordPress loads jQuery in no-conflict mode by default 2. Use `jQuery` globally, `$` within document ready callbacks 3. Don't create compatibility shims - they add complexity without value 4. Trust WordPress's jQuery implementation ## Security Best Practices ### Always Escape Output ```php // ✅ CORRECT echo esc_html($user_input); echo esc_url($link); echo esc_attr($attribute); echo wp_kses_post($content_with_html); // ❌ WRONG echo $user_input; ``` ### Always Sanitize Input ```php // ✅ CORRECT $clean_text = sanitize_text_field($_POST['field']); $clean_email = sanitize_email($_POST['email']); $clean_url = esc_url_raw($_POST['url']); // ❌ WRONG $data = $_POST['field']; ``` ### Always Verify Nonces ```php // ✅ CORRECT if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'action_name')) { wp_die('Security check failed'); } // ❌ WRONG - No nonce verification if (isset($_POST['submit'])) { // Process form } ``` ### Always Check Capabilities ```php // ✅ CORRECT if (!current_user_can('edit_posts')) { wp_die('Insufficient permissions'); } // Check custom roles properly $user = wp_get_current_user(); if (!in_array('hvac_trainer', $user->roles)) { wp_die('Access denied'); } // ❌ WRONG if (!current_user_can('hvac_trainer')) { // Custom roles aren't capabilities! wp_die('Access denied'); } ``` ## Plugin Development Standards ### File Organization ``` hvac-community-events/ ├── hvac-community-events.php # Main plugin file ├── includes/ # PHP classes and functions │ ├── class-*.php # One class per file │ └── functions.php # Helper functions ├── assets/ # Frontend resources │ ├── css/ # Stylesheets │ ├── js/ # JavaScript files │ └── images/ # Images ├── templates/ # Page templates └── docs/ # Documentation ``` ### Class Structure ```php /** * HVAC Example Class * * @package HVAC_Community_Events * @since 1.0.0 */ class HVAC_Example { /** * Instance * @var HVAC_Example */ private static $instance = null; /** * Get instance (Singleton pattern) */ public static function instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } /** * Constructor */ private function __construct() { $this->init_hooks(); } /** * Initialize hooks */ private function init_hooks() { add_action('init', array($this, 'init')); } } ``` ### Naming Conventions - **PHP Classes**: `HVAC_Class_Name` (prefix + PascalCase) - **Functions**: `hvac_function_name` (prefix + snake_case) - **Hooks**: `hvac_hook_name` (prefix + snake_case) - **Constants**: `HVAC_CONSTANT_NAME` (prefix + UPPER_SNAKE_CASE) - **Database**: `hvac_table_name` (prefix + snake_case) ## Asset Management ### Conditional Loading ```php /** * Only load assets on plugin pages */ public function enqueue_scripts() { // Check if we're on a plugin page if (!$this->is_plugin_page()) { return; } wp_enqueue_script( 'hvac-dashboard', HVAC_PLUGIN_URL . 'assets/js/hvac-dashboard.js', array('jquery'), HVAC_PLUGIN_VERSION, true ); // Localize script for AJAX wp_localize_script('hvac-dashboard', 'hvac_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('hvac_ajax_nonce') )); } ``` ### CSS Organization ```css /* Use consistent naming and organization */ .hvac-wrapper { max-width: 1200px; margin: 0 auto; padding: 20px; } /* Component-based styling */ .hvac-card { background: #fff; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* State modifiers */ .hvac-card--active { border-color: var(--hvac-primary); } ``` ## Database Operations ### Use WordPress Database API ```php global $wpdb; // ✅ CORRECT - Using prepared statements $results = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}posts WHERE post_author = %d", $user_id ) ); // ❌ WRONG - Direct query without preparation $results = $wpdb->get_results( "SELECT * FROM wp_posts WHERE post_author = " . $user_id ); ``` ### Cache Expensive Queries ```php $cache_key = 'hvac_trainer_events_' . $user_id; $events = wp_cache_get($cache_key); if (false === $events) { // Expensive query $events = $this->get_trainer_events($user_id); // Cache for 1 hour wp_cache_set($cache_key, $events, '', HOUR_IN_SECONDS); } ``` ## AJAX Best Practices ### Proper AJAX Handler ```php /** * AJAX handler with security checks */ public function ajax_handler() { // Verify nonce if (!check_ajax_referer('hvac_ajax_nonce', 'nonce', false)) { wp_send_json_error('Security check failed'); } // Check capabilities if (!current_user_can('edit_posts')) { wp_send_json_error('Insufficient permissions'); } // Sanitize input $data = sanitize_text_field($_POST['data']); // Process request $result = $this->process_data($data); // Return JSON response if ($result) { wp_send_json_success($result); } else { wp_send_json_error('Processing failed'); } } ``` ## Template Development ### WordPress Template Requirements ```php
risky_operation(); } catch (Exception $e) { // Log error for debugging error_log('HVAC Plugin Error: ' . $e->getMessage()); // Show user-friendly message if (WP_DEBUG) { wp_die('Error: ' . esc_html($e->getMessage())); } else { wp_die('An error occurred. Please try again later.'); } } ``` ## Performance Optimization ### Optimize Queries ```php // ✅ CORRECT - Single query with JOIN $results = $wpdb->get_results(" SELECT p.*, pm.meta_value as venue_id FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_type = 'tribe_events' AND pm.meta_key = '_EventVenueID' "); // ❌ WRONG - Multiple queries in loop foreach ($events as $event) { $venue = get_post_meta($event->ID, '_EventVenueID', true); // Process... } ``` ### Lazy Loading ```php // Only load heavy resources when needed public function maybe_load_map_data() { if (!is_page('find-trainer')) { return; } // Load map data only on find-trainer page $this->load_trainer_locations(); } ``` ## Testing with Display Session ### Using Headless Testing ```bash # Set display environment for headless testing export DISPLAY=:0 export XAUTHORITY=/run/user/1000/.mutter-Xwaylandauth.90WDB3 # Run Playwright tests node test-trainer-features.js ``` ### Testing Best Practices 1. Always test on staging before production 2. Use actual user accounts for realistic testing 3. Verify both UI and functionality 4. Check browser console for errors 5. Test with different user roles ## Deployment Process ### Pre-deployment Checklist ```bash # 1. Run validation bin/pre-deployment-check.sh # 2. Test on staging scripts/deploy.sh staging # 3. Verify staging node test-all-features.js # 4. Deploy to production (only when requested) scripts/deploy.sh production ``` ### Post-deployment Verification ```bash # Clear caches wp cache flush wp rewrite flush # Verify pages exist wp post list --post_type=page --name=dashboard # Check plugin activation wp plugin list --name=hvac-community-events ``` ## Common Pitfalls to Avoid ### ❌ DON'T - Create standalone fixes outside plugin deployment - Use hardcoded database prefixes - Skip nonce verification - Echo unsanitized output - Assume jQuery methods exist without proper setup - Check capabilities using role names - Load assets globally - Use PHP namespaces (WordPress compatibility) ### ✅ DO - Deploy complete plugin updates - Use `$wpdb->prefix` - Always verify nonces - Escape all output - Use WordPress jQuery patterns - Check roles properly with `in_array()` - Load assets conditionally - Use prefixed class/function names ## Debugging Tips ### Enable Debug Mode (Development Only) ```php // wp-config.php define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); define('SCRIPT_DEBUG', true); ``` ### Debug Logging ```php // Log to debug.log error_log('HVAC Debug: ' . print_r($data, true)); // Conditional logging if (defined('WP_DEBUG') && WP_DEBUG) { error_log('HVAC: Processing user ' . $user_id); } ``` ## Resources - [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/) - [Plugin Security Best Practices](https://developer.wordpress.org/plugins/security/) - [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/) - [Theme Integration Guide](https://developer.wordpress.org/themes/basics/)