Cum să interoghez articolele după cheia meta parțială?

26 mar. 2016, 05:00:10
Vizualizări: 23.1K
Voturi: 11

Am o funcție care stochează statusul de "like" pentru un articol ca meta post. Vreau să asociez acel "like" cu utilizatorul care l-a dat, așa că am configurat un câmp personalizat numit "like_status_{user_id}" (unde {user_id} este ID-ul utilizatorului conectat) pe care îl stochez ca 0 sau 1. Astfel, pentru un articol cu mai multe "like-uri" vor exista mai multe valori meta în baza de date configurate astfel:

'meta_key' = 'like_status_0'
'meta_value' = 1
'meta_key' = 'like_status_2'
'meta_value' = 1
'meta_key' = 'like_status_34'
'meta_value' = 1

...și așa mai departe.

Există potențial mii de like-uri pe un articol specific. Cum aș putea rula o interogare care să arate dacă altcineva a dat like acelui articol?

Mă gândeam la ceva de genul:

$query = new WP_Query(array(
    'meta_key' => 'like_status_{user_id}',
    'meta_value' => 1,
));

Încerc să trimit o notificare tuturor celor care au dat like unui articol atunci când altcineva dă like aceluiași articol... ceva de genul "Hei, altcineva a dat like articolului pe care l-ai apreciat. Ar trebui să verifici!" Dar am nevoie de o modalitate de a afla dacă altcineva a dat like acelui articol și, dacă da, cine sunt acești utilizatori pentru a-i putea notifica.

Dacă nu este posibil, puteți sugera o modalitate mai bună de a stoca aceste date ca post_meta păstrând în același timp eficiența actualizării rapide a statusului de like al unui singur utilizator pentru un articol?

0
Toate răspunsurile la întrebare 5
3
12

Este destul de dificil să răspund concret la întrebarea ta. Prima parte este însă ușoară. Recent am făcut ceva similar pe stackoverflow

Cheile meta sunt comparate și trebuie să se potrivească exact. WP_Query nu oferă nicio modalitate simplă de a ajusta acest comportament printr-un parametru, dar putem introduce noi înșine unul și apoi ajusta clauza posts_where pentru a face o comparație de tip LIKE pe cheile meta.

FILTRUL

Acesta este doar un filtru de bază, ajustează-l după nevoie.

add_filter( 'posts_where', function ( $where, \WP_Query $q )
{ 
    // Verificăm variabila noastră personalizată de interogare
    if ( true !== $q->get( 'wildcard_on_key' ) )
        return $where;

    // Filtrăm clauza
    $where = str_replace( 'meta_key =', 'meta_key LIKE', $where );

    return $where;
}, 10, 2 );

După cum poți vedea, filtrul este declanșat doar când setăm noul nostru parametru personalizat, wildcard_on_key la true. Când această verificare este adevărată, pur și simplu înlocuim operatorul = cu operatorul LIKE.

Doar o observație, comparațiile LIKE sunt în mod inerent mai costisitoare decât alte comparații.

INTEROGAREA

Poți interoga postările în felul următor pentru a obține toate postările cu chei meta like_status_{user_id}:

$args = [
    'wildcard_on_key' => true,
    'meta_query'      => [
        [
            'key'   => 'like_status_',
            'value' => 1,
        ]
    ]
];
$query = new WP_Query( $args );

ALTA ÎNTREBARE

Câmpurile personalizate nu au un impact asupra performanței, poți citi postarea mea pe acest subiect aici. Totuși, sunt îngrijorat de faptul că spui că fiecare postare poate avea sute sau mii de aprecieri. Acest lucru poate afecta performanța la obținerea și stocarea în cache a unei cantități atât de mari de date din câmpuri personalizate. De asemenea, poate înfunda baza de date cu o cantitate enormă de date inutile din câmpuri personalizate, ceea ce o face greu de întreținut.

Nu sunt un mare fan al stocării datelor serializate în câmpuri personalizate, deoarece nu poți căuta sau ordona după date serializate. Aș sugera totuși stocarea tuturor ID-urilor de utilizator într-un singur câmp personalizat sub formă de array. Poți actualiza pur și simplu array-ul cu ID-ul utilizatorului atunci când un utilizator apreciază o postare. Obținerea datelor din câmpul personalizat și parcurgerea array-ului de ID-uri pentru a face ceva cu ele este simplă. Aruncă o privire la get_post_meta().

