Sortare după valoarea meta, incluzând și postările care nu au una

10 iun. 2013, 01:34:49
Vizualizări: 41.4K
Voturi: 53

Am modificat căutarea implicită WP folosind filtrul pre_get_posts, permițând utilizatorului să sorteze postările (inclusiv mai multe tipuri de postări personalizate) după diferite câmpuri.

Problema pe care o întâmpin este că atunci când îi spun WP să sorteze după o valoare meta, va exclude toate postările care nu au acea valoare meta setată. Acest lucru face ca numărul de rezultate să se schimbe dacă modifici sortarea de la "Preț" la "Dată" deoarece "Postările" nu au setat "Preț" dar "Produsele" au.

Acest lucru nu este ce îmi doresc, așa că aș vrea să știu dacă există o modalitate de a include TOATE postările - chiar și cele care nu au valoarea meta după care sortez - și să le pun pe cele fără valoare la final.

Știu cum să sortez după mai multe câmpuri dar asta nu ajută.

Mulțumesc

Se pare că nu sunt singurul cu această întrebare: Cum să incluzi postări atât cu cât și fără anumită meta_key în argumentele pentru wp_query? dar nu există o soluție acolo.

Actualizare

Am încercat răspunsul dar nu sunt sigur dacă am înțeles corect, iată ce am momentan:

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

    $qry->set('orderby', 'meta_value date'); # Sortarea funcționează atât cu meta_value cât și cu meta_value_num - Am încercat ambele
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Valoarea meta este un număr (este folosită pentru a stoca un preț, după cum sugerează și numele)

Actualizare 2

Am comentat codul pentru sortare și acum am doar atât:

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

Cu acest cod, interogarea pare să returneze toate postările care nu au cheia item_price și niciuna din postările care o au. Adică problema este acum inversată.

Dacă adaug și codul pentru sortare, obțin 0 rezultate.

Edit: ...trei ani mai târziu... :P Am avut din nou această problemă. Am încercat toate răspunsurile date și niciunul nu funcționează. Nu sunt sigur de ce unii oameni par să creadă că funcționează, dar pentru mine cel puțin nu funcționează.

Soluția la care am ajuns este să folosesc filtrul save_post - asigurându-mă că toate postările au câmpul personalizat după care vreau să sortez. Este puțin enervant că trebuie să fac asta, dar atâta timp cât o faci devreme nu vei avea probabil probleme.

În acest caz construiam un "contor de vizualizări" pentru postări și voiam ca utilizatorii să poată sorta după cele mai citite postări. Din nou, postările care nu au fost niciodată vizualizate (presupun că este destul de improbabil - dar totuși) dispăreau când se sorta după numărul de vizualizări. Am adăugat acest cod pentru a mă asigura că toate postările au un contor de vizualizări:

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

Te rugăm să ne arăți codul tău. Ne va fi mai ușor să răspundem.

kaiser kaiser
10 iun. 2013 02:24:14

În primul rând: meta_query și tax_query sunt întotdeauna un array( array() ) deoarece combină mai multe array-uri. În al doilea rând - după cum am menționat în răspunsul meu - trebuie să folosești meta_value_num pentru numere. De asemenea, poate fi necesar să definești explicit meta_value_num (vezi pagina Codex pentru WP_Query). În cele din urmă, nu are sens să folosești order atât în direcția ASC cât și DESC. Acest lucru nu este posibil. Delimitatorul de spațiu funcționează doar pentru orderby și nu poți să-i spui să sorteze primul ASC și al doilea DESC. Pentru asta există filtrul posts_clauses.

kaiser kaiser
10 iun. 2013 03:42:38

Și asigură-te că intrările tale meta_value_num sunt adevărate numere. Am văzut de prea multe ori când cineva susține că este un număr, dar de fapt îl salvează ca string în baza de date.

kaiser kaiser
10 iun. 2013 03:43:23

Mulțumesc pentru ajutor, voi încerca asta și revin cu feedback. Motivul pentru ASC DESC este să sorteze după meta_value în ASCdate în DESC, din câte pot observa funcționează.

powerbuoy powerbuoy
10 iun. 2013 04:06:12

