Tipo de publicación personalizada, WP_Query y 'orderby'
Tengo un tipo de publicación personalizada con la siguiente configuración:
$supports = array(
'title'
, 'editor'
, 'thumbnail'
, 'revisions'
, 'page-attributes'
);
$args = array(
'hierarchical' => true
, 'supports' => $supports
[...]
);
register_post_type('myType', $args);
Me gustaría mostrar todas las publicaciones ordenadas como en el área de administración de WordPress (la sangría es para legibilidad):
1,
2,
3,
1, (padre 3)
2, (padre 3)
4
Para ello intenté la siguiente consulta con el tipo de orden establecido en 'menu_order':
$loop = new WP_Query( array(
'post_type' => 'myType'
, 'posts_per_page' => 50
, 'orderby' => 'menu_order'
, 'order' => 'ASC'
));
Desafortunadamente, todas las publicaciones se ordenan por menu_order
, pero exclusivamente por menu_order
, ignorando la relación padre (post_parent
). Así obtengo algo como esto:
1,
1, (padre 3)
2,
2, (padre 3)
3,
4
Cambiar la consulta a 'orderby' => 'parent menu_order'
produce lo siguiente:
1,
2,
3,
4
1, (padre 3)
2, (padre 3)
En general, parece que todo funciona como está diseñado y el valor orderby se traduce directamente al 'Order By' SQL correspondiente.
Pregunta
¿Cuál es la forma más fácil de obtener el orden deseado?
SQL
Supongo que esta es la consulta SQL principal que WordPress crea:
SELECT SQL_CALC_FOUND_ROWS wp_2_posts.ID
FROM wp_2_posts
WHERE 1=1 AND wp_2_posts.post_type = 'inhalt' AND (wp_2_posts.post_status = 'publish' OR wp_2_posts.post_status = 'private')
ORDER BY wp_2_posts.post_parent, wp_2_posts.menu_order ASC LIMIT 0, 50
que luego se complementa con:
SELECT wp_2_posts.*
FROM wp_2_posts
WHERE ID IN (40,42,44,46,48,50,52,54,56,58,60,76,62,65,69,71,74)
SELECT post_id, meta_key, meta_value
FROM wp_2_postmeta
WHERE post_id IN (40,42,44,46,48,50,52,54,56,58,60,62,65,74,69,71,76)
Solución alternativa
Una solución alternativa conocida, pero no una respuesta, es asignar a todas las publicaciones valores de orden más altos y "espaciados", como:
100,
200,
300,
310,
320,
400

Consulta el codex para otras opciones, pero parece que en este caso querrías usar 'parent' como orden principal.
$loop = new WP_Query( array(
'post_type' => 'myType'
, 'posts_per_page' => 50
, 'orderby' => 'parent menu_order'
, 'order' => 'ASC'
));
Esto ordenará principalmente por el elemento padre, con un orden secundario según el menú. Esto debería darte el resultado deseado.

Gracias Eric, pero desafortunadamente esto lleva a 1, 2, 3, 4, 1(padre 3), 2 (padre 3).

Hmm, extraño. ¿Prueba intercambiando los dos? ¿O usando solo parent? No probé esto, solo siguiendo el codex.

+1 Finalmente alguien que descubrió que un espacio permite múltiples argumentos.

@SunnyRed ¿Podrías publicar la cadena de consulta exacta que resulta de eso? (Pista: Debug Bar Plugin + Extensiones).

Gracias Kaiser. Tu sugerencia de la barra de depuración ya hizo que valiera la pena hacer mi pregunta. He añadido el SQL - espero que sea el correcto, ya que hay bastante a primera vista.

Por lo que parece en la sentencia SQL, el orden debería cambiarse a:
'orderby' => 'parent menu_order'
, ya que la sentencia SQL los está intercambiando.

Hola, Erik. Lo siento, esto fue porque intenté diferentes enfoques. WP y el orden SQL están sincronizados.

Hasta donde sé, no hay una solución alternativa a nivel de base de datos para esto. Este es un problema con el que me encuentro con cierta frecuencia, que es cuando necesitas convertir una lista con referencias de estructura en un array ordenado donde los elementos hijos aparecen inmediatamente después de sus padres. Esto se puede lograr en PHP, pero aunque esta solución es bastante compacta, no es exactamente sencilla.
La siguiente solución agrega un filtro al hook the_posts
, que estructura y luego aplana el conjunto de resultados con una función recursiva.
// Agrega los posts hijos de cada nivel a la lista de resultados, en orden
function recursively_flatten_list( $list, &$result ) {
foreach( $list as $node ) {
$result[] = $node['post'];
if( isset( $node['children'] ) )
recursively_flatten_list( $node['children'], $result );
}
}
function my_sort_posts( $posts, $query ) {
// No hacer nada fuera del área de administración. Solo operar en la consulta principal. Solo operar en consultas de páginas.
if( is_admin() || !$query->is_main_query() || $query->get( 'post_type' ) != 'page' )
return;
$refs = $list = array();
// Crea estructura jerárquica en una sola pasada.
// Gracias nuevamente a Nate Weiner:
// http://blog.ideashower.com/post/15147134343/create-a-parent-child-array-structure-in-one-pass
foreach( $posts as $post ) {
$thisref = &$refs[$post->ID];
$thisref['post'] = $post;
if( $post->post_parent == 0)
$list[$post->ID] = &$thisref;
else
$refs[$post->post_parent]['children'][$post->ID] = &$thisref;
}
// Crea una única lista ordenada
$result = array();
recursively_flatten_list( $list, $result );
return $result;
}
add_filter( 'the_posts', 'my_sort_posts', 10, 2 );
He probado esta solución y es lo suficientemente genérica para jerarquías de páginas arbitrarias.
Este código asume que los posts ya están ordenados por menu_order
. Si vas a usar esta solución, asegúrate de cambiar el parámetro orderby
a simplemente "menu_order"
donde hagas la llamada a new WP_Query
.
