Ordinare per valore meta includendo i post che non ne hanno uno

10 giu 2013, 01:34:49
Visualizzazioni: 41.4K
Voti: 53

Ho modificato la ricerca predefinita di WP utilizzando il filtro pre_get_posts, permettendo all'utente di ordinare i post (inclusi diversi tipi di post personalizzati) per campi differenti.

Il problema che sto riscontrando è che quando chiedo a WP di ordinare per un valore meta, questo esclude tutti i post che non hanno quel valore meta impostato. Ciò fa sì che il numero dei risultati cambi se si passa dall'ordinamento per "Prezzo" a "Data" poiché i "Post" non hanno "Prezzo" impostato mentre gli "Articoli" sì.

Non è questo che voglio, quindi vorrei sapere se c'è un modo per includere TUTTI i post - anche quelli che non hanno il valore meta su cui sto ordinando - e mettere quelli senza valore per ultimi.

So come ordinare per più di un campo ma questo non aiuta.

Grazie

Sembra che non sia l'unico con questa domanda: Come includere post sia con che senza una certa meta_key negli argomenti per wp_query? ma lì non c'è una soluzione.

Aggiornamento

Ho provato la risposta ma non sono sicuro di aver capito correttamente, ecco cosa ho al momento:

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

    $qry->set('orderby', 'meta_value date'); # L'ordinamento funziona sia con meta_value che con meta_value_num - ho provato entrambi
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Il valore meta è un numero (viene utilizzato per memorizzare un prezzo come suggerisce il nome)

Aggiornamento 2

Ho commentato la parte dell'ordinamento e ora ho solo questo:

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

Con questo codice la query sembra restituire tutti i post che non hanno la chiave item_price e nessuno dei post che ce l'hanno. Cioè il problema ora è invertito.

Se aggiungo anche il codice dell'ordinamento ottengo 0 risultati.

Modifica: ...tre anni dopo... :P Ho avuto di nuovo questo problema. Ho provato tutte le risposte date e nessuna funziona. Non so perché alcune persone sembrano pensare che funzionino ma per me non funzionano.

La soluzione che ho trovato alla fine è utilizzare il filtro save_post - assicurandomi che tutti i post abbiano il campo personalizzato su cui voglio ordinare. È un po' fastidioso dover farlo, ma finché lo fai all'inizio probabilmente non avrai problemi.

In questo caso stavo costruendo un "contatore di visualizzazioni" sui post e volevo che gli utenti potessero ordinare per i post più letti. Di nuovo, i post che non sono mai stati visualizzati (immagino sia abbastanza improbabile - ma comunque) scomparivano quando si ordinava per numero di visualizzazioni. Ho aggiunto questo pezzo di codice per assicurarmi che tutti i post abbiano un conteggio delle visualizzazioni:

add_action('save_post', function ($postId) {
    add_post_meta($postId, '_sleek_view_count', 0, true);
});
11
Commenti

Mostraci il tuo codice. Faciliterà la risposta.

kaiser kaiser
10 giu 2013 02:24:14

Prima cosa: meta_query e tax_query sono sempre un array( array() ) poiché combinano più array. Secondo - come menzionato nella mia risposta - devi usare meta_value_num per i numeri. Potrebbe anche essere necessario definire esplicitamente il meta_value_num (vedi la pagina del Codex su WP_Query). Infine, non ha senso usare order in direzione ASC e DESC. Non è possibile. Il delimitatore spazio funziona solo per orderby e non puoi dirgli di ordinare il primo ASC e il secondo DESC. È per questo che esiste il filtro posts_clauses.

kaiser kaiser
10 giu 2013 03:42:38

E assicurati che le tue voci meta_value_num siano veri numeri. L'ho visto spesso che qualcuno afferma che è un numero, ma in realtà lo salva come stringa nel database.

kaiser kaiser
10 giu 2013 03:43:23

Grazie per il tuo aiuto, proverò questa soluzione e ti farò sapere. Il motivo per ASC DESC è ordinare in base a meta_value in ASC e alla data in DESC, per quanto posso vedere funziona.

powerbuoy powerbuoy
10 giu 2013 04:06:12

Inoltre, l'ordinamento effettivo funziona bene. L'unico problema è che i post (o custom post type) senza il meta valore non vengono visualizzati. (Riguardo a meta_value_num)