De asemenea, sortarea propriu-zisă funcționează corect. Singura problemă este că articolele (sau tipurile personalizate de postări) care nu au valoarea meta nu sunt afișate. (În legătură cu meta_value_num)

powerbuoy powerbuoy
10 iun. 2013 04:29:58

Pentru a clarifica, dorești să incluzi atât postările cu o anumită valoare meta, cât și cele care nu au această valoare setată?

Howdy_McGee Howdy_McGee
15 apr. 2014 21:20:52

@Howdy_McGee așa este. Unele dintre tipurile mele personalizate de postări AU această valoare setată. Altele nu. Iar tipurile de postări integrate (cum ar fi POST și PAGINĂ) nu o au. Așa că de fiecare dată când încerc să sortez după acel câmp personalizat, apar doar postările CARE au câmpul personalizat.

powerbuoy powerbuoy
15 apr. 2014 21:31:23

Am văzut întrebări similare înainte, @G.M. are un răspuns bun, dar trebuie să afișezi toate rezultatele pe aceeași pagină, așa că dacă acest lucru nu funcționează pentru tine, atunci fie afișezi doar postările cu meta_key, fie postările fără meta_key, nu pe ambele. Iată postarea la care mă refer.

Howdy_McGee Howdy_McGee
15 apr. 2014 21:35:10

Ah, bine, mulțumesc. Aceasta a fost pentru un proiect pe care l-am terminat de mult, dar voi ține minte asta pentru data viitoare. Totuși, aș prefera să continui să folosesc "WP loop" normal cu paginarea obișnuită...

powerbuoy powerbuoy
15 apr. 2014 23:38:05

Am deschis un tichet Trac pentru a adăuga posibil o soluție în clasele de interogare de bază care ar face acest lucru posibil: https://core.trac.wordpress.org/ticket/42907

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

@powerbuoy te rog să consulți răspunsul meu pentru o soluție.

Paul Paul
23 feb. 2019 18:36:01
Arată celelalte 6 comentarii
Toate răspunsurile la întrebare 17
9
25

Simplu și eficient, testat în 2018, folosit în producție în prezent.

Actualizare 2022: Modificat astfel încât interogarea NOT_EXISTS să fie înaintea interogării EXISTS și clarificat efectul pe care îl au mai multe chei asupra clauzei 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' ); 

Aceasta verifică toate elementele cu și fără cheia meta, fără a specifica o valoare. Interogarea meta furnizează cheia pentru orderby în mod fiabil. A fost testat. Clauza orderby cu meta_value/meta_value_num va folosi ultima chei din lanț.

Exemplu practic

/**
 * Modifică interogarea înainte de preluarea postărilor. Setează 
 * parametrii `meta_query` și `orderby` când niciun parametru `orderby` 
 * nu este setat (ordonare implicită).
 * 
 * @param   WP_Query  $query  Obiectul complet `WP_Query`.
 * @return  void
 */
function example_post_ordering( $query ) {
    
    // dacă nu suntem în wp-admin, 
    // și interogarea este interogarea principală, 
    // și interogarea nu este singulară, 
    // și interogarea nu are setat niciun parametru orderby...
    // Notă: verificați tipurile de postări, etc. aici după necesitate.
    if ( ! is_admin() 
    && $query->is_main_query() 
    && ! $query->is_singular() 
    && empty( $query->get( 'orderby' ) ) ) {
        
        // Setarea doar a `meta_key` nu este suficientă, deoarece aceasta 
        // va ignora postările care nu au încă sau nu vor avea niciodată 
        // o valoare pentru cheia specificată. Această interogare meta va 
        // înregistra `meta_key` pentru ordonare, dar nu va ignora acele postări fără o valoare pentru această cheie.
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'NOT EXISTS'
            ),
            array(
                'key' => 'custom_meta_key', 
                'compare' => 'EXISTS'
            ),
        ) );

        // Ordonează după valoarea meta, apoi după titlu dacă mai multe 
        // postări au aceeași valoare pentru cheia meta furnizată.
        // Folosiți `meta_value_num` dacă valorile meta sunt numerice.
        $query->set( 'orderby', 'meta_value title' );
    }

}

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

