Aller au contenu principal


Le bloc "likes"

Ce billet est la suite de la page "autopsie d'un module" dont la lecture aidera à la compréhension de ce billet.
Le bloc "likes" est l'élément central de mon module personnalisé "dh_like". Sa  finalité, illustrée ci-dessous, est de permettre au visiteur dire s'il aime ou non la page affichée. 

Cliquer pour agrandir l'image

 

Dernière MAJ :
28 nov 2024
Billet créé le :
07 Mar 2023

Ce bloc doit être ajouté aux pages pour lesquelles l'administrateur du site souhaite obtenir un avis du lecteur soit :

C'est le programme "dhLikeBlock.php" , qui est le code "source" de ce bloc. Ce code figure dans l'onglet "dh_like_block" du §4 "les codes" présenté ci-dessous et figure dans le fichier "dh_like_block.php" du dossier "src/Plugin/Block" du module.

Ce billet, qui explique les différentes parties de ce code,  se compose de 3 parties :

  1. Analyse du code du fichier "dhLikeBlock.php";

  2. Analyse du code des fichier générant les formulaires contenus dans le bloc ;

  3. La gestion de la table "like" dans la base de données du site.

1. Analyse du fichier "DhlikeBlock.php"

La documentation de base : https://www.drupal.org/docs/creating-modules/creating-custom-blocks/create-a-custom-block est à connaître pour une bonne compréhension de ce billet.

Il définit la classe "dh_like_block" comme extension de la classe abstraite "BlockBase"  qui offre la fonction générique "buid()" pour construire et renvoyer un "renderable array" (tableau de rendu) qui affichera le bloc "dh_like_block".

 

 La classe mère "BlockBase" offre la possibilité au développeur d'ajouter au bloc, ses propres éléments de configuration. On parle alors d'un bloc "configurable" et par opposition à bloc non configurable. Une version configurable de ce bloc est présentée dans le billet : "le bloc des likes version configurable".   

Dans ce code, la fonction "buid()" récupère dans ses variables "$like_button_form" et "$dislike_button_form" les références des objets formulaire créés par l'appel de la fonction "formBuilder" de la classe Drupal ( "\Drupal::formBuilder()") ;

