Tri sur la méta valeur mais incluez les posts qui n'en ont pas

37

J'ai modifié la recherche intégrée dans WP à l'aide du pre_get_postsfiltre, ce qui permet à l'utilisateur de trier les publications (y compris un ensemble de types de publications personnalisées) selon différents champs.

Le problème que j'ai cependant est que lorsque je dis à WP de trier par une méta-valeur, il exclut tous les posts qui n'ont pas cette méta-valeur définie. Cela entraîne le changement du nombre de résultats si vous modifiez le tri de "Prix" en "Date", car les "Messages" n'ont pas "Prix" défini mais "Articles".

Ce n'est pas ce que je veux, alors j'aimerais savoir s'il existe un moyen d'inclure TOUS les messages - même ceux qui n'ont pas la méta-valeur sur laquelle je fais le tri - et de mettre ceux qui ne le sont pas.

Je sais comment trier sur plus d'un domaine mais cela ne m'aide pas.

Merci

Il semble que je ne sois pas le seul à poser cette question: façon d’inclure des publications avec et sans certaines méta_key dans les arguments de wp_query? mais il n'y a pas de solution là-bas.

Mise à jour

J'ai essayé la réponse mais je ne suis pas sûr d'avoir bien compris. Voici ce que j'ai maintenant:

<?php
function my_stuff ($qry) {
    $qry->set('meta_query', array(array(
        'key' => 'item_price', 
        'value' => '', 
        'compare' => 'NOT EXISTS'
    )));

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

La méta valeur est un nombre (il est utilisé pour stocker un prix comme son nom l'indique)

Mise à jour 2

J'ai commenté les commandes et tout ce que j'ai à présent est le suivant:

<?php
$qry->set('meta_query', array(array(
    'key' => 'item_price', 
    'value' => '', 
    'compare' => 'NOT EXISTS'
)));

Avec ce code, la requête semble renvoyer toutes les publications qui ne possèdent pas la item_priceclé et aucune des publications qui en disposent. IE le problème est maintenant inversé.

Si j'ajoute également le code de commande, j'obtiens 0 résultats.

Edit: ... trois ans plus tard ... : PI avait à nouveau ce problème. J'ai essayé toutes les réponses données et aucune ne fonctionne. Je ne sais pas pourquoi certaines personnes semblent penser qu'elles travaillent mais elles ne travaillent pas pour moi du moins.

La solution avec laquelle je me suis retrouvé utilise le save_postfiltre - en s'assurant que toutes les publications ont le champ personnalisé que je souhaite trier. C'est un peu agaçant que je sois obligé de le faire, mais si vous le faites tôt, vous n'aurez probablement aucun problème.

Dans ce cas, je construisais un "compteur de vues" sur les messages et je voulais que les utilisateurs puissent trier les messages les plus lus. Encore une fois, les messages qui n’ont jamais été visionnés (je suppose que c’est assez improbable - mais qui restent) ont disparu lors du tri sur le nombre de vues. J'ai ajouté ce morceau de code pour m'assurer que toutes les publications ont un nombre de vues:

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});
Powerbuoy
la source
S'il vous plaît nous montrer votre code. Il est plus facile de répondre.
Kaiser
Tout d' abord: meta_queryet tax_querysont toujours un array( array() )car ils combinent des réseaux multiples. Deuxièmement - comme mentionné dans ma réponse - vous devez utiliser meta_value_numpour les chiffres. Il pourrait également être nécessaire de définir réellement le meta_value_num(voir WP_QueryEntrée de page Codex). Enfin, il ne fait pas de sens orderdans ASC et DESC direction. Ce n'est pas possible. Le délimiteur d'espace ne fonctionne que pour orderbyet vous ne pouvez pas lui dire de trier le premier ASCet le second DESC. C'est à ça que posts_clausessert le filtre.
Kaiser
Et assurez-vous que vos meta_value_numentrées sont de vrais nombres. Vu trop souvent, quelqu'un a dit qu'il s'agissait d'un nombre, mais l'enregistrait en fait sous forme de chaîne dans la base de données.
Kaiser
Merci pour votre aide, je vais essayer ceci et je reviendrai vers vous. La raison pour ASC DESCest pour qu'il trie le meta_valuedans ASCet datedans DESC, pour autant que je peux dire cela fonctionne.
powerbuoy
1
@Howdy_McGee c'est correct. Certains de mes types de publication personnalisés ont cet ensemble de valeurs. Certains non. Et les types de poste intégrés (comme POST et PAGE) ne le font pas. Donc, chaque fois que j'essaie de trier sur ce champ personnalisé, seules les publications AVEC le champ personnalisé apparaissent.
powerbuoy