Aceasta va ordona postările după custom_meta_key în mod implicit și nu va ignora postările fără o valoare pentru acea cheie.

18 apr. 2018 18:23:10
Comentarii

Doar citind codul, tot ce face este să obțină postările care au custom_meta_key și postările care nu au custom_meta_key. Poți include un exemplu practic funcțional cu sortarea.

powerbuoy powerbuoy
18 apr. 2018 20:33:10

Ai dreptate, asta este tot ce face, dar linia de mai jos este responsabilă pentru ordonarea după meta_value (a cheii meta interogate). $query->set( 'orderby', 'meta_value title' ); (Ordonează după valoarea meta, apoi după titlu când mai multe postări au aceeași valoare pentru cheia meta). Acest lucru ar trebui făcut în hook-ul pre_get_posts, folosind variabila $query transmisă. Reține că întrebarea era cum să ordonezi după valoarea meta, fără a ignora postările care nu au o valoare pentru acea cheie meta.

noahmason noahmason
18 apr. 2018 21:18:00

@powerbuoy Vezi exemplul practic actualizat

noahmason noahmason
18 apr. 2018 21:55:47

Bine, o să încerc data viitoare când voi avea această problemă.

powerbuoy powerbuoy
27 apr. 2018 21:13:34

A funcționat pentru mine într-un apel personalizat get_posts() pentru a aduce postările cu metadate _featured în partea de sus, apoi să le ordonez după dată. Mulțumesc!

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

Această soluție a funcționat pentru mine doar când am inversat ordinea dintre NOT EXISTS și EXISTS. Funcționa, dar ordonarea era ignorată.

mikemike mikemike
3 ian. 2021 18:48:42

@mikemike mulțumesc pentru informație, schimbând în jur a funcționat pentru mine

bigdaveygeorge bigdaveygeorge
11 ian. 2021 19:45:54

@mikemike am înțeles - tocmai am făcut această actualizare mai sus.

noahmason noahmason
9 iul. 2022 20:08:50

Tocmai mă confruntam cu această problemă și asta a funcționat perfect,

AuRise AuRise
5 iul. 2024 17:32:37
Arată celelalte 4 comentarii
4
12

Această metodă va returna toate articolele, inclusiv cele cu și fără meta_key solicitat, dar va produce rezultate ciudate la sortare.

add_action('pre_get_posts', 'my_stuff');
function my_stuff ($qry) {
    $qry->set(
        'meta_query',
        array(
            'relation' => 'OR', # Potrivirile pentru acest meta_query ar trebui adăugate la cele care corespund interogării 'meta_key'
            array(
                'key' => 'item_price', 
                'value' => 'bug #23268', 
                'compare' => 'NOT EXISTS'
            )
        )
    );

    $qry->set('orderby', 'meta_value date'); # Sortarea funcționează cu meta_value precum și meta_value_num - am încercat pe ambele
    $qry->set('order', 'ASC DESC');
    $qry->set('meta_key', 'item_price');
}

Am descoperit acest lucru prin experimentarea diferitelor răspunsuri la această întrebare și analizând SQL-ul generat prin încercări și erori. Se pare că setarea array('meta_query' => array('relation' => 'OR')) produce un LEFT JOIN adecvat în loc de INNER JOIN, care este necesar pentru a include articolele care nu au metadatele. Specificarea NOT EXISTS previne clauza WHERE să filtreze articolele care nu au câmpul meta. Pentru această interogare WP_Query specifică, SQL-ul generat este (cu indentare și linii noi adăugate):

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'
        -- Uite, aici dăm permisiune SQL să aleagă un rând
        -- aleatoriu din wp_postmeta când acest articol nu are
        -- '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