powerbuoy powerbuoy
10 giu 2013 04:29:58

Per chiarire, vuoi includere sia i post con un determinato meta valore che quelli che non lo hanno impostato?

Howdy_McGee Howdy_McGee
15 apr 2014 21:20:52

@Howdy_McGee è corretto. Alcuni dei miei custom post type HANNO questo valore impostato. Altri no. E i post type predefiniti (come POST e PAGE) non lo hanno. Quindi ogni volta che provo a ordinare per quel campo personalizzato, vengono mostrati solo i post CHE hanno il campo personalizzato.

powerbuoy powerbuoy
15 apr 2014 21:31:23

Ho visto domande simili prima, @G.M. ha una buona risposta, ma devi mostrare tutti i risultati sulla stessa pagina, quindi se non funziona per te, allora mostri solo post con il meta_key o post senza il meta_key, non entrambi. Ecco il post a cui mi riferisco.

Howdy_McGee Howdy_McGee
15 apr 2014 21:35:10

Ah ok grazie. Questo era per un progetto che ho completato da tempo ma lo terrò a mente per la prossima volta. Anche se, preferirei continuare a usare il normale "ciclo WP" con la normale paginazione...

powerbuoy powerbuoy
15 apr 2014 23:38:05

Ho aperto un ticket su trac per aggiungere eventualmente una soluzione alle classi di query core che renderebbero possibile questo tipo di cosa: https://core.trac.wordpress.org/ticket/42907

J.D. J.D.
15 dic 2017 00:11:13

@powerbuoy per favore consulta la mia risposta per una soluzione.

Paul Paul
23 feb 2019 18:36:01
Mostra i restanti 6 commenti
Tutte le risposte alla domanda 17
9
25

Facile facile, testato nel 2018, attualmente utilizzato in produzione.

Aggiornamento 2022: Modificato in modo che la query NOT_EXISTS venga prima della query EXISTS, e chiarisce l'effetto che chiavi multiple hanno sulla clausola orderby.

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

Questo controlla tutti gli elementi con e senza la meta chiave, senza specificare un valore. La meta query fornisce la chiave per l'ordinamento in modo affidabile. È stato testato. La clausola orderby con meta_value/meta_value_num utilizzerà l'ultima chiave nella catena.

Esempio pratico

/**
 * Modifica la query prima di recuperare i post. Imposta i 
 * parametri `meta_query` e `orderby` quando non è impostato 
 * alcun parametro `orderby` (ordinamento predefinito).
 * 
 * @param   WP_Query  $query  L'oggetto completo `WP_Query`.
 * @return  void
 */
function example_post_ordering( $query ) {
    
    // Se non siamo in wp-admin, 
    // e la query è la query principale, 
    // e la query non è una query singola, 
    // e la query non ha un parametro orderby impostato...
    // Nota: controlla i tipi di post, ecc. qui se necessario.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) {
        
        // Impostare solo `meta_key` non è sufficiente, poiché questo 
        // ignorerà i post che non hanno ancora, o non avranno mai, 
        // un valore per la chiave specificata. Questa meta query 
        // registra la `meta_key` per l'ordinamento, ma non 
        // ignora quei post senza un valore per questa chiave.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
        ) );

        // Ordina per il meta valore, poi per il titolo se più 
        // post condividono lo stesso valore per la meta chiave fornita.
        // Usa `meta_value_num` se i meta valori sono numerici.
        $query->set( 'orderby', 'meta_value title' );
    }

}

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

Questo ordinerà i post per custom_meta_key di default, e non ignorerà i post senza un valore per quella chiave.

18 apr 2018 18:23:10
Commenti

Solo leggendo il codice, sembra che faccia semplicemente ottenere i post che hanno custom_meta_key e i post che non hanno custom_meta_key. Sentiti libero di includere un esempio pratico funzionante con l'ordinamento.

powerbuoy powerbuoy
18 apr 2018 20:33:10

Hai ragione, è tutto ciò che sta facendo, ma la riga sottostante è responsabile dell'ordinamento per meta_value (della meta key interrogata). $query->set( 'orderby', 'meta_value title' ); (Ordina per meta value, poi per titolo quando più post hanno lo stesso valore per la meta key). Questo dovrebbe essere fatto nell'hook pre_get_posts, utilizzando la variabile $query passata. Tieni presente che la domanda era come ordinare per meta value, senza ignorare i post che non hanno un valore per quella meta key.