Réponses:

4

Il y a deux solutions possibles à cela:

1. Tous les messages ont méta

La meilleure solution que j'ai trouvée ici consiste à attribuer à tous les articles / produits un prix de 0. Vous pouvez le faire manuellement ou parcourir tous les articles. Si le prix est vide, mettez-le à jour.

Pour rendre cela gérable à l'avenir, vous pouvez vous accrocher save_postet leur donner une valeur lors de leur première addition (uniquement si elle est vide).

2. Plusieurs requêtes

Vous pouvez exécuter la première requête comme vous le faites et stocker les identifiants des publications renvoyées. Vous pouvez ensuite exécuter une autre requête pour toutes les publications et toutes les commandes, en excluant les ID renvoyés par la première requête.

Vous pouvez ensuite imprimer séparément les deux résultats et vous obtiendrez les résultats souhaités.

Steven Jones
la source
1
Trois ans plus tard, le même problème se save_postposait à nouveau: P devait utiliser la méthode (j'ai mis à jour ma question avec le code que j'ai utilisé).
powerbuoy
10

Easy Peasy, vient de tester 2018, en utilisant actuellement dans la production.

$query->set( 'meta_query', array(
    'relation' => 'OR',
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'EXISTS'
    ),
    array(
        'key' => 'custom_meta_key', 
        'compare' => 'NOT EXISTS'
    )
) );
$query->set( 'orderby', 'meta_value title' ); 

Ceci vérifie tous les éléments avec et sans la clé méta, sans valeur spécifiée. la méta-requête fournit la clé de la commande de manière fiable. Il a été testé. Cependant, je ne sais pas comment cela fonctionnera lorsque la méta-requête utilise plusieurs clés.

Exemple pratique

/**
 * Modifies query before retrieving posts. Sets the 
 * `meta_query` and `orderby` param when no `orderby` 
 * param is set, (default ordering).
 * 
 * @param   WP_Query  $query  The full `WP_Query` object.
 * @return  void
 */
function example_post_ordering( $query ) {

    // if not in wp-admin, 
    // and the query is the main query, 
    // and the query is not a singular query, 
    // and the query does not have an orderby param set...
    // Note: check for post types, etc. here as desired.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) ) {

        // Setting just `meta_key` is not sufficient, as this 
        // will ignore posts that do not yet, or never will have 
        // a value for the specified key. This meta query will 
        // register the `meta_key` for ordering, but will not 
        // ignore those posts without a value for this key.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            )
        ) );

        // Order by the meta value, then by the title if multiple 
        // posts share the same value for the provided meta key.
        // Use `meta_value_num` if the meta values are numeric.
        $query->set( 'orderby', 'meta_value title' );
    }

}

add_action( 'pre_get_posts', 'example_post_ordering', 10 );

Cela classera les publications par custom_meta_keydéfaut et n'ignorera pas les publications sans valeur pour cette clé.

noahmason
la source
Juste à la lecture du code, tout ce qui semble faire est d' obtenir des postes qui ont custom_meta_keyet obtenir des postes whick n'ont . N'hésitez pas à inclure un exemple de travail réel avec le tri. custom_meta_key
powerbuoy
1
Vous avez raison, c’est tout ce que vous faites, mais la ligne ci-dessous est responsable de la commande par meta_value (de la méta-clé interrogée). $query->set( 'orderby', 'meta_value title' );(Classer par valeur méta, puis par titre lorsque plusieurs publications ont la même valeur pour la clé méta). Cela devrait être fait dans le pre_get_postscrochet, en utilisant la $queryvariable passée . Gardez à l'esprit que la question posée était comment commander par méta-valeur sans ignorer les publications qui n'ont pas de valeur pour cette méta-clé.
noahmason
@powerbuoy Voir exemple pratique mis à jour
noahmason
Très bien, je vais essayer la prochaine fois que je serai confronté à ce problème.
powerbuoy
1
Travaillé pour moi dans un get_posts()appel personnalisé pour déplacer les messages avec _featuredméta vers le haut, puis ordre par date après cela. Merci!
Natebeaty
8