Actualizarea unui câmp personalizat este de asemenea ușoară. Pentru asta, va trebui să te uiți la update_post_meta(), nu știu cum creezi câmpurile tale personalizate, dar update_post_meta() este cu siguranță ceva ce ai vrea să folosești.

Dacă trebuie să trimiți emailuri sau notificări push atunci când un câmp personalizat este actualizat, ai la dispoziție următoarele hook-uri cu care poți lucra. (Vezi update_metadata() pentru context)

CONCLUZIE

Înainte de a posta acest răspuns, din nou, înainte de a alege calea datelor serializate, asigură-te că nu vei avea nevoie să sortezi după aceste date sau să cauți anumite informații în interiorul lor.

26 mar. 2016 09:09:10
Comentarii

Mulțumesc pentru explicația legată de performanța post_meta! Foarte utilă.

codescribblr codescribblr
26 mar. 2016 20:25:14

Acesta ar trebui să fie răspunsul acceptat, este întotdeauna mai bine să folosești filtre decât interogări personalizate. De asemenea, reține că dacă folosești get_posts în loc de WP_Query, trebuie să treci suppress_filters => false, altfel filtrul nu va fi declanșat. Pentru a efectua căutarea LIKE pe cheia meta, trebuie să adaugi % în față și în spatele cheii în array, în funcție de tipul de căutare LIKE pe care dorești să-l faci.

Earle Davies Earle Davies
31 iul. 2016 20:10:30

Și cum ai filtra dacă vrei să interoghezi postări, dar să EXCLUZI toate postările care au o cheie meta de post cu un anumit prefix? (de ex. exclude toate postările care au o meta post LIKE 'prefixul_meu_' ?

gordie gordie
1 apr. 2017 18:47:42
1

Începând cu WordPress 5.1, este posibilă utilizarea interogărilor meta, precum: interogare meta în WordPress

27 feb. 2019 09:59:05
Comentarii

Evadarea liniuțelor de subliniere pare a fi o problemă cu această metodă, dar în rest, arată destul de bine. Mulțumesc pentru evidențiere.

Jake Jake
24 apr. 2019 00:20:40
4

Din păcate, nu poți efectua o interogare meta_query folosind o comparație LIKE pe valoarea meta_key atunci când utilizezi WP_Query. Am trecut și eu prin asta...

În schimb, ai câteva alte opțiuni dacă dorești să menții relațiile de "like" ca metadate ale postărilor și nu ca metadate ale utilizatorilor sau metadate într-un tabel personalizat.

Opțiunea 1

  • nu necesită modificarea schemei de metadate
  • folosește clasa wpdb pentru a executa o interogare personalizată

Exemplu:

//când un utilizator apreciază o postare...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "like_status_{$current_user_id}", 1, false);

//mai târziu în cerere...
global $wpdb;

$results = $wpdb->get_results(
    "
    SELECT meta_key 
    FROM {$wpdb->prefix}postmeta 
    WHERE meta_key 
    LIKE 'like_status_%'
    ",
    ARRAY_N
);

$results = array_map(function($value){

    return (int) str_replace('like_status_', '', $value[0]);

}, $results);

array_walk($results, function($notify_user_id, $key){

    //aplică tuturor utilizatorilor, exceptând utilizatorul care tocmai a dat like
    if ( $notify_user_id !== $current_user_id ) {
        //logică de notificare aici...           
    }

});

Notă: logica poate fi simplificată în continuare dacă dorești.

Opțiunea 2

  • necesită modificarea schemei de metadate
  • necesită stocarea ID-ului utilizatorului ca valoare meta
  • îți permite să folosești WP_Query împreună cu meta_query

Opțiunea 2 necesită să schimbi cheia meta de la like_status_{user_id} la ceva universal precum like_status sau liked_by_user_id, unde în loc să stochezi valoarea 1 împotriva cheii, stochezi ID-ul utilizatorului ca valoare.

//când un utilizator apreciază o postare...
$current_user_id = get_current_user_id();
add_post_meta($current_user_id, "liked_by_user_id", $current_user_id, false);

//mai târziu în cerere
$args = array(
    'post_type'  => 'post', //sau un tip de postare la alegere
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
            'key' => 'liked_by_user_id',
            'value' => 0,
            'type' => 'numeric'
            'compare' => '>'
        )
    )
);