Rezultatul este o listă cu toate articolele care au meta_value pentru item_price și cele care nu au item_price. Toate articolele cu item_price vor fi sortate corect între ele, dar articolele fără item_price vor folosi o valoare meta aleatorie altă (de exemplu, _edit_last care pare să fie 1 destul de des în baza mea de date sau altă metadadă internă WordPress complet arbitrară) pentru wp_postmeta.meta_value în clauza ORDER BY. Deci, deși această metodă este aproape și poate părea să funcționeze pentru anumite date, este defectă. Așadar, tot ce pot spune este că, dacă valorile tale pentru item_price nu intră în conflict cu câmpurile meta aleatoare pe care MySQL le alege pentru articolele fără item_price, această metodă ar putea funcționa pentru tine. Dacă tot ce ai nevoie este să te asiguri că articolele tale cu item_price sunt sortate corect între ele, fără a ține cont de ordonarea celorlalte articole, poate fi OK. Dar cred că aceasta este o limitare în WordPress. Te rog corectează-mă, sper să greșesc și să existe o soluție ;-).

Se pare că pentru INNER JOIN wp_postmeta, MySQL alege un rând aleatoriu dintre multiplele rânduri postmeta asociate cu articolul atunci când meta_key lipsește din articolul respectiv. Din perspectiva SQL, trebuie să ne dăm seama cum să-i spunem WordPress să genereze ORDER BY mt1.meta_value. Această coloană este corect NULL când meta_key-ul nostru solicitat lipsește, spre deosebire de wp_postmeta.meta_value. Dacă am putea face asta, SQL ar sorta aceste intrări NULL (lipsă) înaintea oricărei alte valori, oferindu-ne o ordine bine definită: mai întâi toate articolele fără câmpul meta specificat, apoi cele care au câmpul. Dar aceasta este tocmai problema: 'orderby' => 'meta_value' se poate referi doar la 'meta_key' => 'item_price' și wp_postmeta fără alias este întotdeauna un INNER JOIN în loc de un LEFT JOIN, ceea ce înseamnă că wp_postmeta.meta_value și wp_postmeta.meta_key nu pot fi niciodată NULL.

Deci, cred că trebuie să spun că acest lucru nu este posibil cu WP_Query integrat în WordPress așa cum este documentat acum (în WordPress-3.9.1). Enervant. Deci, dacă chiar ai nevoie ca acest lucru să funcționeze corect, probabil că trebuie să te conectezi altundeva în WordPress și să modifici direct SQL-ul generat.

31 iul. 2014 08:32:41
Comentarii

Pare foarte promițător! Voi încerca această soluție data viitoare când voi avea această problemă. Aș vrea să-ți dau răspunsul chiar acum, dar aș prefera să confirm mai întâi că funcționează pentru mine.

powerbuoy powerbuoy
5 aug. 2014 19:09:06

Această soluție a făcut ca nimic să nu mai apară pentru mine. Nu s-a afișat nimic după implementare.

Jake Jake
26 oct. 2016 14:32:47

@Jake Da, la fel și la mine. Am avut din nou această problemă astăzi și am încercat această soluție. Returnează 0 rezultate.

powerbuoy powerbuoy
7 iun. 2017 12:00:52

Ce versiune de WordPress folosiți voi? Cred că acest articol descrie cum să folosești un API intern, nedocumentat, care nu este suportat de WordPress și, prin urmare, probabil funcționează doar dacă utilizați WordPress-3.9.1 sau nu prea multe versiuni după aceea.

binki binki
7 iun. 2017 17:40:40
0

Problema pe care o are toată lumea aici are legătură cu ordinea interogărilor meta. Pentru a sorta corect, va trebui să plasați interogarea "NOT EXISTS" înainte de interogarea "EXISTS".

Motivul pentru aceasta este că WordPress utilizează meta_value din ultima instrucțiune "LEFT JOIN" din clauza "ORDER BY".

De exemplu:

