Mettre à jour les commentaires avec l’Heartbeat API

Depuis WordPress 3.6, une nouvelle API a fait son apparition dans WordPress : l’heartbeat.

Sous ce nom poétique se cache un système qui permet de faire de la communication client/serveur à intervalles régulières.

Cet API définie un « poul » dans WordPress, et à chaque « battement » va collecter et exécuter les actions souhaitées par le core, le thème ou les plugins.
Plus vulgairement, il s’agit surtout d’une requête ajax dans un setInterval. On va donc y retrouver quelques similitudes avec les méthodes d’admin-ajax.

WordPress Heartbeat API
Découvrez l’Heartbeat API de WordPress

Au niveau utilisation, on rencontre l’heartbeat par exemple lorsque deux personnes souhaitent éditer un même article. Un message apparaît pour indiquer que telle personne est déjà en train d’écrire. C’est cette API qui fait ça.

Ou bien sur le blog make.wordpress.org, lorsque quelqu’un laisse un commentaire d’un article que vous êtes en train de lire, il est ajouté automatiquement et un message s’affiche pour vous en avertir.

Nous pourrions en avoir une multitude d’usages, mais pour découvrir cette nouvelle fonctionnalité je vous propose de voir comment mettre automatiquement à jour les commentaires d’un article.

Comment fonctionne l’Heartbeat API ?

L’Heartbeat API dépend à la fois du core, et à la fois d’un fichier javascript qui peut être ajouté via le thème ou un plugin.

Une fois ce fichier présent sur le site, de nouvelles méthodes permettront de faire communiquer du code javascript avec des fonctions PHP hookées par nos soins.

Pour l’exemple de cet article, réalisons un plugin.

Pour cela, il faut créer un nouveau répertoire nommé « commentaires-heartbeat » dans le dossier plugins du site. À l’intérieur de celui-ci, ajoutons deux nouveaux fichiers vides : « commentaires-heartbeat.php » et « commentaires-heartbeat.js ».

Éditez le fichier PHP et ajoutez-y les lignes :

<?php
/**
Plugin Name: Plugin commentaires
Description: Les commentaires qui se chargent avec l'heartbeat
Author: willybahuaud
Version: 0.9
*/

Vous pouvez maintenant activer ce plugin, dans l’onglet extensions de l’administration de WordPress.

Une liaison entre javascript et PHP

La première étape va être de charger le fichier javascript de l’heartbeat là où nous en auront besoin, c’est à dire sur les pages des articles.

On fait ça en utilisant la fonction wp_enqueue_script, au moment de l’enqueue des scripts, et seulement s’il s’agit d’un article :

add_action( 'wp_enqueue_scripts', 'heartbeat_enqueue_scripts' );
function heartbeat_enqueue_scripts(){
    if( is_single() ) {
        wp_register_script( 'heartbeat-comments', plugin_dir_url( __FILE__ ) . 'commentaires-heartbeat.js', 'heartbeat', '0.9', true );
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'heartbeat' );
        wp_enqueue_script( 'heartbeat-comments' );
    }
}

Vous voyez que j’en ai aussi profité pour ajouter la librairie jQuery ainsi que le fichier javascript de notre plugin.

Côté javascript maintenant, nous allons enqueue une nouvelle action nommée « commentaire » sur l’heartbeat.
Le code qui va suivre comporte du jQuery, il faut donc le contenir dans un jQuery noConflict.

Puis nous allons créer un nouvel objet qui contiendra les paramètres de notre action.

Ensuite, on ajoute notre action. La méthode se nomme enqueue et prend trois arguments :

Pour finir, il faut écouter l’événement jQuery heartbeat-tick qui concernerait commentaire.

La méthode hasOwnProperty sert à savoir si l’action a renvoyé quelque chose.

jQuery(document).ready( function($) {

    var args = {
        'version': '3.6',
    };
     
    wp.heartbeat.enqueue(
        'commentaire',
        args,
        false
    );

    $(document).on( 'heartbeat-tick.commentaire', function( event, data ) {
        console.log( 'beat' );
        if ( data.hasOwnProperty( 'commentaire' ) ) {
            console.log( data[ 'commentaire' ] );            
            wp.heartbeat.enqueue(
                'commentaire',
                args,
                false
            );
        }
    });

} );

Pour finir avec le javascript, une fois l’événement exécuté, il ne faut pas oublier de remettre notre action dans la file de l’heartbeat.

Il faut ensuite relier les actions correspondantes côté PHP.

Il existe plusieurs hooks sur lesquels nous pouvons nous greffer :

Comme pour une requête ajax, nous allons utiliser deux filtres pour construire la réponse qui sera envoyée aux utilisateurs qu’ils soient connectés ou pas.

