WordPress hwk Blog Créer des Requêtes Ajax avec Cache Statique & Mise à jour à Intervalle Régulier sur WordPress

Créer des Requêtes Ajax avec Cache Statique & Mise à jour à Intervalle Régulier sur WordPress

25 March 2018

Les projets web complexes requièrent souvent d’afficher des modules avec des traitements lourds: Appels API, multiple requêtes dans la base de données etc… Si il est nécessaire de régulièrement rafraîchir les données avant de les afficher aux visiteurs, alors ces modules vont inévitablement poser des problèmes de performance.

Dans ce tutoriel nous allons optimiser l’impact de ces modules en créant un système de cache indépendant, avec des requêtes Ajax dynamiques & ciblées.

Introduction & Concept

Bien que ce tutoriel pourrait s’appliquer à des pages entières, nous allons ici nous concentrer sur la gestion par “modules”, dans les pages. Le cache statique global sera délégué aux extensions de cache traditionnelles: WP Super Cache, W3TC ou encore WP Rocket.

Notre système de Cache Ajax va nous permettre d’afficher un module avec des informations actualisées toutes les X minutes. Lorsqu’un visiteur atterri sur une page, le module affichera sa dernière version HTML en cache. Si cette version est expirée (+X minutes), le cache sera tout de même affiché, mais une requête Ajax actualisera le contenu du module afin de le remplacer par une version plus récente.

Le prochain visiteur affichera directement le nouveau fichier cache, sans requête et sans appel Ajax. Cette méthode nous permet de drastiquement optimiser les requêtes effectuées par le serveur, tout en gardant un affichage à jour et indépendamment des règles de cache de nos autres plugins.

Configuration simple

Pour chacun de nos modules, nous allons enregistrer une configuration avec des paramètres. La configuration ci-dessous aura pour effet:

  • id: Cette configuration sera identifiable par l’id 'hwk_ajax_heavy_query'.
  • template: Le fichier de traitement lourd est templates/modules/module-heavy.php.
  • cache: Le fichier de cache est templates/modules/cache/module-heavy.html.
  • interval: Le fichier de cache sera actualisé via Ajax, toutes les 15 minutes.
  • flush: A chaque mise à jour du cache via Ajax, vider le cache de la page qui appel cette configuration.
<?php
add_filter('hwk_ajax_configs', 'hwk_ajax_declare_config');
function hwk_ajax_declare_config($config){
$config[] = array(
'id' => 'hwk_ajax_heavy_query', // L'ID de référence de la configuration
'template' => 'templates/modules/module-heavy', // Path d'affichage du fichier qui effectue le traitement lourd
'cache' => 'templates/modules/cache/module-heavy', // path du fichier du cache HTML
'interval' => 900, // Intervalle de mise à jour du module
'flush' => get_the_ID(), // Flush du cache global à chaque intervalle? true|false|post_id
);
return $config;
}

Configuration avancée

Cette nouvelle configuration sera appelée sur une page single d’un post_type. Nous souhaitons que le module fasse un traitement spécifique en fonction du post_id en cours. Par conséquent, chaque fichier de cache doit avoir son propre post_id. Nous définissons donc les arguments cache et param de la manière suivante:

  • id: Cette configuration sera identifiable par l’id 'hwk_ajax_heavy_query_post'.
  • template: Le fichier de traitement lourd est templates/modules/module-heavy-post.php.
  • cache: Le fichier de cache est templates/modules/cache/module-heavy-post-%param%.html.
  • param: Variable dynamique exécuté sur page qui appel le module. Ici, get_the_ID() remplacera le terme %param% du fichier de cache.
<?php
add_filter('hwk_ajax_configs', 'hwk_ajax_declare_config_advanced');
function hwk_ajax_declare_config_advanced($config){
$config[] = array(
'id' => 'hwk_ajax_heavy_query_post',
'template' => 'templates/modules/module-heavy-post',
'cache' => 'templates/modules/cache/module-heavy-post-%param%', // %param% sera remplacé par la valeur indiqué dans le paramètre 'param'
'param' => get_the_ID(),
'flush' => get_the_ID(),
);
return $config;
}

Affichage du Template

Désormais, nous pouvons appeler les configurations 'hwk_ajax_heavy_query' ou 'hwk_ajax_heavy_query_post' directement dans notre Template de page:

<?php get_header(); ?>
<?php if(have_posts()): ?>
<?php while(have_posts()): the_post(); ?>
<?php hwk_ajax_template('hwk_ajax_heavy_query'); ?>
<?php hwk_ajax_template('hwk_ajax_heavy_query_post'); ?>
<?php endwhile; ?>
<?php endif; ?>
<?php get_footer(); ?>

Exemple de Module de Traitement

Ce fichier correspond à notre module templates/modules/module-heavy.php:

