Création d’un contrôle de sélecteur de liens CMB2 personnalisé pour WordPress
Dans ce tutoriel je vais voir comment créer un champ personnalisé pour étendre les fonctionnalités de CMB2 (Custom Meta Boxes 2) par WebDevStudios.
Je développe des sites Web (et des applications Web) avec le CMS WordPress (Système de gestion de contenu), et lorsqu’un nouveau projet débarque, vous pouvez garantir qu’il y aura une obligation pour moi de développer des ‘Meta Boxes personnalisées’ pour permettre à l’utilisateur d’avoir un contrôle précis sur le contenu et la mise en page des sites.
Je détaillerai comment j’ai construit le sélecteur de liens de contrôle CMB2 pour CMB2 (disponible dans tous les bons référentiels de plugins WordPress). Une capture d’écran peut être vue ci-dessous.
La commande ‘Link Picker’ CMB2 en action
Le sélecteur de liens déclenche la boîte de dialogue « Insérer/modifier le lien » intégrée à WordPress lorsque vous cliquez sur le bouton « Choisir ». Cela peut être vu dans la capture d’écran ci-dessous:
Appuyer sur le bouton vous permet de choisir parmi un lien (ou d’ajouter le vôtre)
Je suis sûr que vous conviendrez qu’avoir un contrôle comme celui-ci est incroyablement pratique si vous voulez donner aux éditeurs de votre site la possibilité d’ajouter un lien et également de rechercher WordPress pour ses liens internes, plutôt que de devoir couper et coller les liens dans un lien champ.
Présentation / Historique
Pour ceux qui ne sont pas au courant, une méta-boîte vit sur l’écran de l’éditeur d’un article WordPress et contiendra probablement divers contrôles de formulaire (zones de texte, listes déroulantes, cases à cocher, etc.). Ces commandes permettent aux utilisateurs de votre site Web de modifier facilement un texte personnalisé ou une fonctionnalité sur le site.

