Creación de un control de selección de enlaces CMB2 personalizado para WordPress
En este tutorial, veré cómo puede crear un control personalizado para ampliar la funcionalidad de CMB2 (Custom Meta Boxes 2) de WebDevStudios.
Desarrollo sitios web (y aplicaciones web) con WordPress CMS (Sistema de gestión de contenido), y cuando llega un nuevo proyecto, puede garantizar que habrá un requisito para mí para desarrollar ‘Meta Boxes personalizados’ para permitir que el usuario tenga un control preciso sobre el contenido y el diseño de los sitios.
Detallaré cómo construí el Selector de enlaces de control CMB2 para CMB2 (disponible en todos los buenos repositorios de complementos de WordPress). Una captura de pantalla de la cual se puede ver a continuación.
El control CMB2 ‘Link Picker’ en acción
El Selector de enlaces activa el cuadro de diálogo integrado de WordPress ‘Insertar/editar enlace’ cuando hace clic en el botón ‘Elegir’. Esto se puede ver en la siguiente captura de pantalla:
Presionar el botón le permite elegir entre un enlace (o agregar uno propio)
Estoy seguro de que estará de acuerdo en que tener un control como este es increíblemente útil si desea que los editores de su sitio puedan agregar un enlace y también buscar en WordPress sus enlaces internos, en lugar de tener que cortar y pegar los enlaces en un enlace. campo.
Introducción / Historia
Para aquellos que no lo saben, un meta cuadro vive en la pantalla del editor de una publicación de WordPress, y probablemente contendrá varios controles de formulario (cuadros de texto, listas desplegables, casillas de verificación, etc.). Estos controles permiten a los usuarios de su sitio web cambiar fácilmente un texto personalizado o una funcionalidad en el sitio.

