Cómo ordenar las publicaciones dentro de una taxonomía de WordPress
Publicado: 2020-12-18Hasta el momento Nelio ha publicado varios plugins en el repositorio de WordPress.org. Algunos de ellos son totalmente gratuitos y ofrecen elegantes soluciones a problemas habituales, como por ejemplo Nelio Maps, que te permite insertar un mapa de Google en tus páginas o posts creados con Gutenberg, y Nelio Compara Imágenes, que te permite insertar un bloque para, como sugiere su nombre, comparar dos imágenes una al lado de la otra. También publicamos un par de plugins premium que nos ayudan a pagar las facturas: Nelio Content y Nelio A/B Testing.
Si echa un vistazo a nuestro sitio web, verá que nuestros dos complementos premium ocupan un lugar destacado, ya que ambos tienen algunas páginas de destino y precios y ambos tienen una base de conocimiento relacionada donde los usuarios pueden encontrar la respuesta a sus preguntas. Hoy hablaremos de cómo creamos las páginas de documentación para Nelio A/B Testing y Nelio Content y lo que tuvimos que hacer para ordenar las preguntas a nuestro antojo.
Cómo crear tipos de publicaciones personalizadas en WordPress
Primero, creemos un tipo de publicación personalizada para realizar un seguimiento de todas las preguntas que contendrá nuestra base de conocimientos. Puedes ver el resultado en la siguiente captura de pantalla, donde hay una instancia de este nuevo tipo de publicación para la base de conocimiento de Nelio A/B Testing:

Para crear un tipo de publicación personalizado, todo lo que tiene que hacer es usar la función register_post_type de WordPress de la siguiente manera:
function nelio_add_help_type() { $labels = array( 'name' => __( 'Questions', 'nelio-help' ), 'singular_name' => __( 'Question', 'nelio-help' ), 'menu_name' => __( 'Testing's Help', 'nelio-help' ), 'all_items' => __( 'All Questions', 'nelio_help' ), 'add_new_item' => __( 'Add New Question', 'nelio_help' ), 'edit_item' => __( 'Edit Question', 'nelio_help' ), 'view_item' => __( 'View Question', 'nelio_help' ), ); register_post_type( 'nab_help', array( 'capability_type' => 'post', 'labels' => $labels, 'map_meta_cap' => true, 'menu_icon' => 'dashicons-welcome-learn-more', 'public' => true, 'show_in_rest' => true, 'supports' => [ 'title', 'editor', 'author' ], ) ); } add_action( 'init', 'nelio_add_help_type' );y ¡voilá! Es posible que ya sepa que hay complementos como Campos personalizados avanzados que lo ayudan a crear y personalizar la interfaz de tipos de publicaciones personalizadas, pero no lo recomiendo necesariamente para implementar un ejemplo tan simple.
Por cierto, si te preguntas dónde debería colocarse este fragmento, no te pierdas nuestro tutorial sobre cómo personalizar WordPress con complementos personalizados.
Cómo crear una nueva taxonomía para organizar el contenido
Nuestra base de conocimientos está organizada en “Temas” y “Palabras clave”, como puedes ver en la siguiente captura de pantalla:

Los temas y las palabras clave son el equivalente de las categorías y las etiquetas que tiene por defecto en las publicaciones de WordPress. Centrémonos en lo primero, ya que lo segundo es exactamente lo mismo.
Como ya puede imaginar, un tema es una taxonomía personalizada que hemos creado para nuestro tipo de contenido personalizado. Para agregar una nueva taxonomía a un tipo de publicación de WordPress, simplemente debe usar la función register_taxonomy de la siguiente manera:
function nelio_add_help_taxonomy() { $labels = array( 'name' => __( 'Topics', 'nelio-help' ), 'singular_name' => __( 'Topic', 'nelio-help' ), 'menu_name' => __( 'Topics', 'nelio-help' ), 'all_items' => __( 'All Topics', 'nelio_help' ), /* ... */ ); register_taxonomy( 'nab_topic', [ 'nab_help' ], array( 'hierarchical' => true, 'label' => __( 'Topic', 'nelio-help' ), 'query_var' => true, 'show_admin_column' => false, 'show_ui' => true, 'show_in_rest' => true, ) ); } add_action( 'init', 'nelio_add_help_taxonomy' ); La función es bastante sencilla de usar. Primero, nombra la taxonomía ( nab_topic en nuestro ejemplo). Luego, especifica el tipo de publicación con la que está relacionada (solo tenemos una: nab_help ). Finalmente, agrega algunos argumentos más para personalizar la taxonomía. El resultado es una nueva taxonomía con la que crear los Temas concretos que necesitamos para categorizar las preguntas en nuestra base de conocimiento:

Cómo ordenar las publicaciones en una taxonomía
Ahora que tenemos una taxonomía personalizada y podemos agregar preguntas a cada tema que creamos, ¿cómo clasificamos esas preguntas dentro de un tema específico? Bueno, me temo que tendremos que codificar un poco...
Echando un vistazo a la base de datos
WordPress y sus relaciones con tus publicaciones se almacenan en cuatro tablas de bases de datos diferentes:
-
wp_termsincluye todos los términos que crea dentro de una determinada taxonomía. Por ejemplo, en nuestra taxonomía de temas , tenemos términos como Preguntas generales , Compatibilidad y Pagos y facturación . -
wp_termmetanos ayuda a almacenar datos adicionales relacionados con cada término. Es bastante similar awp_postmeta. -
wp_term_taxonomyrelaciona cada término enwp_termscon su taxonomía definitoria. Así, por ejemplo, esta tabla realiza un seguimiento del hecho de que el término Pregunta general es en realidad una instancia de la taxonomíanab_topic. -
wp_term_relationshipsrelaciona cada término (term_taxonomy_id) con las publicaciones que "contiene" (object_id). Por ejemplo, esta tabla nos dice que la pregunta sobre qué es Nelio A/B Testing es una Pregunta General (y por eso aparece la pregunta al navegar por el tema).
WordPress 2.5 agregó un nuevo campo numérico en la tabla wp_term_relationships , term_order , que nos permite especificar la posición que ocupa un determinado elemento dentro de la taxonomía. Esto suena como el candidato que buscábamos para ordenar nuestras preguntas dentro de nuestro tema... pero hay un problema: por lo que sé, WordPress no ofrece un mecanismo estándar para (1) definir el valor del campo term_order y ( 2) usarlo para ordenar las publicaciones dentro de una determinada taxonomía. ¡Arreglemos esto!
Cómo usar el campo term_order para ordenar las publicaciones incluidas en una taxonomía
Supongamos que pudiéramos establecer de alguna manera los valores deseados term_order que queremos, como puede ver en la siguiente captura de pantalla:

De forma predeterminada, WordPress ignora este campo y las publicaciones no se ordenan en el front-end. Es por eso que las tres preguntas que se muestran en la captura de pantalla anterior no están ordenadas correctamente.
Afortunadamente, podemos decirle fácilmente a WordPress que use el campo para ordenar las publicaciones por él. Simplemente use el filtro posts_orderby para modificar la consulta que recupera las publicaciones que pertenecen a un determinado término para que incluya una solicitud de clasificación:
add_filter( 'posts_orderby', 'nelio_sort_questions_in_topic', 99, 2 ); function nelio_sort_questions_in_topic( $orderby, $query ) { if ( ! nelio_is_topic_tax_query( $query ) ) return; global $wpdb; return "{$wpdb->term_relationships}.term_order ASC"; } function nelio_is_topic_tax_query( $query ) { if ( empty( $query->tax_query ) ) return; if ( empty( $query->tax_query->queries ) ) return; return in_array( $query->tax_query->queries[0]['taxonomy'], [ 'nab_topic' ], true ); } Todo lo que hace el fragmento anterior es verificar que estamos consultando una determinada taxonomía (en nuestro caso, nab_topic ) y, cuando lo hacemos, agregamos una cláusula ORDER BY en la consulta para que el resultado final esté ordenado por el valor de la Atributo term_order que se encuentra en la $wpdb->term_relationships :


Implementación de una interfaz de usuario para ordenar los contenidos de una taxonomía
Finalmente, solo nos queda una cosa por hacer. En la sección anterior, asumimos que de alguna manera podíamos establecer el valor correcto en el campo term_order . ¿Pero cómo? ¿Hay una pantalla u opción en algún lugar para establecer este valor de una manera fácil de usar? Que yo sepa, no lo hay. Pero podemos crear uno.
En mi opinión, la mejor solución a este problema sería tener una interfaz de usuario que me permita seleccionar la taxonomía que quiero ordenar y luego arrastrar y soltar todas las preguntas para establecer su orden. Algo como esto, ve:

Para lograr el efecto deseado debemos implementar los siguientes pasos:
- Registre una nueva página en el tablero de WordPress
- Implemente la función que representará el selector de taxonomía y las publicaciones dentro de la taxonomía seleccionada
- Agregue un pequeño fragmento de JavaScript para que la interfaz de usuario responda
- Cree una nueva devolución de llamada para guardar el orden resultante una vez que hayamos terminado de ordenar las publicaciones.
Puede hacer que esta interfaz sea tan complicada como desee, utilizando la nueva pila de desarrollo de WordPress. Pero hoy voy a delinear una solución rápida y sucia que hace el trabajo.
Como decía, lo primero es dar de alta la página donde pondremos la interfaz. Podemos hacer esto fácilmente con la función add_submenu_page durante la acción admin_menu :
add_action( 'admin_menu', function() { add_submenu_page( 'edit.php?post_type=nab_help', 'Sort', 'Sort', 'edit_others_posts', 'nab-help-question-sorter', __NAMESPACE__ . '\render_question_sorter' ) } ); El método render_question_sorter también es extremadamente simple:
function render_question_sorter() { echo '<div class="wrap"><h1>Sort Questions</h1>'; $terms = get_terms( 'nab_topic' ); render_select( $terms ); foreach ( $terms as $term ) { render_questions_in_term( $term ); } render_script(); echo '</div>'; } Como puede ver, simplemente recuperamos todos los términos en la taxonomía nab_topic y luego confiamos en tres funciones auxiliares para (1) representar el selector, (2) representar las preguntas en cada categoría y (3) agregar un pequeño script para hacer todo. dinámica.
Representar el selector es tan fácil como iterar sobre cada término y representar una option :
function render_select( $terms ) { echo '<select>'; foreach ( $terms as $term ) { printf( '<option value="%s">%s</option>', esc_attr( $term->slug ), esc_html( $term->name ) ); } echo '</select>'; }Para representar las preguntas que tenemos en cada término de la taxonomía, lanzamos una consulta a la base de datos e iteramos a través de todas ellas:
function render_questions_in_term( $term ) { printf( '<div class="term-block" data-term->', esc_attr( $term->slug ), esc_attr( $term->term_id ) ); echo '<div class="sortable">' $query = new WP_Query( array( 'post_type' => 'nab_help', 'post_per_page' => -1, 'tax_query' => array( array( 'taxonomy' => 'nab_topic', 'field' => 'term_id', 'terms' => $term->term_id, 'orderby' => 'term_order', ), ), ) ); while ( $query->have_posts() ) { $query->the_post(); global $post; printf( '<div class="question" data-question->%s</div>', esc_attr( $post->ID ), esc_html( $post->post_title ) ); } echo '</div>'; printf( '<input type="button" data-term- value="%s" />', esc_attr( $term->term_id ), esc_attr( 'Save' ) ); echo '</div>'; } Tenga en cuenta que esta función genera un div que usa el slug del $term actual como su ID, agrupa las preguntas en otro elemento div y finalmente incluye un botón Guardar . Todos estos detalles serán útiles cuando finalmente rendericemos el script que conecta nuestra aplicación.
Una vez que todo esté listo, simplemente necesitamos agregar algo de magia de JavaScript. En particular, esta interfaz de usuario rápida y sucia requiere dos scripts ( jquery y jquery-ui ) para implementar la funcionalidad de arrastrar y soltar y luego el siguiente fragmento de código JS:
( function() { // MAKE QUESTIONS SORTABLE $( '.sortable' ).sortable(); // TERM SELECTOR const select = document.getElementById( 'topic' ); const termBlocks = [ ...document.querySelectorAll( '.term-block' ) ]; select.addEventListener( 'change', showSelectedTermBlock ); // Helper function function showSelectedTermBlock() { /* ... */ } // SAVE BUTTONS [ ...document.querySelectorAll( '.term-block input[type="button"]' ) ].forEach( addButtonLogic ); // Helper function function addButtonLogic( button ) { const termId = button.getAttribute( 'data-term-id' ); button.addEventListener( 'click', () => { button.disabled = true; const selector = `div[data-term-] .question`; const ids = [ ...document.querySelectorAll( selector ) ] .map( ( question ) => question.getAttribute( 'data-question-id' ); $.ajax( { url: ajaxurl, method: 'POST', data: { action: 'nelio_save_tax_sorting', objectIds: ids, termId, }, } ).always( () => button.disable = false ); } } } )() A primera vista, el fragmento anterior puede parecer complicado. Pero lo prometo: no lo es. Primero, hacemos que nuestras preguntas sean " sortable " mediante el uso de la función clasificable de jQuery UI. Luego, agregamos un oyente a nuestro componente de select que muestra/oculta el conjunto apropiado de preguntas usando una función de ayuda. Finalmente, agregamos los detectores de click a nuestros botones Guardar para que, cuando el usuario haga clic en ellos, el orden en que se ordenan las preguntas se almacene en la base de datos.
Como puede ver, guardar este nuevo pedido se realiza a través de una solicitud AJAX, lo que significa que debemos implementar su contraparte de devolución de llamada de PHP:
add_action( 'wp_ajax_nelio_save_tax_sorting', __NAMESPACE__ . '\save_tax_sorting' ); function save_tax_sorting() { $term_id = isset( $_POST['termId'] ) ? absint( $_POST['termId'] : 0 ); if ( ! $term_id ) die(); $object_ids = isset( $_POST['objectIds'] ) ? $_POST['objectIds'] : []; if ( ! is_array( $object_ids ) || empty( $object_ids ) ) die(); $object_ids = array_values( array_map( 'absint', $object_ids ) ); global $wpdb; foreach ( $object_ids as $order => $object_id ) { $wpdb->update( $wpdb->term_relationships, array( 'term_order' => $order + 1 ), array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id, ), ); } die(); }¡Y eso es! Bastante fácil, ¿eh?
En resumen
WordPress no ofrece un mecanismo predeterminado para ordenar las publicaciones dentro de una determinada taxonomía. ¡Pero eso esta bien! Podemos implementar uno nosotros mismos: todo lo que tenemos que hacer para ordenar las publicaciones en una taxonomía de WordPress es configurar el campo term_order en la tabla de la base de datos term_relationships y luego extender WordPress con el filtro posts_orderby para que use dicho campo.
Finalmente, hemos esbozado una interfaz de usuario simple para completar fácilmente el campo term_order arrastrando y soltando las publicaciones incluidas en una determinada taxonomía.
Espero que les haya gustado este tutorial y si fue así, por favor compártanlo con sus amigos y colegas. Y, como siempre, si tiene alguna pregunta, déjela en la sección de comentarios a continuación y estaré encantado de responderla.
Imagen destacada de Steve Johnson en Unsplash.