Cette méthode renverra tous les messages, y compris ceux avec et sans la demande meta_key, mais fera des choses étranges lors de la commande.

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Matches to this meta_query should be added to those matching the 'meta_key' query
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

J'ai trouvé cela en manipulant toutes les réponses différentes à cette question et en analysant le code SQL généré par essais et erreurs. Il semble que la définition des array('meta_query' => array('relation' => 'OR'))sorties soit appropriée LEFT JOINau lieu de INNER JOINcelle nécessaire pour inclure les publications manquant les métadonnées. La spécification NOT EXISTSempêche la WHEREclause de filtrer les publications dépourvues du champ méta. Pour ce particulier WP_Query, le SQL généré est (indentation / nouvelles lignes ajoutées):

SELECT SQL_CALC_FOUND_ROWS
    wp_posts.ID
    FROM wp_posts
    INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
    INNER JOIN wp_postmeta ON wp_posts.ID = wp_postmeta.post_id
    LEFT JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id AND mt1.meta_key = 'item_price')
    WHERE 1=1
    AND ( wp_term_relationships.term_taxonomy_id IN (2) )
    AND wp_posts.post_type = 'post'
    AND (wp_posts.post_status = 'publish'
        OR wp_posts.post_status = 'private')
    AND (wp_postmeta.meta_key = 'item_price'
        -- Oh look, here we give SQL permission to choose a random
        -- row from wp_postmeta when this particular post is missing
        -- 'item_price':
        OR  mt1.post_id IS NULL )
    GROUP BY wp_posts.ID
    ORDER BY wp_postmeta.meta_value,wp_posts.post_date DESC
    LIMIT 0, 10

Le résultat est une liste de tous les posts avec meta_value item_priceet de ceux qui manquent item_price. Tous les messages avec item_priceseront commandés correctement par rapport à l'autre, mais les messages manquants item_priceutilisera une aléatoire autre valeur méta ( par exemple, ce _edit_lastqui semble être 1assez souvent dans ma base de données ou d'autres métadonnées wordpress interne qui est tout à fait arbitraire) pour son wp_postmeta.meta_valueen la ORDER BYclause. Ainsi, alors que cette méthode est proche et peut sembler fonctionner pour certaines données, elle est cassée. Donc, tout ce que je peux dire, c'est que si vos item_pricevaleurs n'entrent pas en conflit avec les champs de méta aléatoires choisis par MySQL pour les publications manquantes item_price, cela pourrait fonctionner correctement pour vous. Si tout ce dont vous avez besoin est une garantie que vos messages avecitem_pricesont correctement ordonnés les uns par rapport aux autres sans tenir compte de l'ordre des autres postes, cela peut aller. Mais je pense que ceci est juste une lacune dans Wordpress. S'il vous plaît, corrigez-moi, j'espère que je me trompe et qu'il existe un moyen de résoudre ce problème ;-).

Il semble que pour le INNER JOIN wp_postmeta, MySQL choisit une ligne aléatoire parmi plusieurs postmetalignes associées à la publication lorsque celle-ci meta_keyest manquante. Du point de vue SQL, nous devons déterminer comment dire à wordpress de générer une sortie ORDER BY mt1.meta_value. Cette colonne est correctement NULLlorsque notre demande meta_keyest manquante, contrairement à wp_postmeta.meta_value. Si nous pouvions le faire, SQL les trierait NULL(entrées manquantes) avant toute autre valeur, ce qui nous donnerait un ordre bien défini: tout d'abord, tous les posts manquant du champ postmeta particulier, deuxièmement, les posts ayant le champ. Mais c’est tout le problème: 'orderby' => 'meta_value'ne peut que se référer à 'meta_key' => 'item_price' et l’aliasé wp_postmetaest toujours un, INNER JOINau lieu de jamais, un LEFT JOINsens wp_postmeta.meta_valueet wp_postmeta.meta_keypeutne jamais être NULL.