Un ejemplo de un cuadro meta con varios controles de formulario
WordPress le permite crear metacuadros usando funciones (como [add_meta_box](https://developer.wordpress.org/reference/functions/add_meta_box/)), pero crear metacuadros de esta manera puede ser un proceso largo, con mucha repetición de código (especialmente si desea usar los mismos controles de formulario en varios proyectos).
¿Por qué CMB2?
Algunos de ustedes pueden haber oído hablar de Advanced Custom Fields (ACF) que proporciona una GUI (interfaz gráfica de usuario) que le permite crear metaboxes directamente con WordPress.
En mi opinión, ACF no es una gran herramienta para ninguna solución web escalable. El complemento depende demasiado de los datos almacenados en la base de datos. Esto causa dolor al implementar cambios en un sitio, ya que no puede simplemente subir su código y ver los cambios al instante. En su lugar, debe volver a hacer el trabajo en los diversos entornos de implementación (escenario, en vivo, etc.). Así que necesitábamos una solución que nos permitiera crear metaboxes programáticamente. Introduzca CMB2.
Antes de adoptar CMB2, anteriormente usábamos HM Custom Meta Boxes de esos encantadores humanos en Human Made (que comenzó como una bifurcación del precursor de WebDevStudio para CMB2, ‘Custom Meta Boxes’).
Nos encantaban los metaboxes personalizados de HM, y con los fragmentos de código más simples, ¡podíamos crear rápidamente metaboxes personalizados para hacer casi cualquier cosa!
Ejemplo de marcado de metaboxes personalizados de HM (este es el marcado para el metabox de Instagram en la primera captura de pantalla)
Entonces, ¿por qué cambiar a CMB2? Bueno, HM Custom Meta Boxes lamentablemente no estaba recibiendo mucho cariño (hablé con su desarrollador principal y él es un hombre muy, muy ocupado), mientras que CMB2 avanzaba con nuevas características, nuevos controles y había ganado terreno. en la comunidad de WordPress con muchas personas que lo adoptan y lanzan complementos para ampliarlo (incluidas varias de nuestras agencias asociadas).
Finalmente, como se habrá dado cuenta, trabajar con CMB2 es tan increíblemente simple como nos habíamos acostumbrado, ya que ambas plataformas comparten un ancestro común.
Tutorial
Antes de comenzar, todos tienen su propio conjunto de ideales sobre cómo crear un complemento de WordPress, y he probado algunos, sin embargo, el tutorial sobre ‘ Composición raíz en WordPress ‘ de Tom J Nowell, cambió por completo mi forma de trabajar. Encuentro su enfoque limpio, simple, y hace que el mantenimiento futuro de cualquier complemento sea pan comido. Si toma la fuente del complemento Link Picker for CMB2, puede ver los métodos que enseña en la práctica.
Construyendo el formulario
Para crear el formulario que muestra el Selector de enlaces, lo primero que debemos hacer es engancharnos a la cmb2_render_[control_name]acción. Como he llamado a este control ‘link_picker’, podemos completar el gancho así:
<?php
add_action( 'cmb2_render_link_picker', array( $this, 'cmb2_render_link_picker' ), 10, 5 );
`
Para aquellos de ustedes que realmente no entienden el add_actiongancho, funciona de la siguiente manera:
- El primer argumento
cmb2_render_link_pickeres el nombre del gancho al que queremos engancharnos. - El segundo argumento
array( $this, 'cmb2_render_link_picker' )es la función que queremos llamar cuando se ejecuta este gancho. Tenga en cuenta que estoy envolviendo esto en una matriz,$thiscomo el primer parámetro, porque estoy llamando a la función dentro de una clase. Si no está trabajando con clases, puede usar simplemente el nombre de la funcióncmb2_render_link_picker. - El
10, es el orden en que queremos que se active la función (cuanto menor sea el número, antes se activará cuando se llame a la acción). - El
5es la cantidad de parámetros que se pasarán a la función que estoy llamando (esto quedará claro en breve).
A continuación, creamos la función que representará el formulario:
<?php
public function cmb2_render_link_picker( $field, $value, $object_id, $object_type, $field_type_object) {
…
}
`
He dejado el ‘DocBlock’ en el código anterior que describe lo que cmb2_render_link_picker()hace cada uno de los parámetros pasados a la función.
Tenga en cuenta que mi función comienza con la publicdeclaración. Esto nuevamente se debe a que estoy trabajando dentro de una clase. Si no está trabajando con clases, puede omitir esto.
El valor de este campo se pasa a la función a través del $valueparámetro. En el caso de este campo, pasaremos a través de una matriz, ya que nuestro control tiene tres elementos separados:
- El texto
- la dirección URL
- Si el enlace se abre en una nueva ventana (o no)
Debido a $valueque no siempre está configurado (por ejemplo, la primera vez que se representa el control), debemos inicializarlo con algunos valores predeterminados. Hacemos esto con el siguiente bit de código:
<?php
$value = wp_parse_args(
$value,
array(
'text' => '',
'url' => '',
'blank' => 'false',) );
Entonces podemos ponernos a trabajar en la representación del formulario. Aquí hay un ejemplo del primer control de entrada de texto:
<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'],) );
?>
¡Uf! Eso se ve un poco desordenado, ¿no? Vamos a desglosarlo, línea por línea:
- La etiqueta del párrafo de apertura.
- La etiqueta de apertura del control, pero con el
foratributo establecido automáticamente por el$field_type_object_idparámetro. Esto generará automáticamente una ID para el control cuando se represente. - El texto de nuestra etiqueta, creado con el texto de la matriz de opciones de controles (o recurre a la palabra ‘Texto’).
- La etiqueta de la etiqueta de cierre
- La etiqueta del párrafo de cierre.
- Iniciar declaración de PHP
- Use un control de entrada (parte del
$field_type_objectpara crear una entrada de formulario (el tipo predeterminado será texto). - Inicie la matriz de parámetros.
- Establece la clase de la entrada.
- Establezca el nombre de la entrada, nuevamente usando el
$field_type_objectayudante. - Establezca la ID de la entrada en la misma ID que se configuró en la etiqueta.
- Obtenga el valor de
$value, ya que se trata de una matriz, queremos la clave de ‘texto’ para este control. - Cierra la matriz.
- Cierra la función de entrada.
- Cierra la declaración de PHP.
El marcado del campo de formulario de URL es muy similar, solo que para usar tipos de entrada HTML5 podemos establecer un parámetro adicional de ‘tipo’ en ‘url’:
<?php
…
'type' => 'url',
…
Finalmente queremos implementar un menú desplegable. El marcado es muy familiar:
<?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,) );
Tenga en cuenta que la $field_type_objectfunción que estamos usando es selectgenerar un menú desplegable. También tenga en cuenta que en la línea 6 tenemos un nuevo atributo de options. En esto estamos pasando una cadena de ‘opciones’. Esto se genera antes de este control así:
<?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>';
Luego, todo lo que tenemos que hacer es envolverlo en algunos <div>y tenemos nuestro control completamente renderizado:
<?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
}
¡Y eso es! ¡Hemos hecho nuestro control! CMB2 maneja automáticamente todos los datos que queremos guardar, así que no hay nada que hacer allí.
Estilos
La captura de pantalla del control que estamos creando (cerca de la parte superior de esta publicación) tiene algunos estilos personalizados aplicados, por lo que se muestra en línea. No entraré en cómo diseñar el formulario hoy, pero si tiene curiosidad, puede descargar el complemento y ver la fuente.
Hacer que el control sea repetible
Para aquellos de ustedes que quieran avanzar un poco más, pueden hacer que el control funcione con las regiones repetibles de CMB2. Para hacer eso, necesita hacer un poco de mapeo de matriz. Para hacer eso, use el siguiente código:
<?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;
}
Elegir un enlace
Por supuesto, el objetivo del selector de enlaces es integrarse en la función de selección de enlaces de WordPress, haciendo que aparezca la pantalla de diálogo ‘Insertar/editar enlace’ cuando se hace clic en el botón ‘Elegir’.
Para que esto suceda, confiamos en gran medida en JavaScript. En particular, estoy usando jQuery para hacer que las cosas sucedan.
Antes de mostrarle el JavaScript que inicia el cuadro de diálogo, primero debemos poner en cola el propio JavaScript interno de WordPress, que precargará los modales y las bibliotecas de las que depende nuestro código. Eso se parece un poco a esto:
<?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 );
Como puede ver, muchas de las bibliotecas internas de WordPress dependen de jQuery para cargar la ventana emergente, por lo que tiene sentido que nuestro activador de ventana emergente haga lo mismo. Esto se hace a través del /js/plugin.jsque se carga en la línea 10 del ejemplo anterior.
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;
});
});
Usando las clases que envolvimos en nuestros controles de formulario, el JavaScript apunta a los controles y empuja el resultado seleccionado desde la ventana emergente del selector de enlaces a los campos de control relevantes.
Uso del control
Entonces, después de leer el tutorial anterior, y posiblemente después de revisar el código fuente del complemento Link Picker para CMB2, o simplemente descargar mi versión, es posible que ahora se pregunte cómo usar la cosa con CMB2. Bueno, no podría ser más fácil:
<?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' );