Cómo devolver resultados de get_posts() en un orden explícitamente definido

3 mar 2011, 19:59:57
Vistas: 10K
Votos: 6

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

7
Comentarios

@Dalton, ¿está descartado un plugin de orden por post? Podrías entonces usar 'orderby' => 'menu_order', 'order' => 'ASC'

eileencodes eileencodes
3 mar 2011 20:12:47

En este caso eso no funcionaría - en realidad estoy retornando adjuntos, cada uno con diferentes padres, por lo que menu_order se vuelve irrelevante. ¡Muchas gracias por la respuesta!

Dalton Rooney Dalton Rooney
3 mar 2011 20:20:09

@Dalton, quizás estoy entendiendo mal tu problema, pero en el cuadro de carga de medios, si vas a "galería" puedes reordenar imágenes asignándoles números y luego nuevamente como se sugirió arriba usar 'orderby' => 'menu_order, 'order' => 'ASC'

eileencodes eileencodes
3 mar 2011 20:43:19

Solo una nota al margen: Puedes usar múltiples argumentos con orderby separados por espacio: &order_by=date ID por ejemplo.

kaiser kaiser
3 mar 2011 22:57:22

@eileen.carpenter: El problema en mi caso es que cada adjunto podría teóricamente tener un padre diferente, haciendo irrelevante menu_order. Cada uno de los IDs de adjuntos en mi consulta podría potencialmente tener el mismo menu order. Estoy pasando los posts incluidos mediante un shortcode, así que necesito devolver los adjuntos en el orden dado.

Dalton Rooney Dalton Rooney
4 mar 2011 01:02:39

@kaiser: No creo que eso solucione mi problema aquí, pero ¡es realmente genial y no sabía sobre eso!

Dalton Rooney Dalton Rooney
4 mar 2011 01:03:08

Podrías hacer algo similar a lo que he hecho aquí, solo que usarías un array de IDs en lugar de instrumentos (y sin todo lo demás que no aplica a tu caso).

t31os t31os
4 mar 2011 01:58:40
Mostrar los 2 comentarios restantes
Todas las respuestas a la pregunta 5
7
11

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 ); 

?> 
4 ago 2013 00:09:24
Comentarios

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

fuxia fuxia
4 ago 2013 00:53:18

Es una solución nativa, sin hacks

Pablo S G Pacheco Pablo S G Pacheco
5 ago 2013 18:01:28

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

fuxia fuxia
5 ago 2013 19:39:47

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í

Pablo S G Pacheco Pablo S G Pacheco
5 ago 2013 21:26:42

@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.

Dalton Rooney Dalton Rooney
15 ago 2013 20:36:32

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

Q Studio Q Studio
17 oct 2013 20:32:54

mejor respuesta hasta ahora.

RafaSashi RafaSashi
19 ago 2016 15:43:32
Mostrar los 2 comentarios restantes
0
10

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";
}
4 mar 2011 07:23:46
6

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?

4 mar 2011 19:09:41
Comentarios

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.

wyrfel wyrfel
4 mar 2011 20:28:30

¡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.

Dalton Rooney Dalton Rooney
4 mar 2011 20:42:24

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.

Dougal Campbell Dougal Campbell
4 mar 2011 21:24:59

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 Campbell Dougal Campbell
4 mar 2011 21:41:47

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

Dalton Rooney Dalton Rooney
4 mar 2011 22:33:11

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.

Dougal Campbell Dougal Campbell
5 mar 2011 00:30:31
Mostrar los 1 comentarios restantes
0

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

14 dic 2012 14:18:03
3

¿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.

3 mar 2011 22:48:29
Comentarios

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.

Dalton Rooney Dalton Rooney
4 mar 2011 01:09:23

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).

Dougal Campbell Dougal Campbell
4 mar 2011 16:18:40

Estaba a punto de eliminar esta respuesta mía, pero pensé que podría ser útil mantenerla para documentar el comportamiento del orden natural de la base de datos cuando eliminas el orderby.

Dougal Campbell Dougal Campbell
4 mar 2011 21:45:13