noahmason noahmason
18 apr 2018 21:18:00

@powerbuoy Vedi l'esempio pratico aggiornato

noahmason noahmason
18 apr 2018 21:55:47

Va bene, proverò la prossima volta che mi troverò di fronte a questo problema.

powerbuoy powerbuoy
27 apr 2018 21:13:34

Ha funzionato per me in una chiamata personalizzata get_posts() per spingere i post con meta _featured in cima, ordinando poi per data. Grazie!

Nate Beaty Nate Beaty
9 nov 2018 20:03:22

Questo ha funzionato solo quando ho invertito l'ordine di NOT EXISTS ed EXISTS. Funzionava, ma l'ordinamento veniva ignorato.

mikemike mikemike
3 gen 2021 18:48:42

@mikemike grazie per l'informazione, facendo questa modifica ha funzionato anche per me

bigdaveygeorge bigdaveygeorge
11 gen 2021 19:45:54

@mikemike ricevuto - ho appena applicato l'aggiornamento qui sopra.

noahmason noahmason
9 lug 2022 20:08:50

Stavo proprio affrontando questo problema e questa soluzione ha funzionato perfettamente,

AuRise AuRise
5 lug 2024 17:32:37
Mostra i restanti 4 commenti
4
12

Questo metodo restituirà tutti i post, inclusi quelli con e senza il meta_key richiesto, ma produrrà risultati strani nell'ordinamento.

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Le corrispondenze a questa meta_query verranno aggiunte a quelle che corrispondono alla query 'meta_key'
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # L'ordinamento funziona con meta_value così come con meta_value_num - ho provato entrambi
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Ho scoperto questo sperimentando con tutte le diverse risposte a questa domanda e analizzando l'SQL generato attraverso tentativi ed errori. Sembra che impostare array('meta_query' => array('relation' => 'OR')) produca un appropriato LEFT JOIN invece di un INNER JOIN che è necessario per includere i post mancanti dei metadati. Specificare NOT EXISTS impedisce alla clausola WHERE di filtrare i post privi del campo meta. Per questa particolare WP_Query, l'SQL generato è (sono stati aggiunti rientri/a capo):

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 guarda, qui diamo a SQL il permesso di scegliere una riga
        -- casuale da wp_postmeta quando questo particolare post manca
        -- di '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

Il risultato è un elenco di tutti i post con meta_value di item_price e quelli che mancano di item_price. Tutti i post con item_price saranno ordinati correttamente tra loro, ma i post mancanti di item_price utilizzeranno qualche altro valore meta casuale (ad esempio, _edit_last che spesso sembra essere 1 nel mio database o qualche altro metadato interno di WordPress completamente arbitrario) per il loro wp_postmeta.meta_value nella clausola ORDER BY. Quindi, sebbene questo metodo sia vicino e possa sembrare funzionare per alcuni dati, è difettoso. Quindi, tutto ciò che posso dire è che, se i tuoi valori di item_price non entrano in conflitto con i campi meta casuali che MySQL sceglie per i post mancanti di item_price, questo potrebbe funzionare bene per te. Se tutto ciò di cui hai bisogno è la garanzia che i tuoi post con item_price siano ordinati correttamente tra loro senza riguardo per l'ordinamento degli altri post, potrebbe andare bene. Ma penso che questa sia solo una carenza in WordPress. Correggimi, spero di sbagliarmi e che ci sia un modo per risolvere questo problema ;-).

Sembra che per l'INNER JOIN wp_postmeta, MySQL stia scegliendo una riga casuale tra le multiple righe postmeta associate al post quando il meta_key è mancante dal post specificato. Da una prospettiva SQL, dobbiamo capire come dire a WordPress di produrre ORDER BY mt1.meta_value. Questa colonna è propriamente NULL quando il nostro meta_key richiesto è mancante, a differenza di wp_postmeta.meta_value. Se potessimo fare ciò, SQL ordinerebbe questi NULL (voci mancanti) prima di qualsiasi altro valore, dandoci un ordine ben definito: prima arrivano tutti i post mancanti del particolare campo postmeta, poi arrivano i post che lo possiedono. Ma questo è l'intero problema: 'orderby' => 'meta_value' può riferirsi solo a 'meta_key' => 'item_price' e il wp_postmeta non aliasato è sempre un INNER JOIN invece di un LEFT JOIN, il che significa che wp_postmeta.meta_value e wp_postmeta.meta_key non potranno mai essere NULL.