Donc, je suppose que je dois dire que ce n'est pas possible avec le script intégré de wordpress WP_Querytel qu'il est maintenant documenté (dans wordpress-3.9.1). Déranger. Donc, si vous avez réellement besoin que cela fonctionne correctement, vous devrez probablement vous connecter à Wordpress ailleurs et modifier directement le code SQL généré .

Binki
la source
Semble très prometteur! Je vais essayer ceci la prochaine fois que j'ai ce problème. J'aimerais vous donner la réponse tout de suite, mais je préférerais confirmer que cela fonctionne pour moi en premier.
powerbuoy
Cela a empêché tout de venir pour moi. Rien n'a montré après la mise en œuvre de cela.
Jake
@Jake Oui même ici. Avait ce problème encore aujourd'hui et essayé ceci. Renvoie 0 résultats.
powerbuoy
Quelle version de Wordpress utilisez-vous? Je pense que ce post décrit comment utiliser une API interne non documentée qui n'est pas prise en charge par wordpress et ne fonctionne donc probablement que si vous êtes sur wordpress-3.9.1 ou s'il n'y a pas trop de versions.
binki
2

Je pense avoir une solution.

Vous pouvez utiliser deux meta_keymessages, un que tous les messages ont (like "_thumbnail_id")et le meta_keyfiltre que vous souhaitez utiliser.

Donc vos arguments:

$qry->set(
    'meta_query',
    array(
        'relation' => 'OR',
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'item_price', 
            'value' => '', 
            'compare' => 'EXISTS'
        )
    )
);

$qry->set('orderby', 'meta_value date'); # Sorting works with meta_value as well as meta_value_num - I've tried both
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
Rafael M31
la source
1
Le problème ici est la comparaison de chaîne vide, supprimez-la et cela fonctionne 'value' => '', également deuxième comparaison devrait être NOT EXISTSet la dernière instruction set n'est pas nécessaire
nodws
2

Le problème rencontré par tout le monde ici est lié à l'ordre des méta-requêtes. Pour trier correctement, vous devez placer la requête "NOT EXISTS" avant la requête "EXISTS".

La raison en est que WordPress utilise la meta_value de la dernière instruction "LEFT JOIN" dans la clause "ORDER BY".

Par exemple:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // this comes first!
        ['key' => 'item_price', 'compare' => 'EXISTS'],
    ],
    'order' => 'DESC',
    'orderby' => 'meta_value_num',
    'post_status' => 'publish',
    'post_type' => 'page',
    'posts_per_page' => 10,
]);
Paul
la source
1

Si cela convient, vous pouvez ajouter une méta-valeur par défaut chaque fois qu'une publication est enregistrée ou mise à jour, si la méta-valeur n'existe pas.

function addDefaultMetaValue($post_id) {
    add_post_meta($post_id, 'item_price', 0, true);
}
add_action('save_post', 'addDefaultMetaValue');

Si vous utilisez un type de message personnalisé, remplacez le add_action('save_post', 'addDefaultMetaValue');par, par add_action('save_post_{post_type}', 'addDefaultMetaValue');exempleadd_action('save_post_product', 'addDefaultMetaValue');

rjpedrosa
la source
1

J'ai eu le problème par moi-même pour les méta-valeurs numériques et a souligné que l'ordre de la requête est également important. Pour moi, la NOT EXISTSrequête doit être la première.

Exemple:

$query->set( 'orderby', 'meta_value_num' );
$query->set( 'meta_query', [
    'relation' => 'OR',
    [ 'key' => 'your_meta_name', 'compare' => 'NOT EXISTS' ],
    [
        'key' => 'your_meta_name',
        'compare' => 'EXISTS',
    ],
] );

