web-dev-qa-db-fra.com

Tri aléatoire dans une requête déjà triée

J'ai une requête de travail qui renvoie un ensemble de publications personnalisées commandées par leur ID de package (ASC). Pour une requête avec 9 résultats, cela peut retourner par exemple:

4 messages (ID de poste 1,2,3,4) avec numéro de paquet 1

3 messages (ID de poste 5,6,7) avec le paquet ID 2

2 messages (ID de poste 8,9) avec paquet ID 3

Lorsque ces messages sont affichés, ils sont toujours dans le même ordre. En d'autres termes, ils sont affichés dans l'ordre, depuis l'ID de poste 1 jusqu'à l'ID de poste 9.

Ce que j'essaie de faire est de trier les résultats de chaque sous-ensemble (définis par Package ID) de manière aléatoire. De cette façon, les messages seraient affichés comme tels:

Affichage aléatoire des ID de poste 1 à 4 (par exemple 2,1,4,3)

Affichage aléatoire des identifiants de poste 5 à 7 (p. Ex. 6,5,7)

Affichage aléatoire des identifiants de poste de 8 à 9 (p. Ex. 8,9)

Ainsi, les publications sont toujours groupées par ID de package, mais les publications de chaque package sont affichées de manière aléatoire.

Voici à quoi ressemble ma requête actuelle:

$args = array( 
    'post_type' => 'custom_post_type',
    'post_status' => 'publish',
    'posts_per_page' => 9,
    'meta_key' => 'pkg_id',
    'orderby' => 'pkg_id',
    'order' => 'ASC'
);

Ce que je pensais "pourrait" fonctionner mais ne fonctionne pas:

$args = array( 
    'post_type' => 'custom_post_type',
    'post_status' => 'publish',
    'posts_per_page' => 9,
    'meta_key' => 'pkg_id',
    'orderby' => array( 'pkg_id' => 'ASC', 'Rand' )
);

Toutes les suggestions que je suis complètement perplexe!

6
David Clough

Il n'est pas nécessaire de faire un nombre surchargé de requêtes pour accomplir cela. vous pouvez toujours utiliser seulement une requête, un filtre the_posts et quelques PHP pour trier votre code selon vos besoins.

Je suppose que ceci est une requête personnalisée de ce que j'ai lu dans votre question, afin que nous puissions faire ce qui suit:

  • Premièrement, nous souhaitons introduire un paramètre WP_Query personnalisé afin de pouvoir utiliser ce paramètre en tant que déclencheur de notre filtre the_posts. Nous appellerons ce paramètre n wpse_custom_sort et sa valeur sera true afin de déclencher le filtre.

  • Ensuite, nous allons utiliser le filtre the_posts pour trier les publications en fonction du champ personnalisé, puis trier ces publications de manière aléatoire par champ personnalisé et finalement renvoyer les publications triées.

LE CODE

Juste quelques notes cependant

  • Le code n'est pas testé et nécessite au moins PHP 5.4.

  • J'ai utilisé get_post_meta() pour renvoyer les valeurs de champ personnalisées par publication. Comme vous utilisez ACF, vous devrez peut-être ajuster ce paramètre pour utiliser get_field(). Je ne connais pas bien ACF, vous devrez donc trier cette partie. La logique resterait toujours la même si

  • Rappelez-vous simplement que l'interrogation de champs personnalisés n'entraîne pas de requêtes supplémentaires car ils sont mis en cache. Il s'agit donc d'un moyen très léger et optimisé d'atteindre votre objectif final.

Voici le code de la requête personnalisée. Il vous suffit simplement d'ajouter le paramètre supplémentaire à vos arguments de requête dans votre modèle de page personnalisé.

// Run your query normally
$args = [
    'wpse_custom_sort' => true, // This parameter will trigger or the_posts filter
    // Your query args here
];
$q = new WP_Query( $args );

// Your loop as normal

