Cómo devolver resultados de get_posts() en un orden explícitamente definido
Estoy intentando crear un bucle de posts ordenados explícitamente, por ejemplo:
<?php $args = array(
'include' => '1,3,8,4,12' ); ?>
<?php get_posts( $args ); ?>
Los resultados se ordenan por fecha por defecto, y no hay una opción orderby para devolver los posts en el orden en que fueron ingresados. Ha habido múltiples solicitudes de errores/funcionalidades publicadas sobre esto en Trac, pero hasta ahora sin éxito. He estado revisando los archivos del núcleo un poco pero no he logrado nada con esto.
¿Alguien puede sugerir una solución alternativa para este comportamiento?
Saludos, Dalton

Creo que esta es la forma más rápida de devolver los resultados de un get_posts en un orden definido. Y además, es una solución nativa, sin trucos
<?php
$posts_order = array('1,3,8,4,12');
$args = array(
'post__in' => $posts_order,
'orderby' => 'post__in'
);
get_posts( $args );
?>

Por favor agrega una explicación a tu respuesta: ¿por qué podría eso resolver el problema?

Explica en tu respuesta cuál es la diferencia con la respuesta aceptada que también usa post__in
.

En la respuesta aceptada, @dougal sugirió crear dos funciones: set_include_order() y menu_order_sort(). No hay necesidad de eso. Además, sugirió usar WP_Query en lugar de get_posts, como el autor de la pregunta quería.
Y post__in solo funciona si también incluyes el parámetro 'orderby', como sugerí

@PabloKarzin: si miras la fecha de la pregunta original, este tipo de clasificación no era posible en WP_Query hasta WordPress 3.5. Tu actualización es correcta, pero no era posible en 2011.

excelente respuesta - voto negativo incorrecto - 3.6.1 - simple y fácil - ¡Gracias!

Puedes probar esto:
add_filter('posts_orderby', 'enforce_specific_order');
$posts = get_posts($args);
remove_filter( current_filter(), __FUNCTION__ );
function enforce_specific_order($orderby) {
global $wpdb;
return "FIND_IN_SET(".$wpdb->posts.".ID, '1,3,8,4,12') ASC";
}

De acuerdo, estaba decidido a encontrar una manera de hacer esto, y creo que lo tengo. Esperaba encontrar una solución más simple y evitar tener que usar un nuevo objeto WP_Query, pero está demasiado integrado en cómo funciona el bucle. Primero, tenemos un par de funciones de utilidad:
// Establecer el orden del menú de publicaciones según nuestra lista
function set_include_order(&$query, $list) {
// Mapear el ID de la publicación a su orden en la lista:
$map = array_flip($list);
// Establecer menu_order según la lista
foreach ($query->posts as &$post) {
if (isset($map[$post->ID])) {
$post->menu_order = $map[$post->ID];
}
}
}
// Ordenar publicaciones por $post->menu_order.
function menu_order_sort($a, $b) {
if ($a->menu_order == $b->menu_order) {
return 0;
}
return ($a->menu_order < $b->menu_order) ? -1 : 1;
}
Estas nos permitirán establecer la propiedad menu_order
basada en nuestra propia lista, y luego ordenar las publicaciones en un objeto de consulta según eso.
Así es como consultamos y ordenamos las publicaciones:
$plist = array(21, 43, 8, 44, 12);
$args = array(
'post_type' => 'attachment',
'post_status' => 'any',
'post__in' => $plist
);
// Crear una nueva consulta
$myquery = new WP_Query($args);
// establecer el menu_order
set_include_order($myquery, $plist);
// y ordenar realmente las publicaciones en nuestra consulta
usort($myquery->posts, 'menu_order_sort');
Así que ahora tenemos nuestro propio objeto de consulta, y $myquery->posts
está ordenado según nuestra función personalizada menu_order_sort
. La única parte complicada ahora es que debemos construir nuestro bucle usando nuestro objeto de consulta personalizado:
while($myquery->have_posts()) : $myquery->the_post();
?>
<div><a id="post_id_<?php the_ID(); ?>" class="nb" href="<?php the_permalink(); ?>"><?php the_title(); ?></a> ID de publicación: <?php the_ID(); ?>
</div>
<?php
endwhile;
wp_reset_postdata();
Obviamente, podrías ajustar el código de plantilla del bucle allí.
Esperaba encontrar una solución que no requiriera el uso de un objeto de consulta personalizado, quizás usando query_posts()
y reemplazando la propiedad posts
en la consulta global $wp_query
, pero simplemente no pude hacer que funcionara correctamente. Con un poco más de tiempo para trabajar en ello, podría haber sido factible.
De todos modos, ¿ves si eso te lleva a donde necesitas ir?

Bouy... parece un poco excesivo y no funciona con paginación (ya que ordenas después de la consulta), pero +1 por el gran esfuerzo para resolverlo sin inyectar algo a nivel SQL.

¡Increíble! Definitivamente funciona para posts y responde la pregunta original. Solo hay un inconveniente, que es que necesito devolver adjuntos, no posts. No me di cuenta de que WP-Query() no funciona con adjuntos, he estado usando get_posts()... ¿hay alguna manera fácil de hacer que esto funcione con get_posts()? Voy a seguir trabajando en ello y ver qué puedo lograr.

Actualicé el código para obtener adjuntos. :) Con los adjuntos, debes especificar post_status
como 'any'
, porque normalmente usan el estado inherit
en lugar de publish
, para reflejar el estado de su padre.

Ah, y para las personas que lean esto en el futuro, tengan en cuenta el comentario de @wyrfel: esto no funcionará bien si necesitas paginar los resultados, ya que la clasificación no ocurre dentro de MySQL.

@Dougal: Muchas gracias, tus cambios funcionaron. Afortunadamente no estoy paginando estos resultados, así que estoy 100% listo.

Una última nota: si tienes un gran número de IDs para recuperar, también podrías agregar 'posts_per_page' => -1
al array $args
, para evitar toparte con límites de paginación. En realidad, quizás quieras hacer esto de todos modos, en caso de que el sitio en cuestión tenga su valor predeterminado de posts_per_page configurado muy bajo.

A partir de WordPress 3.5, esta característica ahora está en el núcleo. Puedes ordenar publicaciones explícitamente usando el parámetro "post__in". http://core.trac.wordpress.org/ticket/13729

¿Qué tal si simplemente limpiamos el orderby
con un filtro? Justo antes de consultar tus publicaciones, coloca:
add_filter('posts_orderby', '__return_false');
Luego, después de que tu bucle haya terminado:
remove_filter('posts_orderby', '__return_false');
La razón para eliminar el filtro nuevamente es en caso de que tengas otros bucles en la página (como los de los widgets) que necesiten su ordenamiento explícito normal.

Dougal: En realidad existe una opción orderby=none, pero eso devuelve el orden natural de las publicaciones en la base de datos, esencialmente ordenadas por ID. Lo que busco es una forma de devolver mis publicaciones en el orden especificado en la consulta. No es compatible con WP_Query, así que supongo que estoy buscando una solución alternativa o un truco.

Bueno, rayos. Pensé que una consulta 'include' devolvería los resultados en el orden dado si eliminabas el orderby
. Pero ahora que lo pienso, tiene sentido que MySQL devuelva los resultados en orden natural (está intentando ser eficiente).