Quindi suppongo di dover dire che questo non è possibile con il WP_Query integrato di WordPress così com'è documentato attualmente (in wordpress-3.9.1). Fastidioso. Quindi, se hai effettivamente bisogno che questo funzioni correttamente, probabilmente devi agganciarti a WordPress altrove e modificare direttamente l'SQL generato.

31 lug 2014 08:32:41
Commenti

Sembra molto promettente! Proverò questa soluzione la prossima volta che avrò questo problema. Vorrei darti la risposta adesso, ma preferirei prima confermare che funzioni nel mio caso.

powerbuoy powerbuoy
5 ago 2014 19:09:06

Questo ha impedito che qualsiasi cosa venisse visualizzata per me. Non mostrava nulla dopo aver implementato questa soluzione.

Jake Jake
26 ott 2016 14:32:47

@Jake Sì, stesso problema qui. Oggi ho avuto di nuovo questo problema e ho provato questa soluzione. Restituisce 0 risultati.

powerbuoy powerbuoy
7 giu 2017 12:00:52

Che versione di WordPress state utilizzando? Penso che questo post descriva come utilizzare un'API interna e non documentata che non è supportata da WordPress e quindi probabilmente funziona solo se si utilizza WordPress-3.9.1 o versioni non troppo successive.

binki binki
7 giu 2017 17:40:40
0

Il problema che tutti stanno riscontrando qui riguarda l'ordine delle meta query. Per ordinare correttamente, dovrai inserire la query "NOT EXISTS" prima della query "EXISTS".

Il motivo è che WordPress utilizza il meta_value dell'ultima istruzione "LEFT JOIN" nella clausola "ORDER BY".

Ad esempio:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // questo viene per primo!
        ['key' => 'item_price', 'compare' => 'EXISTS'],
    ],
    'order' => 'DESC',
    'orderby' => 'meta_value_num',
    'post_status' => 'publish',
    'post_type' => 'page',
    'posts_per_page' => 10,
]);
23 feb 2019 18:33:29
3

Ci sono due possibili soluzioni a questo problema:

1. Tutti i post hanno il meta

La soluzione migliore che ho trovato qui è assegnare un prezzo di 0 agli altri post/prodotti. Puoi farlo manualmente, o scorrere tutti i post e, se il prezzo è vuoto, aggiornarlo.

Per renderlo gestibile in futuro, puoi agganciarti a save_post e assegnare un valore quando vengono aggiunti per la prima volta (solo se è vuoto).

2. Query multipli

Potresti eseguire la prima query come stai facendo e memorizzare gli ID dei post restituiti. Potresti poi eseguire un'altra query per tutti i post e ordinarli per data, escludendo gli ID restituiti dalla prima query.

Puoi quindi stampare i due risultati separatamente in ordine e otterrai i risultati desiderati.

20 apr 2014 17:01:04
Commenti

Tre anni dopo ho avuto lo stesso problema di nuovo :P Ho dovuto usare il metodo save_post (ho aggiornato la mia domanda con il codice che ho usato).

powerbuoy powerbuoy
7 giu 2017 12:11:42

Questo è possibile senza dover usare hook o eseguire query multiple. Vedi la mia risposta.

Jacob Raccuia Jacob Raccuia
11 lug 2020 07:51:50

Avresti dovuto mostrare esempi di codice.

Steve Moretz Steve Moretz
6 ago 2021 12:50:35
0

Anch'io ho riscontrato un problema simile e la seguente soluzione mi ha aiutato:

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

Ho trovato una descrizione su WordPress Codex con il titolo "'orderby' con multiple 'meta_key'": https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters Ordinamento con più meta_key in WordPress

25 mag 2019 22:24:40
1

Penso di avere una soluzione.

Puoi utilizzare due meta_key, uno che tutti i post hanno (come "_thumbnail_id"), e il meta_key che desideri usare come filtro.

