Skip to content

WordPress Integration

The Elasticsearch integration seamlessly integrates with WordPress through custom page templates, AJAX handlers, and the WordPress hook system. This ensures compatibility with WordPress standards while providing powerful search and monitoring capabilities.

The integration includes two custom WordPress page templates:

File: page-search.php
Template Name: “Search Page”
Purpose: Public-facing search interface

<?php
/*
Template Name: Search Page
*/
get_header(); ?>
<div class="search-container">
<div class="search-header">
<h1>Product Search</h1>
<form id="search-form" class="search-form">
<input type="text" id="search-query" name="q" placeholder="Search products...">
<button type="submit">Search</button>
</form>
</div>
<div class="search-filters">
<?php get_template_part('template-parts/category-selector'); ?>
<div class="price-filter">
<label>Price Range:</label>
<input type="number" id="min-price" placeholder="Min">
<input type="number" id="max-price" placeholder="Max">
</div>
</div>
<div id="search-results" class="search-results">
<!-- Results will be loaded via AJAX -->
</div>
<div id="search-pagination" class="pagination">
<!-- Pagination will be loaded via AJAX -->
</div>
</div>
<script>
// Search functionality
document.getElementById('search-form').addEventListener('submit', function(e) {
e.preventDefault();
performSearch();
});
function performSearch() {
const query = document.getElementById('search-query').value;
const category = document.getElementById('category-select').value;
const minPrice = document.getElementById('min-price').value;
const maxPrice = document.getElementById('max-price').value;
// AJAX search request
jQuery.post(ajaxurl, {
action: 'search_products',
nonce: ajax_nonce,
query: query,
category: category,
min_price: minPrice,
max_price: maxPrice,
page: 1
}, function(response) {
if (response.success) {
displaySearchResults(response.data);
} else {
displayError(response.data);
}
});
}
</script>
<?php get_footer(); ?>

File: page-elasticsearch-console.php
Template Name: “Elasticsearch Console”
Purpose: Admin monitoring dashboard

<?php
/*
Template Name: Elasticsearch Console
*/
// Check admin permissions
if (!current_user_can('manage_options')) {
wp_die('Insufficient permissions');
}
get_header(); ?>
<div class="elasticsearch-console">
<div class="console-header">
<h1>Elasticsearch Console</h1>
<div class="console-actions">
<button id="refresh-stats" class="btn btn-primary">Refresh</button>
<button id="test-connection" class="btn btn-secondary">Test Connection</button>
<button id="reset-sync" class="btn btn-danger">Reset Sync</button>
</div>
</div>
<div class="console-stats">
<div class="stat-card">
<h3>Sync Status</h3>
<div id="sync-status" class="stat-value">Loading...</div>
</div>
<div class="stat-card">
<h3>Progress</h3>
<div id="sync-progress" class="stat-value">Loading...</div>
</div>
<div class="stat-card">
<h3>Products Indexed</h3>
<div id="indexed-count" class="stat-value">Loading...</div>
</div>
<div class="stat-card">
<h3>ETA</h3>
<div id="sync-eta" class="stat-value">Loading...</div>
</div>
</div>
<div class="console-logs">
<h3>Recent Logs</h3>
<div id="log-content" class="log-content">
<!-- Logs will be loaded via AJAX -->
</div>
</div>
</div>
<script>
// Console functionality
jQuery(document).ready(function($) {
loadConsoleStats();
// Auto-refresh every 30 seconds
setInterval(loadConsoleStats, 30000);
$('#refresh-stats').click(loadConsoleStats);
$('#test-connection').click(testConnection);
$('#reset-sync').click(resetSync);
});
function loadConsoleStats() {
jQuery.post(ajaxurl, {
action: 'get_elasticsearch_stats',
nonce: ajax_nonce
}, function(response) {
if (response.success) {
updateConsoleDisplay(response.data);
}
});
}
</script>
<?php get_footer(); ?>

File: functions.php