$query = new WP_Query($args);   

array_walk($query->posts, function($post, $key){

    $user_ids = get_post_meta($post->ID, 'liked_by_user_id');

    array_walk($user_ids, function($notify_user_id, $key){
        
        //notifică toți utilizatorii, exceptând utilizatorul care tocmai a dat like
        if ( $notify_user_id !== $current_user_id ) {
                
            //logică de notificare aici...
            //obține utilizatorul de ex. $user = get_user_by('id', $notify_user_id);
            
        }

    });

});
26 mar. 2016 09:11:51
Comentarii

De la versiunea 5.1, consultați răspunsul meu de mai jos

K. Tromp K. Tromp
5 nov. 2019 00:13:14

@K.Tromp Ura!

Adam Adam
5 nov. 2019 12:50:55

Actualizați răspunsul acceptat pentru a reflecta cele mai recente capabilități ale WP sau marcați răspunsul lui @K.Tromp ca fiind cel mai actualizat

zumek zumek
5 mai 2020 05:23:40

Învechit poți acum.

Michael Rogers Michael Rogers
5 ian. 2021 00:20:43
1

Dacă ulterior doriți să extindeți acest lucru, cu statistici mai detaliate, funcționalități suplimentare etc., atunci încă o alternativă ar putea fi: tabele personalizate

  • avantaje: Personalizate în funcție de nevoile dumneavoastră și pot fi indexate pentru o performanță mai bună.

  • dezavantaje: Mai mult de lucru

Ar putea exista și o soluție alternativă folosind o taxonomie personalizată, care ar putea oferi o performanță mai bună la interogări decât interogările de post meta, datorită modului în care tabelele de bază sunt indexate.

Încerc să trimit o notificare tuturor celor care au apreciat un post când altcineva apreciază acel post... ceva de genul: „Hei, altcineva a apreciat postul pe care l-ai apreciat. Ar trebui să arunci o privire!” Dar am nevoie de o modalitate de a afla dacă mai cineva a apreciat acel post și, dacă da, cine sunt aceia pentru a le putea notifica.

Nu sunt sigur ce fel de notificări vă referiți aici, dar acest lucru poate deveni rapid greoi.

Exemplu: Un utilizator care apreciază ~ 1000 de postări și fiecare postare primește ~ 1000 de aprecieri, atunci există 1M de notificări în așteptare, doar pentru acel utilizator! Dacă acestea sunt notificări prin e-mail, atunci furnizorul de hosting s-ar putea să nu fie încântat, iar utilizatorul ar înnebuni. Acest lucru ar putea fi și costisitor cu un serviciu de e-mail terț.

26 mar. 2016 13:18:18
Comentarii

De fapt, trimit notificările o singură dată per persoană pe postare. Deci este mai puțin decât sună — deși tot destul de mult. Motivul pentru care încerc să folosesc tabelele încorporate este că aș dori să pot folosi API-ul standard WP REST în viitor într-o aplicație reală cu aceste date.

codescribblr codescribblr
26 mar. 2016 20:23:06
0
-1

Conform documentației WP_Meta_Query, puteți folosi argumentul compare în argumentul meta_query al WP_Query. Totuși, puteți compara doar pe value și nu pe key, așa că ar trebui să reconsiderați structura acestei abordări.

Un argument like ar arăta astfel:

$arguments = array(
    'meta_query' => array(
        array(
            'key' => 'foo',
            'value' => 'ba',
            'compare' => 'LIKE'
        )
    )
);

$query = new WP_Query($arguments);

Având în vedere că nu puteți face o căutare de tip 'LIKE' pe key, vă sugerez să adăugați postările apreciate în metadatele utilizatorului și să faceți o căutare WP_User_Query pentru utilizatorii care au apreciat acea postare:

$arguments = array(
    'meta_query' => array(
        array(
            'key' => 'liked_post',
            'value' => '<post_id>'
        )
    )
);

$users = new WP_User_Query($arguments);
26 mar. 2016 05:39:55