<?php
// Vérification que notre fichier est bien appelé via notre système
if(!$args = get_query_var('hwk_ajax_data'))
return;
// Data disponible:
// $args['id']
// $args['template']
// $args['cache']
// $args['flush']
// $args['param']
// $args['interval']
// Requête lourde ici
// Appel API, WP_Query etc…
$query = new WP_Query(array(
'post_type' => 'any',
'posts_per_page' => 1,
//'post__not_in' => array($args['param']), // On peut utiliser la valeur 'param' si celle-ci est définie.
));
?>
<!– Obligatoire: Création du container avec les données pour traitement pour l'ajax Ajax –>
<!– La fonction hwk_ajax_template_data() utilise $args['id'], $args['interval'] & $args['param'] –>
<div <?php hwk_ajax_template_data(); ?>>
<!– Affichage HTML du résultat –>
<?php if($query->have_posts()): ?>
<?php while($query->have_posts()): $query->the_post(); ?>
<?php //?>
<?php endwhile; wp_reset_query(); ?>
<?php endif(); ?>
</div>

Définition des fonctions

<?php
add_action('wp_ajax_hwk_ajax_template', 'hwk_ajax_template');
add_action('wp_ajax_nopriv_hwk_ajax_template', 'hwk_ajax_template');
function hwk_ajax_template($id = false){
$configs = apply_filters('hwk_ajax_configs', $configs);
if(empty($configs))
return;
$html = hwk_ajax_config($configs, $id);
if(wp_doing_ajax())
wp_send_json_success($html);
echo $html;
}
function hwk_ajax_config($configs, $id = false){
if(!$id && wp_doing_ajax() && isset($_POST['id']))
$id = $_POST['id'];
if(!$id)
return;
$config = array();
foreach($configs as $line){
if($line['id'] != $id)
continue;
$config = $line;
break;
}
return hwk_ajax_get_template($config);
}
function hwk_ajax_get_template($args = array()){
if(!$args['id'] || !$args['template'])
return;
$args = wp_parse_args($args, array(
'id' => false,
'template' => false,
'cache' => false,
'flush' => false,
'param' => false,
'interval' => 900,
));
if(!$args['param'] && isset($_POST['param']) && !empty($_POST['param']))
$args['param'] = $_POST['param'];
if($args['param'] && strpos($args['cache'], '%param%'))
$args['cache'] = str_replace('%param%', $args['param'], $args['cache']);
$file = hwk_ajax_cache_file($args['cache']);
// Fichier de cache: Permission de lecture Ajax
if($cache = hwk_ajax_cache_read($args, $file))
return $cache;
// Le fichier de Cache n'existe pas. Il faut le créer
ob_start();
set_query_var('hwk_ajax_data', $args);
get_template_part($args['template']);
$page = ob_get_clean();
// Mise à jour du fichier de cache
file_put_contents($file, $page);
// Pas de Flush de Cache
if(!$args['flush'])
return $page;
// WP Super Cache Flush
if(function_exists('wp_cache_clear_cache')){
if(is_int($args['flush']))
wp_cache_post_change($args['flush']);
else
wp_cache_clear_cache();
}
// W3TC Cache Flush
if(function_exists('w3tc_flush_post')){
if(is_int($args['flush']))
w3tc_flush_post($args['flush']);
else
w3tc_flush_posts();
}
// WP Rocket Cache Flush
if(function_exists('rocket_clean_post')){
if(is_int($args['flush']))
rocket_clean_post($args['flush']);
else
rocket_clean_domain();
}
// Affichage du contenu
return $page;
}
function hwk_ajax_cache_file($cache){
return get_template_directory() . '/' . $cache . '.html';
}
function hwk_ajax_cache_read($args, $file){
if(!file_exists($file))
return false;
if(wp_doing_ajax() && (filemtime($file) + $args['interval']) < time())
return false;
return file_get_contents($file);
}
function hwk_ajax_template_data(){
if(!$args = get_query_var('hwk_ajax_data'))
return;
echo 'data-hwk-ajax-action-id="' . $args['id'] . '" data-hwk-ajax-ts="' . (time() + $args['interval']) . '" data-hwk-ajax-param="' . $args['param'] . '"';
}

Script Javascript

jQuery(document).ready(function($){
// Vérification si un container de module existe
if($('[data-hwk-ajax-action-id]').length){
// Traiter chaque module indépendamment
$('[data-hwk-ajax-action-id]').each(function(){
// Variables générales
var $this = $(this),
$id = $this.data('hwk-ajax-action-id'),
$ts_cache = $this.data('hwk-ajax-ts'),
$param = $this.data('hwk-ajax-param'),
$ts_current = Math.floor(Date.now() / 1000),
// Données envoyées en Ajax
$data = {
'action': 'hwk_ajax_template',
'id': $id
};
// Si un param est défini, le faire remonter dans la requête Ajax
if($param != '')
$data.param = $param;
// Ne pas lancer de requête Ajax si le cache n'est pas expiré
if($ts_cache > $ts_current)
return;
// Requête Ajax
$.ajax({
url: ajaxurl,
type: 'post',
data: $data,
success: function(ajax){
// Error
if(!ajax.success || !ajax.data)
return;
// Remplacer le contenu du module par la version à jour
$this.replaceWith(ajax.data);
return;
}
});
});
}
});