On ajoute un élément à la réponse uniquement si des informations concernant les commentaires ont été demandées (il s’agit d’une handle de notre action javascript).

function commentaire_respond_to_server( $response, $data, $screen_id ) {
    if ( isset( $data['commentaire'] ) ) {
        $response['commentaire'] = array(
            'hello' => 'world'
        );
    }
    return $response;
}
 add_filter( 'heartbeat_received', 'commentaire_respond_to_server', 10, 3 );
 add_filter( 'heartbeat_nopriv_received', 'commentaire_respond_to_server', 10, 3 );

On peut maintenant tester notre plugin en front office. Si vous regarder la console, vous verrez les pulsations de l’API ainsi que les messages reçus.

Nous allons maintenant utiliser cette base de plugin pour mettre à jour nos commentaire en live.

Être averti des nouveaux commentaires

Nous souhaitons être prévenus dès que de nouveaux commentaires humains validés sont ajoutés sur l’article courant. En d’autres mots, il va falloir d’abord s’assurer que nous sommes sur un article puis récolter les commentaires publiés depuis que nous avons chargé la page.

Dans un second temps nous afficherons ces commentaires sur la page, puis ferons apparaître une alerte pour en avertir le lecteur.

Récupérer les nouveaux commentaires

Il va donc nous falloir deux variables javascript : l’id de l’article courant et le timestamp au moment du chargement. Ces valeurs seront poussées vers notre fichier via la fonction wp_localize_script(), puis en paramètre de notre action, de manière à pouvoir être envoyées à l’Heartbeat.

Coté PHP :

function heartbeat_enqueue_scripts(){
    if( is_single() ) {
        global $post;
        wp_register_script( 'heartbeat-comments', plugin_dir_url( __FILE__ ) . 'commentaires-heartbeat.js', 'heartbeat', '0.9', true );
        wp_enqueue_script( 'jquery' );
        wp_enqueue_script( 'heartbeat' );
        wp_enqueue_script( 'heartbeat-comments' );
        wp_localize_script( 'heartbeat-comments', 'heartbeatCommentsAtts', array( 'id' => $post->ID, 'time' => time() ) );
    }
}

Et en javascript :

    wp.heartbeat.setInterval( 15 );

    var args = {
        'time' : heartbeatCommentsAtts[ 'time' ],
        'id' : heartbeatCommentsAtts[ 'id' ]
    };
     
    wp.heartbeat.enqueue(
        'commentaire',
        args,
        false
    );

Vous remarquerez au passage que j’ai baissé la pulsation de l’API à une pulsation toutes les 15 secondes. Les valeurs possibles sont 5, 15, 30 ou 60.

Maintenant, il faut récupérer les nouveaux commentaires dans notre fonction « commentaire_respond_to_server« .

On s’assure d’avoir un id d’article ainsi qu’un timestamp de comparaison, puis on fait une requête sur les commentaires en utilisant le paramètre date_query. Ici, on ne souhaite que les commentaires humains (comment_type vide) publiés depuis la date passée en paramètre, et pour l’ID indiqué.

Ensuite, pour chaque commentaire trouvé, il faut reconstituer le markup HTML. Il dépend complètement de votre thème. J’ai illustré ici les fonctions courantes qui vous serons utiles (gravatar, contenu, liens…).

Chaque sortie HTML est poussée dans l’objet retourné, accompagnée de l’objet du commentaire (qui pourrait éventuellement servir en javascript).

function commentaire_respond_to_server( $response, $data, $screen_id ) {
    if ( isset( $data['commentaire'] ) ) {
        if ( ! isset( $data['commentaire']['id'], $data['commentaire']['time'] ) )
            return false;
        $time     = $data['commentaire']['time'];
        $comments = get_comments( array(
            'post_id'      => $data['commentaire']['id'],
            'status'       => 'approve',
            'comment_type' => '',
            'date_query'   => array(
                array(
                    'after' => array(
                        'year'   => date( 'Y', $time ),
                        'month'  => date( 'm', $time ),
                        'day'    => date( 'd', $time ),
                        'hour'   => date( 'H', $time ),
                        'minute' => date( 'i', $time ),
                        'second' => date( 's', $time )
                        )
                    )
                )
            ) );
        if( $comments ) {
            $commentaires = array();
            foreach( $comments as $comment ) {
                $html = '<li id="' . esc_attr( $comment->comment_ID ) . '">';
                $html .= '<div class="comment-infos">' . wp_sprintf( 'Par %s', get_comment_author_link( $comment->comment_ID ) ) . '<span class="comment-time"> — Publié à l\'instant</span></div>';
                $html .= get_avatar( $comment->comment_author_email, 200, 'Mystery Man', $comment->comment_author );
                $html .= '<div class="comment-text">';
                $html .= apply_filters( 'comment_text', get_comment_text( $comment->comment_ID ), $comment );
                $html .= '</div>';
                $html .= '</li>';

                $commentaires[] = array( 'comment' => $comment, 'html' => $html );
            }
        }

        $response['commentaire'] = array(
            'comments' => $commentaires
        );
    }
    return $response;
}