Un exemple de boîte méta avec divers contrôles de formulaire
WordPress vous permet de créer des méta-boîtes à l’aide de fonctions (telles que [add_meta_box](https://developer.wordpress.org/reference/functions/add_meta_box/)), mais la création de méta-boîtes de cette manière peut être un processus long, avec beaucoup de répétitions de code (surtout si vous souhaitez utiliser les mêmes contrôles de formulaire dans plusieurs projets).
Pourquoi CMB2 ?
Certains d’entre vous ont peut-être entendu parler de Advanced Custom Fields (ACF) qui fournit une GUI (Graphical User Interface) qui vous permet de créer des méta-boîtes directement avec WordPress.
ACF, à mon avis, n’est pas un excellent outil pour une solution Web évolutive. Le plugin dépend trop des données stockées dans la base de données. Cela provoque des difficultés lors du déploiement de modifications sur un site, car vous ne pouvez pas simplement pousser votre code et voir les modifications instantanément. Au lieu de cela, vous devez refaire le travail sur les différents environnements de déploiement (staging, live, et al). Nous avions donc besoin d’une solution qui nous permette de créer des méta-boîtes par programmation. Entrez CMB2.
Avant d’adopter CMB2, nous utilisions auparavant les HM Custom Meta Boxes de ces charmants humains de Human Made (qui a commencé comme un fork du précurseur de WebDevStudio pour CMB2, ‘Custom Meta Boxes’).
Nous avons adoré les boîtes méta personnalisées HM, et avec les extraits de code les plus simples, nous pouvions rapidement créer des boîtes méta personnalisées pour faire à peu près n’importe quoi !
HM Custom Meta Boxes Markup Example (il s’agit du balisage de la Meta Box Instagram dans la première capture d’écran)
Alors pourquoi le passage au CMB2? Eh bien, HM Custom Meta Boxes ne recevait malheureusement pas beaucoup d’amour (j’ai parlé à son développeur principal et c’est un homme très très occupé), alors que CMB2 avançait avec de nouvelles fonctionnalités, de nouveaux contrôles, et il avait gagné du terrain dans la communauté WordPress avec de nombreuses personnes l’adoptant et publiant des plugins pour l’étendre (dont plusieurs de nos agences partenaires).
Enfin, comme vous l’avez peut-être compris, travailler avec CMB2 est aussi incroyablement simple que nous nous y étions habitués, car les deux plates-formes partagent un ancêtre commun.
Didacticiel
Avant de commencer, chacun a son propre ensemble d’idéaux sur la façon de créer un plugin WordPress, et j’en ai essayé pas mal, cependant le tutoriel sur ‘ Root Composition in WordPress ‘ par Tom J Nowell, a complètement changé ma façon de travailler. Je trouve son approche propre, simple et facilite la maintenance future de tout plugin. Si vous saisissez la source du plugin Link Picker for CMB2, vous pouvez voir les méthodes qu’il enseigne dans la pratique.
Construire le formulaire
Pour créer le formulaire qui affiche le sélecteur de liens, la première chose à faire est de s’accrocher à l’ cmb2_render_[control_name]action. Comme j’ai appelé ce contrôle ‘link_picker’ nous pouvons compléter le hook comme ceci :
<?php
add_action( 'cmb2_render_link_picker', array( $this, 'cmb2_render_link_picker' ), 10, 5 );
`
Pour ceux d’entre vous qui ne comprennent pas vraiment le add_actioncrochet, cela fonctionne comme suit :
- Le premier argument
cmb2_render_link_pickerest le nom du crochet auquel nous voulons nous accrocher. - Le deuxième argument
array( $this, 'cmb2_render_link_picker' )est la fonction que nous voulons appeler lorsque ce crochet s’exécute. Notez que j’enveloppe ceci dans un tableau, avec$thiscomme premier paramètre, car j’appelle la fonction à l’intérieur d’une classe. Si vous ne travaillez pas avec des classes, vous pouvez simplement utiliser le nom de la fonctioncmb2_render_link_picker. - Le
10, est l’ordre dans lequel nous voulons que la fonction se déclenche (plus le nombre est bas, plus tôt elle se déclenche lorsque l’action est appelée). - C’est
5le nombre de paramètres qui seront passés à la fonction que j’appelle (cela deviendra clair sous peu).
Ensuite, nous créons la fonction qui affichera le formulaire :
<?php
public function cmb2_render_link_picker( $field, $value, $object_id, $object_type, $field_type_object) {
…
}
`
J’ai laissé le ‘DocBlock’ dans le code ci-dessus qui décrit ce que fait chacun des paramètres passés dans la cmb2_render_link_picker()fonction.
Notez que ma fonction commence par la publicdéclaration. C’est encore parce que je travaille au sein d’une classe. Si vous ne travaillez pas avec des classes, vous pouvez omettre cela.
La valeur de ce champ est passée à la fonction via le $valueparamètre. Dans le cas de ce champ, nous passerons par un tableau, car notre contrôle comporte trois éléments distincts :
- Le texte
- L’URL
- Si le lien s’ouvre dans une nouvelle fenêtre (ou pas)
Parce que $valuen’est pas toujours défini (par exemple la première fois que le contrôle est rendu), nous devons l’initialiser avec des valeurs par défaut. Nous le faisons avec le morceau de code suivant :
<?php
$value = wp_parse_args(
$value,
array(
'text' => '',
'url' => '',
'blank' => 'false',) );
Nous pouvons ensuite nous mettre au travail pour rendre le formulaire. Voici un exemple du premier contrôle de saisie de texte :
<p>
<label for="<?php echo $field_type_object->_id( '_text' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_text', 'Text') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text',
'name' => $field_type_object->_name( '[text]' ),
'id' => $field_type_object->_id( '_text' ),
'value' => $value['text'],) );
?>
Phew! Ça a l’air un peu brouillon n’est-ce pas? Décomposons-le, ligne par ligne :
- La balise de paragraphe d’ouverture.
- La balise d’étiquette d’ouverture du contrôle, mais avec l’
forattribut automatiquement défini par le$field_type_object_idparamètre. Cela générera automatiquement un ID pour le contrôle lors de son rendu. - Le texte de notre étiquette, construit en utilisant le texte du tableau d’options des contrôles (ou revient au mot ‘Texte’).
- La balise d’étiquette de fermeture
- La balise de paragraphe fermant.
- Démarrer la déclaration PHP
- Utilisez un contrôle d’entrée (partie de
$field_type_objectpour créer une entrée de formulaire (le type par défaut sera du texte). - Démarrer le tableau de paramètres
- Définissez la classe de l’entrée.
- Définissez le nom de l’entrée, à nouveau à l’aide de l’
$field_type_objectassistant. - Définissez l’ID de l’entrée sur le même ID que celui défini sur l’étiquette de l’étiquette.
- Obtenez la valeur de
$value, car il s’agit d’un tableau, nous voulons la clé ‘texte’ pour ce contrôle. - Fermez le tableau.
- Fermez la fonction d’entrée.
- Fermez la déclaration PHP.
Le balisage du champ de formulaire URL est sensiblement le même, seulement pour utiliser les types d’entrée HTML5, nous pouvons définir un paramètre supplémentaire de ‘type’ sur ‘url’ :
<?php
…
'type' => 'url',
…
Enfin, nous voulons implémenter une liste déroulante. Le balisage est très familier :
<?php
echo $field_type_object->select(
array(
'class' => 'cmb_dropdown',
'name' => $field_type_object->_name( '[blank]' ),
'id' => $field_type_object->_id( '_blank' ),
'options' => $blank_options,) );
Notez que la $field_type_objectfonction que nous utilisons est selectde générer une liste déroulante. Notez également qu’à la ligne 6, nous avons un nouvel attribut de options. Dans cela, nous passons une chaîne d »options’. Ceci est généré avant ce contrôle comme suit :
<?php
$blank_options = '';
$blank_options .= '<option value="false" '. selected( $value['blank'], 'false', false) .'>Opens in same</option>';
$blank_options .= '<option value="true" '. selected( $value['blank'], 'true', false) .'>Opens in new</option>';
Ensuite, tout ce que nous avons à faire est de l’envelopper dans quelques <div>et nous avons notre contrôle entièrement rendu :
<?php
public function cmb2_render_link_picker( $field, $value, $object_id, $object_type, $field_type_object) {
$value = wp_parse_args( $value, array(
'text' => '',
'url' => '',
'blank' => 'false',) );
$blank_options = '';
$blank_options .= '<option value="false" '. selected( $value['blank'], 'false', false) .'>Opens in same</option>';
$blank_options .= '<option value="true" '. selected( $value['blank'], 'true', false) .'>Opens in new</option>';
?>
<div class="link-picker">
<div class="text">
<p>
<label for="<?php echo $field_type_object->_id( '_text' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_text', 'Text') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text',
'name' => $field_type_object->_name( '[text]' ),
'id' => $field_type_object->_id( '_text' ),
'value' => $value['text'],
'desc' => '',) );
?>
</div>
<div class="url">
<p>
<label for="<?php echo $field_type_object->_id( '_url' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_url', 'URL') ); ?>
</label>
</p>
<?php
echo $field_type_object->input(
array(
'class' => 'cmb_text_url',
'name' => $field_type_object->_name( '[url]' ),
'id' => $field_type_object->_id( '_url' ),
'value' => $value['url'],
'type' => 'url',
'desc' => '',) );
?>
</div>
<div class="blank">
<p>
<label for="<?php echo $field_type_object->_id( '_blank' ); ?>'">
<?php echo esc_html( $field_type_object->_text( 'link_picker_blank', 'Window') ); ?>
</label>
</p>
<?php
echo $field_type_object->select(
array(
'class' => 'cmb_checkbox',
'name' => $field_type_object->_name( '[blank]' ),
'id' => $field_type_object->_id( '_blank' ),
'options' => $blank_options,
'desc' => '',) );
?>
</div>
<div class="choose">
<p>
<label>Choose</label>
</p>
<button class="dashicons dashicons-admin-links js-insert-link button button-primary" title="<?php esc_html_e( 'Insert Link', 'cmb' ); ?>">
<span class="screen-reader-text"><?php esc_html_e( 'Choose Link', 'cmb' ); ?></span>
</button>
</div>
</div>
<p class="clear">
<?php echo $field_type_object->_desc();?>
</p>
<?php
}
Et c’est tout! Nous avons fait notre contrôle! CMB2 gère automatiquement toutes les données que nous voulons sauvegarder, donc rien à faire là-bas.
modes
La capture d’écran du contrôle que nous créons (en haut de cet article) comporte quelques styles personnalisés qui lui sont appliqués afin qu’il s’affiche en ligne. Je n’entrerai pas dans la façon de styliser le formulaire aujourd’hui, mais si vous êtes curieux, vous pouvez télécharger le plugin et afficher la source.
Rendre le contrôle répétable
Pour ceux d’entre vous qui veulent aller un peu plus loin, vous pouvez faire fonctionner le contrôle avec les régions répétables de CMB2. Pour ce faire, vous devez faire un peu de mappage de tableau. Pour ce faire, utilisez le code ci-dessous :
<?php
public function cmb2_sanitize_link_picker( $check, $meta_value, $object_id, $field_args, $sanitize_object) {
if (! is_array( $meta_value) ||! $field_args['repeatable']) {
return $check;
}
foreach ($meta_value as $key => $val) {
$meta_value[ $key ] = null;
if(! empty( $val['url'])) {
$meta_value[ $key ] = array_map( 'sanitize_text_field', $val );
}
}
return $meta_value;
}
public function cmb2_types_esc_link_picker( $check, $meta_value, $field_args, $field_object) {
if (! is_array( $meta_value) ||! $field_args['repeatable']) {
return $check;
}
foreach ($meta_value as $key => $val) {
$meta_value[ $key ] = null;
if(! empty( $val['url'])) {
$meta_value[ $key ] = array_map( 'esc_attr', $val );
}
}
return $meta_value;
}
Choisir un lien
Bien sûr, l’intérêt du sélecteur de liens est de s’intégrer à la fonctionnalité de choix de liens de WordPress, en faisant apparaître l’écran de dialogue "Insérer/modifier le lien" lorsque le bouton "Choisir" est cliqué.
Pour ce faire, nous nous appuyons fortement sur JavaScript. En particulier, j’utilise jQuery pour faire bouger les choses.
Avant de vous montrer le JavaScript qui lance la boîte de dialogue, nous devons d’abord mettre en file d’attente le propre JavaScript interne de WordPress, qui préchargera les modaux et les bibliothèques dont notre code dépend. Ça ressemble un peu à ça :
<?php
global $post_id;
if (isset( $post_id)) {
wp_enqueue_media( array( 'post' => $post_id) );
}
$plugin_js_url = plugins_url( 'js/plugin.js', ROOT );
wp_enqueue_script( 'wholesomecode', $plugin_js_url, array( 'jquery', 'jquery-ui-core', 'jquery-ui-draggable', 'jquery-ui-droppable', 'thickbox', 'wpdialogs' ), '1.0.0', true );
Comme vous pouvez le voir, de nombreuses bibliothèques WordPress internes s’appuient sur jQuery pour charger la fenêtre contextuelle, il est donc logique que notre déclencheur de fenêtre contextuelle fasse de même. Cela se fait via le /js/plugin.jsqui est chargé à la ligne 10 de l’exemple ci-dessus.
jQuery(document).ready(function($) {
var url = $('body');
var text = $('body');
var blank = $('body');
$('body').on('click', '.js-insert-link', function(event) {
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
url = $(this).closest('.link-picker').find('input.cmb_text_url ');
text = $(this).closest('.link-picker').find('input.cmb_text ');
blank = $(this).closest('.link-picker').find('input.cmb_checkbox ');
wpActiveEditor = true;
wpLink.open();
wpLink.textarea = url;
return false;
});
$('body').on('click', '#wp-link-cancel, #wp-link-backdrop, #wp-link-close', function(event) {
wpLink.textarea = url;
wpLink.close();
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
return false;
});
$('body').on('click', '#wp-link-submit', function(event) {
console.log(text)
var linkAtts = wpLink.getAttrs();
linkAtts.text = $('#wp-link-text').val();
url.val(linkAtts.href);
if( linkAtts.text != '') {
text.val(linkAtts.text);
}
if (linkAtts.target == '_blank') {
blank.prop('checked', true);
} else {
blank.prop('checked', false);
}
wpLink.textarea = url;
wpLink.close();
event.preventDefault? event.preventDefault(): event.returnValue = false;
event.stopPropagation();
return false;
});
});
En utilisant les classes que nous avons enroulées autour de nos contrôles de formulaire, le JavaScript cible les contrôles et pousse le résultat sélectionné à partir de la fenêtre contextuelle du sélecteur de liens dans les champs de contrôle pertinents.
Utilisation du contrôle
Ainsi, après avoir parcouru le didacticiel ci-dessus, et éventuellement après avoir examiné le code source du plugin Link Picker for CMB2, ou simplement téléchargé ma version, vous vous demandez peut-être maintenant comment utiliser la chose avec CMB2. Eh bien, rien de plus simple :
<?php
function wholesomecode_create_meta_boxes() {
$prefix = '_profile_';
$cmb = new_cmb2_box(
array(
'id' => 'cta',
'title' => __( 'Call to Action', 'cmb2' ),
'object_types' => array( 'profile' ),
'context' => 'normal',
'priority' => 'low',
'show_names' => true,) );
$field1 = $cmb->add_field(
array(
'name' => __( 'Link Picker', 'cmb2' ),
'id' => $prefix. 'cta_link',
'type' => 'link_picker',) );
}
add_action( 'cmb2_admin_init', 'wholesomecode_create_meta_boxes' );