Il est également important de ’orderby’définir le paramètre général pour obtenir la bonne direction pour les valeurs numériques ’meta_value_num’. Sinon, vous obtenez des résultats étranges pour les valeurs numériques, par exemple:

1, 2, 20, 21, 3, 4, 5…

Au lieu de:

1, 2, 3, 4, 5… 20, 21

KittMedia
la source
1

J'ai également rencontré un problème similaire et la solution suivante m'a aidé:

$args = array(
'post_type' => 'kosh_products',
'posts_per_page' => -1,
'meta_query' => array(
    'relation' => 'OR',
    'category_sort_order' => array(
        'key' => '_sort_order',
        'compare' => 'EXISTS'
    ),
    'category_sort_order_not_exists' => array(
        'key' => '_sort_order',
        'compare' => 'NOT EXISTS'
    ), 
),
'orderby' => array( 
    'category_sort_order' => 'ASC',
    'date' => 'ASC'
));
$query = new WP_Query( $args );

J'ai trouvé une description sur WordPress Codex avec le titre " 'orderby' 'avec plusieurs' méta_key's ": https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters entrez la description de l'image ici

Ilya Kogan
la source
0

Il y a une orderbyvaleur possible de meta_valuepour cela.

$query = new WP_Query( array ( 
    'meta_key'   => 'your_keys_name',
    'orderby'    => 'meta_value',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'your_meta_key',
         'value'   => '',
         'compare' => 'NOT EXISTS',
         // 'type'    => 'CHAR',
    ) )
) );

Si vous avez des valeurs numériques, utilisez meta_value_numplutôt.

Avertissement: Ce n'est pas testé, mais cela devrait fonctionner. Le point est que vous devez spécifier vos valeurs meta_keyet key. Sinon, vous ne pouvez pas comparer des valeurs inexistantes, ce qui devrait permettre d'interroger les deux types de publication. C'est une sorte de bidouille, mais tant que ça marche ...

kaiser
la source
Merci pour votre réponse, s'il vous plaît vérifier ma question mise à jour, je ne suis pas sûr de vous avoir bien compris.
powerbuoy
Je n'ai toujours pas fait ce travail, donc si vous avez une solution, j'aimerais savoir ce que je fais mal. Aussi, je mets une prime sur SO si vous voulez le réclamer: stackoverflow.com/questions/17016770/…
powerbuoy
1
Deux choses. 'your_keys_name'et 'your_meta_key'si les deux doivent être la même chaîne au lieu d'être distinctes, sinon vous avez mal compris la question. Deuxièmement, j'ai testé cela sur ma configuration locale et exclut tous les posts où la clé existe (via le meta_query), ainsi que tous les posts où la clé est manquante (jusqu'au meta_keybout), ce qui entraîne l'absence d'affichage de posts. Cependant, cette réponse est un pas en avant vers quelque chose que les mots au moins ;-).
binki
1
Oh, curieusement, cette réponse fonctionne si vous ajoutez simplement quelque chose 'relation' => 'OR'à meta_query. Wacky stuff o_o.
binki
@binki Il suffit de déposer une modification de ma question et de modifier les éléments qui, selon vous, devraient être modifiés. Ceci est un site géré par la communauté :)
kaiser
0

Je pense que ce @kaiser essayait de faire était de dire la requête pour renvoyer tous les messages qui ont cette clé méta en appliquant une sorte de mannequin où la condition de ne pas filtrer l' un de ces postes. Donc, si vous savez que toutes les valeurs que vos champs personnalisés peuvent prendre sont x, y, z, vous pouvez dire "WHERE meta_key IN (x, y, z) ", mais vous pouvez éviter ce problème en disant ! = (' ') :

$query = new WP_Query( array ( 
    'orderby'    => 'meta_value_num',
    'order'      => 'DESC',
    'meta_query' => array( array(
         'key'     => 'item_price',
         'value'   => '',
         'compare' => '!=',
    ) )
) );

Également non testé, mais ça vaut le coup d'essayer :-).

Jon
la source
1
Je ne peux pas vraiment tester cela pour le moment, mais je suis presque certain que cela ne renverra que les messages pour lesquels item_price est défini et ce n'est pas ''.
powerbuoy
0