$pageQuery = new WP_Query([
    'meta_query' => [
        'relation' => 'OR',
        ['key' => 'item_price', 'compare' => 'NOT EXISTS'], // aceasta vine primul!
        ['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

Există două soluții posibile pentru această problemă:

1. Toate articolele au meta

Cea mai bună soluție pe care am găsit-o aici este să oferi celorlalte articole/produse un preț de 0. Poți face acest lucru manual sau să parcurgi toate articolele și, dacă prețul este gol, să îl actualizezi.

Pentru a face acest lucru gestionabil în viitor, poți folosi hook-ul save_post și să le atribui o valoare atunci când sunt adăugate pentru prima dată (doar dacă este gol).

2. Interogări multiple

Poți rula prima interogare așa cum o faci acum și să stochezi ID-urile articolelor returnate. Apoi poți rula o altă interogare pentru toate articolele și să le ordonezi după dată, excluzând ID-urile returnate din prima interogare.

Apoi poți afișa cele două rezultate separat ordonate și vei obține rezultatele dorite.

20 apr. 2014 17:01:04
Comentarii

Trei ani mai târziu și am avut aceeași problemă din nou :P A trebuit să folosesc metoda save_post (am actualizat întrebarea mea cu codul pe care l-am folosit).

powerbuoy powerbuoy
7 iun. 2017 12:11:42

Acest lucru este posibil fără a fi nevoie să folosești hook-uri sau să rulezi multiple interogări. Vezi răspunsul meu.

Jacob Raccuia Jacob Raccuia
11 iul. 2020 07:51:50

Ar fi trebuit să arăți exemple de cod.

Steve Moretz Steve Moretz
6 aug. 2021 12:50:35
0

Am întâlnit și eu o problemă similară și următoarea soluție m-a ajutat:

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

Am găsit o descriere pe WordPress Codex cu titlul "'orderby' cu multiple 'meta_key'-uri": https://codex.wordpress.org/Class_Reference/WP_Query#Order_.26_Orderby_Parameters Exemplu de interogare WordPress

25 mai 2019 22:24:40
1

Cred că am o soluție.

Poți folosi două meta_key-uri, unul pe care îl au toate articolele (cum ar fi "_thumbnail_id"), și meta_key-ul pe care dorești să-l folosești ca filtru.

Deci argumentele tale:

$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'); # Sortarea funcționează cu meta_value la fel ca și meta_value_num - am încercat ambele
$qry->set('order', 'ASC DESC');
$qry->set('meta_key', 'item_price');
22 aug. 2013 21:06:01
Comentarii

Problema aici este comparația cu șirul gol, elimin-o și va funcționa 'value' => '', de asemenea, a doua comparație ar trebui să fie NOT EXISTS iar ultima instrucțiune set nu este necesară

nodws nodws
24 apr. 2018 18:19:50
0

Am avut aceeași problemă cu valorile meta numerice și am observat că ordinea interogării este importantă. Pentru mine, interogarea NOT EXISTS trebuie să fie prima.

Exemplu:

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

De asemenea, pentru a obține direcția corectă pentru valorile numerice, este important ca ’orderby’ general să fie setat la ’meta_value_num’. Altfel, veți obține rezultate ciudate pentru valorile numerice, de exemplu:

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

În loc de:

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

12 ian. 2019 03:16:08
0

Interogarea meta OR care combină NOT EXISTS și EXISTS funcționează, dar a cauzat o interogare lentă.

Această soluție funcționează mult mai rapid.

Cheia principală a fost în clauza 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);

Și în argumentele WP_Query:

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

Aceasta este o întrebare foarte veche, dar niciunul dintre răspunsuri nu a fost suficient de robust pentru mine să le folosesc și să fiu mulțumit de rezultate. Soluția de mai jos folosește în continuare WP_Query, dar manipulează instrucțiunile de join și order by.

Folosește o funcție MySQL numită ORDER BY IF, care îți permite să rulezi logică în timpul instrucțiunii ORDER BY. Acest lucru este important deoarece celelalte soluții de aici doar grupează rezultatele fără valoare, fie la început, fie la sfârșitul listei, ceea ce este inutil atunci când încerci să ordonezi după 2 chei.

În exemplul de mai jos am un CPT (Custom Post Type) numit 'businesses'. Vreau să le ordonez pe toate după titlu, dar cele care au is_premium să apară primele. Am comentat codul pentru a înțelege ce se întâmplă în fiecare parte.

<?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 este important să fie primul
        ],
        [
            'key' => 'is_premium', 
            'compare' => 'EXISTS'
        ],
    ]
    ,
    'orderby' => [
        'meta_value' => 'DESC', // Acesta este câmpul is_premium
        'title' => 'ASC',
    ],
    'paged' => $paged
];

// Adăugăm propriul nostru join la interogare
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');

