web-dev-qa-db-fra.com

WP_Query orderby champ personnalisé puis post_date dans une requête

Je dois commander les trois premiers articles par un champ personnalisé et les articles suivants par post_date en une requête - par exemple:

  • Poste 4 (field_order: 1)
  • Poste 3 (field_order: 2)
  • Poste 2 (field_order: 3)
  • Poste 1 (field_order: null)
  • Poste 0 (field_order: null)

Post 1 et Post 0 doivent être commandés par post_date.

Je peux facilement commander par champ personnalisé, mais lorsque j'ajoute un article sans le champ personnalisé (Articles 1 et 0), il n'apparaît pas dans la requête.

Ci-dessous la requête que j'essaie actuellement:

    $args = array(
        'post_type' => 'insights',
        'posts_per_page' => '9',
        'meta_query' => array(
            array(
                'key' => 'field_order',
                'value'   => '0',
                'compare' => '>'
            )
        ),
        'orderby' => array(
            'field_order' => 'ASC',
            'post_date' => 'ASC',
        ),
        'order' => 'ASC'
    );
5
londonfed

Plusieurs arguments meta_query avec OR

Vous pouvez utiliser l'argument 'relation' => 'OR' dans meta_query avec deux jeux d'arguments field_order: un avec meta_value >= '0' et l'autre avec NOT EXISTS pour générer la requête principale.

        'meta_query'     => array(
            'relation'   => 'OR',
            field_order' => array(
                'key'       => 'field_order',
                'value'     => '0',
                'compare'   => '>=',
                'type'      => 'NUMERIC'
            ),  
            'field_order_withnulls' => array(
                'key'       => 'field_order',
                'compare'   => 'NOT EXISTS',
                'type'      => 'NUMERIC'
            )
        )

Bon ordre par

Nous pouvons utiliser 'field_order_withnulls' => 'ASC'; cependant, en raison de la LEFT JOIN, il y aura des lignes avec des valeurs nullfield_order et celles précédant des valeurs numériques field_order dans ASC orderby.

Pour résoudre ce problème, nous allons utiliser l'astuce ORDER BY -field_order DESC comme décrit ici .

Cela corrigera l'ordre en inversant DESC en ASC, mais en conservant les lignes avec les valeurs null après les valeurs numériques.

Implémentation de l'opérateur - (inverse) dans orderby

Le problème est que WordPress ne fournit aucun moyen direct de définir l'opérateur - (inverse) dans orderby. Nous allons donc introduire un attribut personnalisé WP_Query nommé _inverse_order, puis utiliser le filtre posts_orderby pour le mettre en œuvre.

Exemple de code:

    // posts_orderby filter callback function
    // place this function in theme's functions.php file or in a custom plugin
    function wpse311227_inverse_orderby( $orderby, $query ) {
        remove_filter( 'posts_orderby', 'wpse311227_inverse_orderby', 10, 2 );
        $idx = (int) $query->get( '_inverse_order' ) - 1;
        if( $idx >= 0 ) {
            $orders = preg_split( "/(?<=ASC|DESC),[\s]*/i", $orderby );
            if( $idx < count( $orders ) ) {
                $orders[$idx] = '-' . $orders[$idx];
            }
            return implode( $orders, ', ' );
        }

        return $orderby;
    }


    // adding the posts_orderby filter to implement the custom '_inverse_order' attribute
    // this should be placed just before the WP_Query call
    add_filter( 'posts_orderby', 'wpse311227_inverse_orderby', 10, 2 );
    $args = array(
        'post_type'      => 'insights',
        'posts_per_page' => '9',
        'meta_query'     => array(
            'relation'   => 'OR',
            field_order' => array(
                'key'       => 'field_order',
                'value'     => '0',
                'compare'   => '>=',
                'type'      => 'NUMERIC'
            ),  
            'field_order_withnulls' => array(
                'key'       => 'field_order',
                'compare'   => 'NOT EXISTS',
                'type'      => 'NUMERIC'
            )
        ),
        'orderby' => array(
            'field_order_withnulls' => 'DESC',
            'post_date'             => 'ASC'
        ),
        // this custom attribute is implemented in wpse311227_inverse_orderby() function
        // to correct the ordering by placing a '-' operator
        // value of _inverse_order attribute is the position of the
        // orderby attribute to be be inversed,
        // (position starts with 1)
        // in this case, since: 'field_order_withnulls' => 'DESC'
        // is in position 1 of 'orderby' attribute array, so:
        '_inverse_order'  => 1
    );
    $query = new WP_Query( $args );

Cela produira toutes les publications avec field_order > 0 et les publications qui n'ont pas de métadonnées field_order avec la commande attendue.

Remarque: _ Vous devrez passer une value non vide dans meta_query pour un NOT EXISTS vérifier si la version de WordPress est inférieure à 3.9. Vérifiez cette note du codex :

En raison du bogue n ° 23268, une valeur est requise pour que les comparaisons de NOT EXISTS fonctionnent correctement avant la version 3.9. Vous devez fournir une chaîne pour le paramètre value. Une chaîne vide ou NULL ne fonctionnera pas. Cependant, toute autre chaîne fera l'affaire et n'apparaîtra PAS dans votre code SQL lors de l'utilisation de NOT EXISTS.


Attention: _ Ce WP_Query utilisera deux LEFT JOIN, qui est pas très efficace. Bien que cela ne soit tolérable que pour quelques milliers de messages. J'ai testé avec plus de 15 000 messages et la requête prend environ 0,3 seconde en moyenne. Toutefois, si vous avez des millions, voire des centaines de milliers de messages, vous devrez alors optimiser la requête ou trouver une méthode plus efficace pour obtenir le même résultat.

5
Fayaz