Maintenant, nous allons lancer notre filtre ( cela va dans functions.php ou de préférence dans un plugin personnalisé )

/**
 * Function to flatten a multidimentional array (for later use)
 * 
 * Credit to zdenko
 * @link https://Gist.github.com/kohnmd/11197713
 */
function flatten_array( $arg ) 
{
    return is_array( $arg ) 
        ? 
        array_reduce( $arg, function ( $c, $a ) 
            { 
                return array_merge( $c, flatten_array( $a ) ); 
            }, [] ) 
        : 
        [$arg];
}

// The the_posts filter
add_filter( 'the_posts', function ( $posts, $q )
{
    // First we need remove our filter
    remove_filter( current_filter(), __FUNCTION__ );

    // Check if our custom parameter is set and is true, if not, bail early
    if ( true !== $q->get( 'wpse_custom_sort' ) )
        return $posts; 

    // Before we do any work, and to avoid WSOD, make sure that the flatten_array function exists
    if ( !function_exists( 'flatten_array' ) )
        return $posts;

    // Our custom parameter is available and true, lets continue
    // Loop through the posts
    $major_array = [];
    foreach ( $posts as $p ) {
        $meta = get_post_meta(
            $p->ID,
            'pkg_id',
            true
        );

        // Bail if we do not have a $meta value, just to be safe
        if ( !$meta )
            continue;

        // Sanitize the value
        $meta = filter_var( $meta, FILTER_SANITIZE_STRING );

        // Lets build our array
        $major_array[$meta][] = $p;
    }

    // Make sure we have a value for $major_array, if not, bail
    if ( !$major_array )
        return $posts;

    // Lets randomize the posts under each custom field
    $sorted = [];
    foreach ( $major_array as $field ) 
        $sorted[] = shuffle( $field );

    // Lets flatten and return the array of posts
    $posts = flatten_array( $sorted );

    return array_values( $posts );
}, 10, 2 );
6
Pieter Goosen

Pas possible avec une seule requête, vous devriez faire 3 requêtes séparées, chacune ciblant les différents "ID de paquet" et classant ces requêtes en tant que Rand.

$args1 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 1, // you will have to check if this is correct
        )
    )
);

$args2 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 2, // you will have to check if this is correct
        )
    )
);

$args3 = array(
    'post_type' => 'custom_post_type',
    'orderby' => 'Rand',
    'meta_query' => array(
        array(
            'key' => 'pkg_id',
            'value' => 3, // you will have to check if this is correct
        )
    )
);

Cela séparera vos articles dans leur "ID de paquet" et ordonnera chaque ensemble comme aléatoire

1
ngearing

Si on utilise:

$args = array( 
    'post_type'      => 'custom_post_type',
    'post_status'    => 'publish',
    'posts_per_page' => 9,
    'meta_key'       => 'pkg_id',
    'orderby'        => array( 'pkg_id' => 'ASC', 'Rand' => 'DESC' )
);

alors nous obtenons la partie commande en tant que:

ORDER BY wp_postmeta.meta_value ASC, Rand() DESC

Cela semble fonctionner, mais le DESC n'est pas nécessaire.

Mais notez que la commande par Rand() ne s’adapte pas bien. La méthode de @PieterGoosen pourrait vous convenir mieux dans ce cas.

1
birgire

Un ajout à Pieter Goosen répond.

J'ai dû changer son code à la fin puisque shuffle ($ field) renvoie un boolen, pas le tableau brassé lui-même. Donc, j'ai eu un tableau de booléens, pas un tableau de (post-) tableaux. Pour obtenir le résultat attendu, j'ai procédé comme suit:

  //Instead of 
    foreach ( $major_array as $field ) 
        //Shuffle returns boolean, so $sorted will be a
        $sorted[] = shuffle( $field );  

 // I went with
    foreach ( $major_array as $field ) {
        shuffle( $field );   // 1. Shuffle $field, which is an array
        $sorted[] = $field;  // Then add this 
    }
0
chrwald