// Adăugăm propriul nostru order by la interogare
function custom_orderby($orderby_statement){
    global $wpdb;

    if ( ! is_admin() ) {
        // Aici ordonăm după meta_value DOAR dacă este 1, altfel o ignorăm pentru acea instrucțiune de ordonare,
        // ceea ce înseamnă că va fi ordonată după următoarea instrucțiune (titlu), la fel ca celelalte rezultate
        $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 ); 

// Interogare
$business = new WP_Query( $args );

// Eliminăm filtrele
remove_filter('posts_orderby','custom_orderby');
remove_filter('posts_join','custom_join');
3 ian. 2021 19:41:17
0

Dacă este potrivit, poți adăuga o valoare meta implicită de fiecare dată când un articol este salvat sau actualizat, dacă valoarea meta nu există.

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

Dacă folosești un tip de postare personalizat, înlocuiește add_action('save_post', 'addDefaultMetaValue'); cu add_action('save_post_{post_type}', 'addDefaultMetaValue'); de exemplu add_action('save_post_product', 'addDefaultMetaValue');

20 aug. 2015 19:17:40
6

Există o valoare posibilă pentru orderby și anume meta_value pentru aceasta.

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

Dacă aveți valori numerice, folosiți meta_value_num în schimb.

Atenție: Acest cod nu a fost testat, dar ar trebui să funcționeze. Ideea este că trebuie să specificați valorile pentru meta_key și key. Altfel, nu puteți compara cu valori inexistente, ceea ce ar trebui să facă posibilă interogarea ambelor tipuri de articole. Este un pic hack-ish, dar atâta timp cât funcționează...

10 iun. 2013 02:38:53
Comentarii

Mulțumesc pentru răspuns, te rog verifică întrebarea mea actualizată, nu sunt sigur că te-am înțeles corect.

powerbuoy powerbuoy
10 iun. 2013 03:05:54

Încă nu am reușit să fac asta să funcționeze, așa că dacă ai o soluție aș dori să știu ce fac greșit. De asemenea, am oferit o recompensă pe SO dacă vrei să o revendici: http://stackoverflow.com/questions/17016770/wordpress-order-by-meta-value-if-it-exists-else-date/17183618

powerbuoy powerbuoy
20 iun. 2013 05:45:54

Două lucruri. 'your_keys_name' și 'your_meta_key' ar trebui să fie același șir de caractere în loc de valori distincte, altfel se pare că ai înțeles greșit întrebarea. În al doilea rând, am testat asta pe configurația mea locală și exclude orice articole unde cheia există (prin meta_query) și exclude orice articole unde cheia lipsește (prin meta_key), rezultând că nu sunt afișate articole. Cu toate acestea, acest răspuns este un pas către ceva care funcționează măcar ;-).

binki binki
31 iul. 2014 07:16:07

Oh, interesant, acest răspuns funcționează dacă doar adaugi 'relation' => 'OR' la meta_query. Lucruri ciudate o_o.

binki binki
31 iul. 2014 07:18:19

@binki Doar trimite o [editare] la întrebarea mea și schimbă părțile pe care consideri că ar trebui modificate. Acesta este un site condus de comunitate :)

kaiser kaiser
31 iul. 2014 12:58:04

@kaiser, am mai experimentat, am testat și am ajuns la o mică monolog. Presupun că noul meu răspuns este locul potrivit pentru a-mi păra vorbăria ;-).

binki binki
31 iul. 2014 16:24:43
Arată celelalte 1 comentarii
0

Această soluție a funcționat pentru mine:

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;

    }

}

Cu toate acestea, această soluție afișează mai întâi înregistrările cu meta_value null. Această altă soluție afișează ordinea ASC și valorile null la sfârșit:

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 oct. 2019 20:03:50
0

Am reușit să rezolv această problemă folosind o singură interogare și argumentele corecte. WP 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);

Vedeți răspunsul meu complet aici: https://wordpress.stackexchange.com/a/370841/15209

11 iul. 2020 07:51:26
3

Am reușit să rezolv această problemă cu un mic hack (în opinia mea), dar a funcționat în cazul meu.