Charger les commentaires récupérés

Si vous regardez votre console, vous verrez les hypothétiques commentaires apparaître dans l’objet de la réponse.

Vous pouvez tester en ayant deux fenêtres de votre site ouvertes 🙂

Il nous reste maintenant à charger ces nouveaux commentaires dans l’article. Mais il y a deux cas de figures que nous devons prévoir : soit il s’agit du premier commentaire (et il faut aussi charger leur conteneur), soit il y a déjà des commentaires et il faut juste l’ajouter à la suite.

Reprenons notre fichier javascript…

Si c’est un commentaire de plus

Dans notre fonction, il faut tester si des commentaires ont été retournés dans la réponse. Si c’est le cas, on identifie la liste qui contient les messages puis on utilise la fonction each de jQuery pour ajouter chaque commentaire à la file.

    $(document).on( 'heartbeat-tick.commentaire', function( event, data ) {
        if ( data.hasOwnProperty( 'commentaire' ) ) {
            if( data['commentaire'].comments ) {
                var $list = $( '.comment-list' );
                $.each( data['commentaire'].comments, function(i,val) {
                    $list.append( val.html );
                } );
            }
            args.time = Math.round( new Date().getTime() / 1000 );
            wp.heartbeat.enqueue(
                'commentaire',
                args,
                false
            );
        }
    });

Il ne faut pas oublier de mettre à jour la variable time, de manière à ce que seuls les commentaires postérieurs à cet ajout soient prochainement récoltés.

Si c’est le premier commentaire de l’article

Dans le cas où ce message serait le premier, la seule chose qui diffère est qu’il faut créer le conteneur des commentaires avant de les importer.