// AJAX handlers for Elasticsearch operations
add_action('wp_ajax_get_elasticsearch_stats', 'ajax_get_elasticsearch_stats');
add_action('wp_ajax_get_elasticsearch_log', 'ajax_get_elasticsearch_log');
add_action('wp_ajax_test_elasticsearch_connection', 'ajax_test_elasticsearch_connection');
add_action('wp_ajax_reset_elasticsearch_sync', 'ajax_reset_elasticsearch_sync');
add_action('wp_ajax_run_manual_elasticsearch_sync', 'ajax_run_manual_elasticsearch_sync');
add_action('wp_ajax_search_products', 'ajax_search_products');
// Public AJAX endpoints (for search page)
add_action('wp_ajax_nopriv_search_products', 'ajax_search_products');
add_action('wp_ajax_nopriv_load_category_children', 'ajax_load_category_children');
add_action('wp_ajax_nopriv_search_categories', 'ajax_search_categories');
function ajax_get_elasticsearch_stats() {
// Verify nonce
check_ajax_referer('elasticsearch_nonce', 'nonce');
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
try {
$console_manager = new ElasticsearchConsoleManager();
$stats = $console_manager->getStats();
$performance = $console_manager->getPerformanceEstimates();
wp_send_json_success([
'stats' => $stats,
'performance' => $performance
]);
} catch (Exception $e) {
wp_send_json_error('Failed to get statistics: ' . $e->getMessage());
}
}
function ajax_search_products() {
// Verify nonce
check_ajax_referer('search_nonce', 'nonce');
try {
$query = sanitize_text_field($_POST['query'] ?? '');
$category = sanitize_text_field($_POST['category'] ?? '');
$min_price = floatval($_POST['min_price'] ?? 0);
$max_price = floatval($_POST['max_price'] ?? 0);
$brand = sanitize_text_field($_POST['brand'] ?? '');
$page = intval($_POST['page'] ?? 1);
$per_page = intval($_POST['per_page'] ?? 20);
$filters = [];
if (!empty($category)) $filters['category'] = $category;
if ($min_price > 0) $filters['min_price'] = $min_price;
if ($max_price > 0) $filters['max_price'] = $max_price;
if (!empty($brand)) $filters['brand'] = $brand;
$elasticsearch = new ProductElasticsearch();
$results = $elasticsearch->searchProducts($query, $filters, $page, $per_page);
wp_send_json_success($results);
} catch (Exception $e) {
wp_send_json_error('Search failed: ' . $e->getMessage());
}
}
function ajax_get_elasticsearch_log() {
// Verify nonce
check_ajax_referer('elasticsearch_nonce', 'nonce');
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
try {
$log_type = sanitize_text_field($_POST['log_type'] ?? 'auto_sync');
$lines = intval($_POST['lines'] ?? 100);
$console_manager = new ElasticsearchConsoleManager();
$log_content = $console_manager->getLogContent($log_type, $lines);
wp_send_json_success($log_content);
} catch (Exception $e) {
wp_send_json_error('Failed to get log content: ' . $e->getMessage());
}
}
function ajax_test_elasticsearch_connection() {
// Verify nonce
check_ajax_referer('elasticsearch_nonce', 'nonce');
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
try {
$elasticsearch = new ProductElasticsearch();
$is_available = $elasticsearch->isAvailable();
if ($is_available) {
$document_count = $elasticsearch->getDocumentCount();
wp_send_json_success([
'status' => 'connected',
'document_count' => $document_count,
'message' => 'Elasticsearch connection successful'
]);
} else {
wp_send_json_error('Elasticsearch connection failed');
}
} catch (Exception $e) {
wp_send_json_error('Connection test failed: ' . $e->getMessage());
}
}
function ajax_reset_elasticsearch_sync() {
// Verify nonce
check_ajax_referer('elasticsearch_nonce', 'nonce');
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Insufficient permissions');
}
try {
$console_manager = new ElasticsearchConsoleManager();
$result = $console_manager->resetSync();
wp_send_json_success($result);
} catch (Exception $e) {
wp_send_json_error('Reset failed: ' . $e->getMessage());
}
}
// Add admin menu item
add_action('admin_menu', 'elasticsearch_admin_menu');
function elasticsearch_admin_menu() {
add_management_page(
'Elasticsearch Sync', // Page title
'Elasticsearch Sync', // Menu title
'manage_options', // Capability
'elasticsearch-sync', // Menu slug
'elasticsearch_sync_page' // Callback
);
}
function elasticsearch_sync_page() {
// Admin page content
echo '<div class="wrap">';
echo '<h1>Elasticsearch Sync</h1>';
echo '<p>Manage Elasticsearch synchronization from here.</p>';
echo '</div>';
}
// Include Elasticsearch classes
add_action('after_setup_theme', 'load_elasticsearch_classes');
function load_elasticsearch_classes() {
require_once get_template_directory() . '/inc/elasticsearch.php';
require_once get_template_directory() . '/inc/elasticsearch-console-manager.php';
}
// Enqueue scripts and styles
add_action('wp_enqueue_scripts', 'enqueue_elasticsearch_assets');
function enqueue_elasticsearch_assets() {
// Only enqueue on search and console pages
if (is_page_template('page-search.php') || is_page_template('page-elasticsearch-console.php')) {
wp_enqueue_script('jquery');
// Localize script with AJAX URL and nonce
wp_localize_script('jquery', 'ajax_object', [
'ajax_url' => admin_url('admin-ajax.php'),
'ajax_nonce' => wp_create_nonce('elasticsearch_nonce'),
'search_nonce' => wp_create_nonce('search_nonce')
]);
// Enqueue custom CSS
wp_enqueue_style('elasticsearch-styles', get_template_directory_uri() . '/css/elasticsearch.css');
}
}