Quindi i tuoi argomenti:

$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'); # L'ordinamento funziona sia con meta_value che con meta_value_num - ho provato entrambi
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
22 ago 2013 21:06:01
Commenti

Il problema qui è il confronto con la stringa vuota, rimuovilo e funzionerà 'value' => '', inoltre il secondo confronto dovrebbe essere NOT EXISTS e l'ultima istruzione di impostazione non è necessaria

nodws nodws
24 apr 2018 18:19:50
0

Ho avuto lo stesso problema con i valori meta numerici e ho notato che l'ordine della query è importante. Nel mio caso, la query NOT EXISTS deve essere la prima.

Esempio:

$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',
    ],
] );

È anche importante impostare il parametro generale ’orderby’ su ’meta_value_num’ per ottenere il giusto ordinamento dei valori numerici. Altrimenti si ottengono risultati strani per i valori numerici, ad esempio:

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

Invece di:

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

12 gen 2019 03:16:08
0

La query meta OR che combina NOT EXISTS e EXISTS funziona ma causava una query lenta.

Questa soluzione funziona molto più velocemente.

La chiave principale era nella clausola SQL order by: order by YOUR_FIELD is NULL, YOUR_FIELD

add_filter('posts_orderby', function (string $orderby, WP_Query $query) {
    if ('MY_CUSTOM_SORT' === $query->get('orderby')) {
        $orderby = "mta.meta_value is NULL, mta.meta_value ='', mta.meta_value ASC";
    }
    return $orderby;
}, 10, 2);

add_filter('posts_join', function (string $join, WP_Query $query) {
    if ('MY_CUSTOM_SORT' === $query->get('orderby')) {
        $join .= " LEFT JOIN wp_postmeta AS mta ON ( wp_posts.ID = mta.post_id AND mta.meta_key = 'SOME_META_KEY')";
    }
    return $join;
}, 10, 2);

E nei tuoi argomenti WP_Query:

$args['orderby'] = 'MY_CUSTOM_SORT';
19 ott 2022 01:26:42
0

Questa è una domanda molto vecchia ma nessuna delle risposte era abbastanza robusta per essere utilizzata e soddisfatta con i risultati. La soluzione qui sotto utilizza ancora WP_Query, ma manipola le istruzioni di join e order by.

Sfrutta una funzione di MySQL chiamata ORDER BY IF, che permette di eseguire efficacemente della logica durante l'istruzione ORDER BY. Questo è importante perché le altre soluzioni qui presenti raggruppano semplicemente i risultati senza valore all'inizio o alla fine della lista, il che è inutile quando si cerca di ordinare per 2 chiavi.

Nell'esempio qui sotto ho un CPT di 'businesses'. Voglio che siano tutti ordinati per titolo, ma quelli con is_premium dovrebbero venire prima. Ho commentato il codice in modo da poter vedere cosa succede in ogni punto.

<?php
$paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
$args = [
    'post_type' => 'business',
    'posts_per_page' => 12,
    'meta_query' => [
        'relation' => 'OR',
        [
            'key' => 'is_premium', 
            'compare' => 'NOT EXISTS' // NOT EXISTS prima è importante
        ],
        [
            'key' => 'is_premium', 
            'compare' => 'EXISTS'
        ],
    ]
    ,
    'orderby' => [
        'meta_value' => 'DESC', // Questo è il campo is_premium
        'title' => 'ASC',
    ],
    'paged' => $paged
];

// Aggiungiamo il nostro join personalizzato alla query
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'
        , 'is_premium' );
    }

    return $join;
}
add_filter('posts_join','custom_join');

// Aggiungiamo il nostro order by personalizzato alla query
function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        // Qui ordiniamo per meta_value SOLO se è 1, altrimenti lo ignoriamo per quella istruzione di ordinamento, il che 
        // significa che verrà ordinato dalla prossima istruzione (titolo), come gli altri risultati
        $orderby_statement = "IF(cpm.meta_value = 1, 1, 0) DESC, wp_posts.post_title ASC ";
    }

    return $orderby_statement;
}
add_filter('posts_orderby','custom_orderby', 10, 2 ); 

// Eseguiamo la query
$business = new WP_Query( $args );