J'ai fini par contourner cela avec un peu de bidouille (IMHO), mais cela a fonctionné à ma place dans mon cas.

Vous pouvez vous connecter aux filtres posts_join_paged et posts_orderby pour mettre à jour les chaînes de jointure et de commande. Cela vous permettra de classer par tout ce que vous voulez, à condition que vous le rejoigniez d'abord plutôt que WP_Query en supposant que le champ doit exister pour ce poste particulier. Vous pouvez ensuite supprimer les commandes meta_key, orderbyet `order de vos arguments WP_Query.

Ci-dessous un exemple. Au sommet de chaque fonction, je devais m'échapper pour certains cas, car cela l'ajouterait à tout ce qui utilise WP_Query. Vous devrez peut-être modifier cela pour répondre à vos besoins particuliers.

La documentation sur ces deux filtres fait cruellement défaut alors ... bonne chance! :)

add_filter('posts_join_paged', 'edit_join', 999, 2);
add_filter('posts_orderby', 'edit_orderby', 999, 2);

/**
 * Edit join
 *
 * @param string $join_paged_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_join($join_paged_statement, $wp_query)
{
    global $wpdb;
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $join_paged_statement;
    }

    $join_to_add = "
        LEFT JOIN {$wpdb->prefix}postmeta AS my_custom_meta_key
            ON ({$wpdb->prefix}posts.ID = my_custom_meta_key.post_id
                AND my_custom_meta_key.meta_key = 'my_custom_meta_key')
    ";

    // Only add if it's not already in there
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Edit orderby
 *
 * @param string $orderby_statement
 * @param WP_Query $wp_query
 * @return string
 */
function edit_orderby($orderby_statement, $wp_query)
{
    if (
        !isset($wp_query->query)
        || $wp_query->is_page
        || $wp_query->is_admin
        || (isset($wp_query->query['post_type']) && $wp_query->query['post_type'] != 'my_custom_post_type')
    ) {
        return $orderby_statement;
    }

    $orderby_statement = "my_custom_meta_key.meta_value DESC";

    return $orderby_statement;
}
Pattes délabrées
la source
Le code fonctionne. Mais la meta_value est traitée comme une chaîne. Donc, 6 est considéré supérieur à 50. Des modifications sont-elles possibles pour les traiter comme des nombres?
Drivingralle
@Drivingralle cast(my_custom_meta_key.meta_value as unsigned) DESCdevrait faire l'affaire ...
dimanche
1
Merci @tfrommen. $orderby_statement = "cast(my_custom_meta_key.meta_value as unsigned) DESC";fonctionne très bien.
Drivingralle
0

Cette solution a fonctionné pour moi:

add_action( 'pre_get_posts', 'orden_portfolio' );
function orden_portfolio( $query ) {

    if( ! is_admin() ) {

        $query->set( 'orderby', 'meta_value_num' );
        $query->set( 'order', 'ASC' );
        $query->set( 'meta_query', [
            'relation' => 'OR',
            [ 
                'key' => 'ce_orden', 
                'compare' => 'NOT EXISTS' ],
            [
                'key' => 'ce_orden',
                'compare' => 'EXISTS',
            ],
        ] );

        return $query;

    }

}

Cependant, cette solution affiche d'abord les enregistrements avec meta_value null. Cette autre solution affiche l’ordre ASC et les valeurs nulles à la fin:

function custom_join($join) {
    global $wpdb;

    if( ! is_admin() ) {
        $join .= $wpdb->prepare(
        ' LEFT JOIN ' . $wpdb->postmeta . ' cpm ON cpm.post_id = ' . $wpdb->posts . '.ID AND cpm.meta_key = %s'
        , 'ce_orden' );
    }

    return $join;
}

add_filter('posts_join','custom_join');

function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        $orderby_statement = "CAST( COALESCE(cpm.meta_value,99999) as SIGNED INTEGER) ASC";
    }

    return $orderby_statement;
}

add_filter('posts_orderby','custom_orderby', 10, 2 ); 
jpussacq
la source