La fonction buid() rend un "renderable array" qui permettra, à travers le template "dh-like-block.html.twig" (défini dans l'attribut "#theme") et par l'évaluation des clés "like_button" et "dislike_button" de l'attribut "#data" d'afficher ces formulaires (visualisés par les pouces vers le haut et vers le bas cf. image du bloc) . Ce template, détaillé au §3 "l'affichage du bloc...", affichera également le contenu des attributs :

  • "#sous-titre" ;

  • "#vote" à savoir le ratio, les nombres de "like" et de "dislike".

Ce template utilisera les styles présents dans le fichier attaché à la hiérarchie "dh_like/custom" de la libraire CSS du module  (par l'attribut "#attached") (lire le §4 : le fichier "dh_like.libraries.yml" et le §8 : le dossier "template" du billet "autopsie d'un module") et le vidage du cache pour une mise à jour  immédiate de l'affichage (par l'attribut "#cache").

  La classe mère BlockBase permet d'ajouter la fonction "getCacheMaxAge()" suivante dans le code de déclaration de la classe au lieu de l'attribut "#cache" du "renderable array" de la fonction "build()"

  public function getCacheMaxAge() {      
   return 0;      
 }    

2. Les formulaires

La documentation de base https://www.drupal.org/docs/drupal-apis/form-api/intoduction-to-form-api est à connaitre.
Le code étudié ici, figure dans les onglets "like_form" et "dislike_form" ci-dessous et se trouve contenu respectivement dans les fichiers "LikeForm.php"  et "DislikeForm.php" du dossier "src/Form" du module.

Ces fichiers décrivent les classes "LikeForm" et "DislikeForm" comme extension de la classe FormBase qui offre les fonctions génériques :

  •  "buidForm()" : le constructeur du formulaire ;

  • "validForm()" : pour la validation des données saisies dans le formulaire ;

  • "submitForm()" : pour l'envoi du formulaire.

Dans chaque fichier, la fonction "buidForm()" définit le formulaire avec 3 champs :

  • 'entity_id' : champ caché qui mémorise le "numéro de la page" obtenu par l'emploi de la fonction "getCurrentEntity()" ;

  •  'like_count' : champ caché qui mémorise le nombre actuel de "likes" pour cette page obtenu grâce à getFromDbLike() ;

  • 'submit' : bouton de validation du formulaire.

Un clic sur le bouton "submit" appellera la fonction "submitForm()" qui met à jour la table des likes de la base de données lors de l'appel à la fonction "updateDbLike()".

La fonction getFromDbLike()  interroge la table des likes de la base de données et rend le nombre de like (resp. dislike) enregistré en base de données pour cette page.

Les fonctions "getFromDbLike()" et "updateDbLike()", qui manipulent la table "like" de la base de données, sont détaillées dans le billet 'gérer la table like". Elles sont définies dans le "trait"  "GestionLikeTrait" (défini dans le fichier "GestionLikeTrait.php" du dossier "src\Utility" du module dhlike) et sont accessibles dans les classes LikeForm et DisLikeForm, grâce  à la déclaration "use GestionLikeTrait" lors de la définition de ces classes.

 

3. L'affichage du bloc "like"

Comme indiqué par le "renderable array" de la fonction "build" définissant le bloc like, l'affichage est produit par le template "dh-like-block.html.twig" (voir à ce propos le §6 du billet "autopsie d'un module").

Le code de ce fichier, illustré par l'image figurant dans l'introduction de ce billet, est reproduit ci-dessous :

{{ sousTitre }}
<div class="dh_like_block">
    <div class="dh_like_blockForm">
        {{data.like_button}}{{vote.nbLike}}
    </div>
    <progress value="{{vote.resultat}}" ></progress>
    <div class="dh_like_blockForm">
        {{data.dislike_button}}{{vote.nbDislike}}
    </div>
</div>

Il affiche :

  • les valeurs des variables :

    • sousTitre : affichage du sous-titre définit lors de l'installation du module ;

    •  vote.nbLike : le nombre de like ;

    • vote.nbDislike ; le nombre de disLike ;

  • les formulaires "like_button" et "dislike_button", mis en forme par la classe "dh_like_block" de la bibliothèque "dh_like.css" attachée à ce bloc.

  • une barre de progression  à travers la balise HTML "progress" et son attribut  "value"  recevant la valeur de la variable vote.resultat. 

 

4. Les codes

Cliquez sur le bouton pour copier le code dans le presse papier
 <?php
namespace Drupal\dhlike\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
//pour compléter le formulaire de config dans le menu d'administration
use Drupal\Core\Block\BlockPluginInterface;
/**
* Provides a 'dh_like' Block.
*
* @Block(
* id = "dhlike_block",
* admin_label = @Translation("Le bloc Like (dh)"),
* category = @Translation("Social network"),
* )
*/
class DhlikeBlock extends BlockBase {
/**
* @return int
*/
//https://www.adimeo.com/blog-technique/cache-drupal
public function getCacheMaxAge() {
return 0;
}   
public function defaultConfiguration() {
return [
'dhLike_Elt1' => $this->t('A default value. This block was created at %time', ['%time' => date('c')]),
    'dhLike_Elt2' => date('c'),
];
}
/**
* {@inheritdoc}
*/
public function build() {    
   //Génére le sous titre du bloc
if ($this->configuration['isSubtitle']=='Oui' && strlen($this->configuration['subtitle'])>0){
       $sousTitre = [
           '#type' => 'html_tag',
           '#tag' => 'h4',
           '#value' => $this->configuration['subtitle'],
       ];
}
   // Get the like/dislike buttons.
$like_button_form = \Drupal::formBuilder()->getForm(\Drupal\dhlike\Form\LikeForm::class);
   $dislike_button_form = \Drupal::formBuilder()->getForm(\Drupal\dhlike\Form\DisLikeForm::class);
   //
   $like_count = $like_button_form['like_count']['#default_value'];
   $dislike_count = $dislike_button_form['dislike_count']['#default_value'];
   $nbVotes = $like_count + $dislike_count;
   if ($nbVotes > 0){
       //$form1=$like_button_form ;
       //$form2=$dislike_button_form ;
       $taux = $like_count / $nbVotes;
   }
   else {
       $taux=0.5;
   }
   // renderable array
   $ra= [];
   $ra['1']= [
'#theme' => 'dhlike_block',
'#data' => [
        'like_button' => $like_button_form,
        'dislike_button' => $dislike_button_form,
       ],
       '#sousTitre' =>$sousTitre,
       '#vote' => ['resultat'=>$taux,'nbLike'=>$like_count,'nbDislike'=>$dislike_count,],
       '#config' => [
           'tout' => $this->getConfiguration(), // equivalent de BlockBase::getConfiguration(),
           'titre' => $this->label(),
           ],
       '#cache' => [
'max-age' => 0,
],
       '#attached' => [
           'library' => [
               'dhlike/custom',
           ],
       ],
   ];
   //
   return $ra;
}
/**
* {@inheritdoc}
* Pour compléter le formulaire de configuration affiché lorsqu'on ajoute le bloc à une région
*/
public function blockForm($form, FormStateInterface $form_state) {
$form = parent::blockForm($form, $form_state);
   //
$form['isSubtitle'] = [
'#type' => 'radios',
'#title' => $this->t('Voulez-vous une sous-titre ?'),
'#default_value' => isset($this->configuration['isSubtitle']) ? $this->configuration['isSubtitle'] : "Oui",
'#options' => [
"Oui"=>$this->t('Oui'),
"Non"=>$this->t('Non')
],
'#required'=>true,
'#attributes' => [
'data-n' => 'isSubtitle',
],
];
   //
$form['subtitle'] = [
'#type' => 'textfield',
'#title' => $this->t('Ajout d\' un sous-titre'),
'#description' => $this->t('Indiquez votre sous-titre !'),
'#default_value' => isset($this->configuration['subtitle']) ? $this->configuration['subtitle'] : '',
'#states' => [
//show this textfield only if the radio 'Oui' is selected above
'visible' => [
':input[data-n="asksubtitle"]' => ['value' => "Oui"],
],
],
];
return $form;
}
/**
* {@inheritdoc}
* Vérification de la validité des paramètres saisis
*/
public function blockValidate($form, FormStateInterface $form_state) {
   // récupération des paramètres
$subtitle = $form_state->getValue('subtitle');
$isSubtitle = $form_state->getValue('isSubtitle');
   //
if ($isSubtitle=="Oui" && strlen($subtitle)==0) {
       // pas de sous-titre saisi donc erreeur
$form_state->setErrorByName('subtitle', t('Saisissez au moins un caractère !'));
}
}
/**
* {@inheritdoc}
* Elle renregistre automatiquement en BdD la configuration les valeurs saisie par le formulaire
*/
public function blockSubmit($form, FormStateInterface $form_state) {
   // sauver les paramètres des éléments standards du formulaire
parent::blockSubmit($form, $form_state);
   // sauver les paramètres complémentaires propre au module
$values = $form_state->getValues();
$this->configuration['subtitle'] = $values['subtitle'];
$this->configuration['isSubtitle'] = $values['isSubtitle'];
   // pour éventuel debug affichage dans status
foreach ($form_state->getValues() as $key => $value) {
if (!is_array($value)){
\Drupal::messenger()->addMessage($key . ': ' . $value);
}else{
foreach ($value as $k=>$v){
\Drupal::messenger()->addMessage($k . ': ' . $v);
}
}
}
}
}

  

                
Cliquez sur le bouton pour copier le code dans le presse papier
 <?php
namespace Drupal\dhlike\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\dhlike\Utility\GestionLikeTrait;
/**
* Class LikeForm.
*/
class LikeForm extends FormBase {
use GestionLikeTrait;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'dh_like_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
   //rend est un tableau associatif dont les clés correspondent aux éléments du formulaire.
$current_entity = $this->getCurrentEntity();
// If current page is no entity, return null.
if(!$current_entity){
return null;
}
$entity_id = $current_entity->id();
//$like_count = $this->getLikeCount($entity_id);
   $like_count = $this->getFromDbLike($entity_id, "uplike");
//
$form['entity_id'] = [
'#type' => 'hidden',
'#required' => true,
'#default_value' => $entity_id,
];
//   
   $form['like_count'] = [
'#type' => 'hidden',
'#required' => true,
'#default_value' => $like_count,
];
//
   $form['submit'] = [
       '#type' => 'submit',
       '#value' => ' ',
       '#attributes' => [
           'style' => 'width:27px;',
           'id' =>'liked-form-button',
       ],
       //'#prefix' => ' ',
       //'#suffix' => '<span>'.$button_text.'</span>',
];
//
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
   // retrieve data from form
   $form_values = $form_state->getValues();
   $entity_id = $form_values['entity_id'];
   $like_count = $form_values['like_count'];
   //
   $like_count = $like_count + 1;
$this->updateDbLike($like_count,$entity_id,'uplike' );
return;
}
}

  

                
Cliquez sur le bouton pour copier le code dans le presse papier
  <?php
namespace Drupal\dhlike\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\dhlike\Utility\GestionLikeTrait;
/**
* Class DisLikeForm.
*/
class DisLikeForm extends FormBase {
use GestionLikeTrait;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'dh_dislike_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$current_entity = $this->getCurrentEntity();
//$logged_in = \Drupal::currentUser()->isAuthenticated();
// If current page is no entity, return null.
if(!$current_entity){
return null;
}
$entity_id = $current_entity->id();
$uid = \Drupal::currentUser()->id();
$dislike_count = $this->getFromDbLike($entity_id, "downlike");
   //$button_text = $dislike_count. t(' disLike');
   $button_text = $dislike_count;
//
$form['entity_id'] = [
'#type' => 'hidden',
'#required' => true,
'#default_value' => $entity_id,
];
   //
   $form['submit'] = [
       '#type' => 'submit',
       '#value' => ' ',
       '#attributes' => [
           'style' => 'width:30px;',
           'id' =>'disliked-form-button',
       ],
       //'#prefix' => ' ',
       //'#suffix' => '<span>'.$button_text.'</span>',
];
   //
   $form['dislike_count'] = [
'#type' => 'hidden',
'#required' => true,
'#default_value' => $dislike_count,
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
   $form_values = $form_state->getValues();
   $entity_id = $form_values['entity_id'];
   $dislike_count = $form_values['dislike_count'];
   $dislike_count = $dislike_count + 1;
   $this->updateDbLike($dislike_count,$entity_id,'downlike' );
return;
}
/**
* Get current entity, if any.
*/
private function getCurrentEntity(){
$currentRouteParameters = \Drupal::routeMatch()->getParameters();
foreach ($currentRouteParameters as $param) {
if ($param instanceof \Drupal\Core\Entity\EntityInterface) {
$entity = $param;
return $entity;
}
}
return NULL;
}
}