// Rimuoviamo i filtri
remove_filter('posts_orderby','custom_orderby');
remove_filter('posts_join','custom_join');
3 gen 2021 19:41:17
0

Se appropriato, puoi aggiungere un valore meta predefinito ogni volta che un articolo viene salvato o aggiornato, se il valore meta non esiste.

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

Se stai usando un custom post type, sostituisci add_action('save_post', 'addDefaultMetaValue'); con add_action('save_post_{post_type}', 'addDefaultMetaValue'); ad esempio add_action('save_post_product', 'addDefaultMetaValue');

20 ago 2015 19:17:40
6

Esiste un possibile valore orderby di meta_value per questo.

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

Se hai valori numerici, puoi usare meta_value_num invece.

Nota: Questo non è stato testato, ma dovrebbe funzionare. Il punto è che devi specificare i tuoi valori meta_key e key. Altrimenti non puoi confrontare valori non esistenti, il che dovrebbe permettere di interrogare entrambi i tipi di post. È un po' un hack, ma finché funziona...

10 giu 2013 02:38:53
Commenti

Grazie per la tua risposta, per favore controlla la mia domanda aggiornata, non sono sicuro di averti capito correttamente.

powerbuoy powerbuoy
10 giu 2013 03:05:54

Ancora non sono riuscito a far funzionare questo, quindi se hai una soluzione mi piacerebbe sapere cosa sto sbagliando. Inoltre, ho messo una ricompensa su SO se vuoi rivendicarla: http://stackoverflow.com/questions/17016770/wordpress-order-by-meta-value-if-it-exists-else-date/17183618

powerbuoy powerbuoy
20 giu 2013 05:45:54

Due cose. 'your_keys_name' e 'your_meta_key' dovrebbero essere la stessa stringa invece di essere distinte, altrimenti sembra che tu abbia frainteso la domanda. In secondo luogo, ho testato questo sul mio setup locale ed esclude tutti i post dove la chiave esiste (attraverso il meta_query) e esclude anche i post dove la chiave manca (attraverso meta_key), risultando in nessun post visualizzato. Tuttavia, questa risposta è un passo verso qualcosa che funziona almeno ;-).

binki binki
31 lug 2014 07:16:07

Oh, interessante, questa risposta funziona se aggiungi semplicemente 'relation' => 'OR' al meta_query. Roba strana o_o.

binki binki
31 lug 2014 07:18:19

@binki Fai semplicemente una [modifica] alla mia domanda e cambia le parti che pensi dovrebbero essere modificate. Questo è un sito guidato dalla comunità :)

kaiser kaiser
31 lug 2014 12:58:04

@kaiser, ho fatto ulteriori prove, persino dei test, e ho scritto un piccolo monologo. Presumo che la mia nuova risposta sia il posto appropriato per conservare le mie divagazioni ;-).

binki binki
31 lug 2014 16:24:43
Mostra i restanti 1 commenti
0

Questa soluzione ha funzionato per me:

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;

    }

}

Tuttavia, questa soluzione mostra prima i record con meta_value nullo. Quest'altra soluzione mostra l'ordine ASC con i valori nulli alla fine:

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 ); 
28 ott 2019 20:03:50
0

Sono riuscito a risolvere questo problema utilizzando una singola query con gli argomenti appropriati. Per WordPress 4.1+

$args = array( 
    'posts_per_page' => -1, 
    'post_status' => 'publish',
    'meta_query' => array(
        'relation' => 'OR',
        array(
            'key' => 'custom_sort',
            'compare' => 'EXISTS'
        ),
        array(
            'key' => 'custom_sort',
            'compare' => 'NOT EXISTS'
        )
    ),
    'orderby' => 'meta_value_num title',
    'order' => 'ASC',
));

$query = new WP_Query($args);

Vedi la mia risposta completa qui: https://wordpress.stackexchange.com/a/370841/15209

11 lug 2020 07:51:26
3

Alla fine ho risolto con un piccolo hack (a mio modesto parere), ma ha funzionato nel mio caso.