File: template-parts/category-selector.php

<div class="category-selector">
<label for="category-select">Category:</label>
<select id="category-select" name="category">
<option value="">All Categories</option>
<?php
$categories = get_categories_hierarchy();
foreach ($categories as $category) {
echo '<option value="' . esc_attr($category['id']) . '">' . esc_html($category['name']) . '</option>';
}
?>
</select>
<div id="category-search" class="category-search">
<input type="text" id="category-search-input" placeholder="Search categories...">
<div id="category-search-results" class="category-search-results"></div>
</div>
</div>
<script>
// Category search functionality
document.getElementById('category-search-input').addEventListener('input', function(e) {
const query = e.target.value;
if (query.length < 2) {
document.getElementById('category-search-results').innerHTML = '';
return;
}
jQuery.post(ajaxurl, {
action: 'search_categories',
nonce: ajax_nonce,
query: query
}, function(response) {
if (response.success) {
displayCategorySearchResults(response.data);
}
});
});
</script>
// Create nonces for different actions
function create_elasticsearch_nonces() {
return [
'elasticsearch_nonce' => wp_create_nonce('elasticsearch_nonce'),
'search_nonce' => wp_create_nonce('search_nonce'),
'category_nonce' => wp_create_nonce('category_nonce')
];
}
// Verify nonces in AJAX handlers
function verify_elasticsearch_nonce($action) {
if (!wp_verify_nonce($_POST['nonce'], $action)) {
wp_send_json_error('Invalid nonce');
}
}
// Check user capabilities
function check_elasticsearch_permissions($capability = 'manage_options') {
if (!current_user_can($capability)) {
wp_send_json_error('Insufficient permissions');
}
}
// Public search permissions (no login required)
function check_search_permissions() {
// Search is public, no additional checks needed
return true;
}
// Sanitize search inputs
function sanitize_search_inputs($inputs) {
$sanitized = [];
foreach ($inputs as $key => $value) {
switch ($key) {
case 'query':
case 'category':
case 'brand':
$sanitized[$key] = sanitize_text_field($value);
break;
case 'min_price':
case 'max_price':
case 'page':
case 'per_page':
$sanitized[$key] = intval($value);
break;
default:
$sanitized[$key] = sanitize_text_field($value);
}
}
return $sanitized;
}
// Handle WordPress errors gracefully
function handle_elasticsearch_error($error, $context = '') {
// Log error
error_log("Elasticsearch error [$context]: $error");
// Send appropriate response
if (wp_doing_ajax()) {
wp_send_json_error($error);
} else {
wp_die($error);
}
}
// Custom error handler
set_error_handler(function($severity, $message, $file, $line) {
if (strpos($file, 'elasticsearch') !== false) {
handle_elasticsearch_error("$message in $file:$line", 'PHP Error');
}
});
// WordPress object cache integration
function get_cached_search_results($cache_key, $callback) {
$cached = wp_cache_get($cache_key, 'elasticsearch');
if ($cached === false) {
$cached = $callback();
wp_cache_set($cache_key, $cached, 'elasticsearch', 300); // 5 minutes
}
return $cached;
}
// Cache search results
function cached_search_products($query, $filters, $page, $per_page) {
$cache_key = 'search_' . md5(serialize([$query, $filters, $page, $per_page]));
return get_cached_search_results($cache_key, function() use ($query, $filters, $page, $per_page) {
$elasticsearch = new ProductElasticsearch();
return $elasticsearch->searchProducts($query, $filters, $page, $per_page);
});
}
// Optimize WordPress database queries
add_action('pre_get_posts', 'optimize_elasticsearch_queries');
function optimize_elasticsearch_queries($query) {
if (is_page_template('page-search.php')) {
// Disable WordPress query for search page
$query->set('post_type', 'none');
}
}