Bien entendu, les classes et id des éléments de votre site son susceptible de différer de notre exemple. Le code aura donc très probablement besoin d’être adapté au cas par cas.

            if( data['commentaire'].comments ) {
                if( $( '.comment-list' ).length == 0 ) {
                    $( '#comments' ).prepend( '<h2 class="comments-title">1 Commentaire</h2><ol class="comment-list"></ol>');
                }
                var $list = $( '.comment-list' );
                $.each( data['commentaire'].comments, function(i,val) {
                    $list.append( val.html );
                } );

Mettre à jour le compteur

Si le nombre de commentaires a changé, il faut également mettre à jour le titre de la section où est probablement indiqué le nombre de messages laissés par les internautes sur cet article.

Adaptez également ce code en fonction de votre thème WordPress.

                $.each( data['commentaire'].comments, function(i,val) {
                    $list.append( val.html );
                } );
                var count = $list.find('li').length;
                var text  = ( count > 1 ) ? count + ' Commentaires' : '1 Commentaire'; 
                $( '.comments-title' ).text( text );

Un message d’alerte

L’ultime étape de notre plugin : avertir l’internaute que de nouveaux messages sont apparus.

Rien ne vous oblige à le faire ; peut-être que vous jugerez que l’apparition d’une boite modale peut perturber vos lecteurs. Mais si vous voulez faire comme sur make.wordpress.org, voici le code :

var lien  = '#' + data['commentaire'].comments[0].comment['comment_ID'];
var style = 'position:fixed;color:#FFF;background:#000;padding:1em;font-size:1.2em;font-family:sans-serif;top:2em;right:2em';
$( 'body' ).append( '<a href="' + lien + '" style="' + style + '" class="alert-commentaire">Nouveau commentaire</a>');
setTimeout( function() {
	$( '.alert-commentaire' ).remove();
}, 2000 );

Il doit être ajouté juste après la boucle d’insertion des commentaires.

Cet article est à la fois un tutoriel pour améliorer l’expérience utilisateur sur votre site web, mais c’est aussi un exemple pour prendre en main l’API Heartbeat de WordPress qui vous permettra de faire, je l’espère, bien plus encore…

Voici deux fichiers qui pourraient vous être utiles pour cerner davantage l’Heartbeat : le JS de l’API et ses fonctions PHP.

Avez-vous des idées d’utilisation ?

Contacter l'auteur :

willy bahuaud

Je suis Willy Bahuaud, intégrateur et développeur, spécialiste de WordPress.
Besoin de mes services ? Écrivez-moi !

7 commentaires

  1. Par Gaël Poupard — Il y a 3 années
    Super article !!
    Heartbeat est beaucoup plus claire maintenant 😀
    Pour les applications possibles il y en a quelques-unes, notamment si des mises à jour de contenu sont attendues : le classique F5 hystérique pour s’inscrire à quelque chose ou consulter un résultat ! Ou même une gestion de tickets, de discussions (BBPress pourrait s’en servir, entre autres), etc.

    Si WordPress est utilisé pour gérer ce type de contenu, ça peut être un réel plus pour l’internaute, à condition de l’avertir du fonctionnement.

  2. Par Julio Potier — Il y a 3 années
    Allez, monsieur fini un guide « Navigation Ajax » #teaser chez moi qui sera publié lundi et hop, il balance le lendemain un article « Guide Heartbeat API », mais ho ça va oui !
    Je suis obligé de mettre en fav et RT pour la peine.
    Au fait, on fait comment quand on a les commentaires imbriqués ? mouahahaha le chieur (oui je sais, demmerde toi t’es grand)

    Sérieusement, ça me semble super complet, comme le dit Gael, les apps qui peuvent utiliser ça. Exemple sur la billetterie du WordCamp Paris 2014, http://2014.paris.wordcamp.org/billeterie/ on pourrait mettre à jour le nombre de places restantes, et à 0, remplacer le contenu ! (côté PHP idem on affiche un message si 0)

    Vraiment top,je reviens commenter ici le jour où j’ai utilisé mais je VAIS l’utiliser ;)

    Merci Willy

  3. Par Willy Bahuaud — Il y a 3 années
    Merci 🙂 Oui, ce sont des bonnes idées d’applications, et avec un battement toutes les 5 secondes, c’est quasiment du direct.
    Je crois que les devs de BuddyPress ont parlé d’utiliser l’API Heartbeat pour le module des activités.

    Hé ouai, et mardi normalement un guide WPML chez Seomix #teasing ^^
    Sinon pour les commentaires imbriqués je n’en n’ai pas parlé, j’attendais qu’on me pose la question.

    Ça dépend un peu de la structure du thème, mais il faut remplacer la boucle d’insertion des commentaires par un truc du genre, dans notre fichier javascript :

    
    $.each( data['commentaire'].comments, function(i,val) {
    	if( val.comment['comment_parent '] ) {
    		var $parent = $('#' + val.comment['comment_parent'] );
    		if( $parent.find( 'ul' ).length == 0 ) {
    			$parent.append( '<ul>' + val.html + '</ul>' );
    		} else{
    			$parent.find( 'ul' ).append( val.html );
    		}
    	} else {
        	$list.append( val.html );
        }
    } );
    

    Et merci pour vos commentaires, ils me motivent à faire davantage d’articles 🙂

  4. Par x@v — Il y a 3 années
    Hi,
    ajoutons deux nouveaux fichiers vides : « commentaires-heartbeat.php » et « commentaires-heartbeat.php« 

    A partir de là, j’ai pas trop insiste sur la lecture.

    D’autre part l’article est difficile à lire, à chaque fois il faut quitter la lecture pour cliquer sur le code.

    Merci quand meme !

    PS: qwerty keyboard

  5. Par Willy Bahuaud — Il y a 3 années
    Oui, c’est sûr que l’article est relativement orienté technique, ce n’est pas forcément le cas de tous mes articles. Cependant cette API est surtout intéressante au niveau du développement, et absolument pas documentée. Je ne pouvait pas faire l’impasse.

    Merci d’avoir tenté de suivre tout de même ^^

  6. Par Geoffrey — Il y a 3 années
    Hello,

    Vraiment excellent ce guide. Ça pourrait servir également pour un ecommerce lors d’une vente flash pour les stocks d’un produit, quitte à aller le greffer jusqu’au panier pour le mettre à jour.

    Dans ton exemple tu pourrais vérifier initialement si les commentaires sont activés en plus de checker si tu es sur un article. Autre chose : virez le console.log pour la prod, IE n’est pas fan de base 🙂

    Encore un excellent travail Willy. Merci !

  7. Par Willy Bahuaud — Il y a 3 années
    Oui, tout à fait pour une utilisation en e-commerce. On pourrait l’utiliser pour mettre à jour en live la disponibilité des produits. Par contre pour mettre à jour le panier, je pense qu’une simple requête ajax serait plus simple 🙂

    Tu as tout à fait raison, je devrais aussi checker si les commentaires sont activés et il faut virer les console.log() sur les codes finaux 😛

Commenter