Puoi agganciarti ai filtri posts_join_paged e posts_orderby per modificare le stringhe di join e ordinamento. Questo ti permetterà di ordinare come preferisci, purché prima effettui il join piuttosto che far assumere a WP_Query che il campo debba esistere per quel particolare post. Puoi quindi rimuovere meta_key, orderby e `order dagli argomenti del tuo WP_Query.

Di seguito un esempio. All'inizio di ogni funzione ho dovuto gestire alcuni casi particolari poiché questo verrà aggiunto a tutto ciò che utilizza WP_Query. Potresti doverlo modificare per adattarlo alle tue esigenze specifiche.

La documentazione su questi due filtri è purtroppo carente... buona fortuna! :)

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

/**
 * Modifica il 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')
    ";

    // Aggiungi solo se non è già presente
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Modifica l'ordinamento
 *
 * @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;
}
15 apr 2014 21:17:20
Commenti

Il codice funziona. Ma meta_value viene gestito come una stringa. Quindi 6 viene valutato più alto di 50. È possibile apportare modifiche per trattarli come numeri?

Drivingralle Drivingralle
24 gen 2017 19:59:03

@Drivingralle cast(my_custom_meta_key.meta_value as unsigned) DESC dovrebbe risolvere il problema...

tfrommen tfrommen
2 feb 2017 23:34:21

Grazie @tfrommen. $orderby_statement = "cast(my_custom_meta_key.meta_value as unsigned) DESC"; funziona perfettamente.

Drivingralle Drivingralle
6 feb 2017 15:22:34
0

Alla fine ho utilizzato il filtro 'get_meta_sql' fornito da WordPress

add_filter( 'get_meta_sql', 'adjust_the_meta_sql' ), 10, 2 );

La funzione si presenta così

function adjust_the_meta_sql( $sql, $queries ) {
            if ( 'my_post_type' === filter_input( INPUT_GET, 'post_type' )
                && '_my_key' === $queries[0]['key'] ) {
                $sql['join']  = preg_replace( '/INNER JOIN wp_postmeta ON \(([^)]+)\)/', 'LEFT JOIN wp_postmeta ON ($1 ' . $sql['where'] . ')', $sql['join'] );
                $sql['where'] = '';
            }
            return $sql;
        }

Risolve due problemi con l'SQL generato da WordPress.

  1. Trasforma l'INNER JOIN in un LEFT JOIN
  2. Sposta la clausola WHERE nella clausola ON.

La tua query specifica potrebbe essere diversa, quindi assicurati di testare e modificare se necessario.

18 mag 2022 10:01:29
1

Credo che quello che @kaiser stesse cercando di fare fosse dire alla query di restituire tutti i post che hanno quella meta key applicando una sorta di condizione where fittizia per non filtrare nessuno di quei post. Quindi, se sai che tutti i valori che i tuoi campi personalizzati possono assumere sono x,y,z potresti dire "WHERE meta_key IN(x,y,z)" ma l'idea è che puoi evitare del tutto quel problema dicendo != (''):

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

Anche questo non è testato, ma sembra valga la pena provare :-).

23 ago 2013 01:58:26
Commenti

Non posso testarlo al momento, ma sono abbastanza sicuro che restituirà solo i post dove item_price è impostato e non è ''.

powerbuoy powerbuoy
23 ago 2013 14:13:11
0

Ragazzi, grazie per tutte le risposte valide ma, al momento (2022), l'annidamento è la chiave:

$query->set( 'orderby', array(
     'meta_value_num' => 'DESC',
     'title' => 'ASC' )
); // priorità nell'ordinamento

$query->set( 'meta_query', array(
     'featured' => array(
          'relation' => 'OR',
          array(
               'key' => '_featured',
               'value' => '1', // mostra per primi i post con valore = 1
          ),
          array( // queste regole annidate hanno priorità minore nell'ordinamento ma assicurano di mostrare tutti gli altri post
               'relation' => 'OR',
               array(
                    'key' => '_featured',
                    'compare' => 'NOT EXISTS', // si spiega da solo
               ),
               array(
                    'key' => '_featured',
                    'value' => '0', // il caso in cui il valore esiste ma deve essere considerato come non esistente
               )
          )
     )
));

Bonus: Non ho testato questo caso ma se il meta non è un valore 0/1, la terza regola dovrebbe essere:

array(
      'key' => '_featured',
      'value' => '[valore priorità]',
      'compare' => '!='
)
5 dic 2022 21:53:28