Poți folosi filtrele posts_join_paged și posts_orderby pentru a actualiza șirurile de join și orderby. Acest lucru îți va permite să sortezi după orice dorești, atâta timp cât faci join-ul mai întâi, în loc ca WP_Query să presupună că acel câmp trebuie să existe pentru acel post specific. Apoi poți elimina argumenții meta_key, orderby și `order din WP_Query.

Mai jos este un exemplu. La începutul fiecărei funcții a trebuit să adaug condiții de ieșire pentru anumite cazuri, deoarece acest cod se va aplica la tot ce folosește WP_Query. Poate va trebui să modifici asta pentru nevoile tale specifice.

Documentația pentru aceste două filtre este, din păcate, foarte limitată... noroc! :)

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

/**
 * Modifică join-ul
 *
 * @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')
    ";

    // Adaugă doar dacă nu există deja
    if (strpos($join_paged_statement, $join_to_add) === false) {
        $join_paged_statement = $join_paged_statement . $join_to_add;
    }

    return $join_paged_statement;
}

/** 
 * Modifică 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;
}
15 apr. 2014 21:17:20
Comentarii

Codul funcționează. Dar meta_value este tratată ca un șir de caractere. Astfel, 6 este evaluat mai sus decât 50. Este posibil să se facă modificări pentru a le trata ca numere?

Drivingralle Drivingralle
24 ian. 2017 19:59:03

@Drivingralle cast(my_custom_meta_key.meta_value as unsigned) DESC ar trebui să rezolve problema...

tfrommen tfrommen
2 feb. 2017 23:34:21

Mulțumesc @tfrommen. $orderby_statement = "cast(my_custom_meta_key.meta_value as unsigned) DESC"; funcționează perfect.

Drivingralle Drivingralle
6 feb. 2017 15:22:34
0

Am ajuns să utilizez filtrul 'get_meta_sql' pe care WordPress îl oferă

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

Funcția arată astfel

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

Acest cod rezolvă două probleme cu SQL-ul generat de WordPress.

  1. Transformă INNER JOIN într-un LEFT JOIN
  2. Mută clauza WHERE în clauza ON.

Interogarea ta poate fi diferită, așa că asigură-te să testezi și să ajustezi după caz.

18 mai 2022 10:01:29
1

Cred că @kaiser a încercat să spună că interogarea ar trebui să returneze toate articolele care au acea cheie meta prin aplicarea unei condiții de tip where pentru a nu filtra niciunul dintre acele articole. Deci, dacă știi că toate valorile pe care le pot lua câmpurile personalizate sunt x,y,z, ai putea spune "WHERE meta_key IN(x,y,z)", dar ideea este că poți evita această problemă spunând pur și simplu != (''):

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

De asemenea, nu am testat, dar pare că merită încercat :-).

23 aug. 2013 01:58:26
Comentarii

Nu pot testa acum, dar sunt destul de sigur că va returna doar postările unde item_price este setat și nu este ''.

powerbuoy powerbuoy
23 aug. 2013 14:13:11
0

Băieți, mulțumesc pentru toate răspunsurile valide, dar în prezent (2022), imbricarea este cheia:

$query->set( 'orderby', array(
     'meta_value_num' => 'DESC',
     'title' => 'ASC' )
); // ordonează prioritățile

$query->set( 'meta_query', array(
     'featured' => array(
          'relation' => 'OR',
          array(
               'key' => '_featured',
               'value' => '1', // afișează mai întâi postările cu valoarea = 1
          ),
          array( // aceste reguli imbricate au o prioritate mai mică pentru ordonare, dar ne asigură că afișăm toate celelalte postări
               'relation' => 'OR',
               array(
                    'key' => '_featured',
                    'compare' => 'NOT EXISTS', // se explică de la sine
               ),
               array(
                    'key' => '_featured',
                    'value' => '0', // cazul în care valoarea există, dar trebuie considerată ca inexistentă
               )
          )
     )
));

Caz bonus: Nu am testat acest lucru, dar dacă metadatele nu au o valoare 0/1, a treia regulă ar trebui să fie:

array(
      'key' => '_featured',
      'value' => '[valoare prioritară]',
      'compare' => '!='
)
5 dec. 2022 21:53:28