WP_Query cu "post_title LIKE 'something%'"?
Am nevoie să execut un WP_Query
cu LIKE
pe post_title
.
Am început cu acest WP_Query
obișnuit:
$wp_query = new WP_Query(
array (
'post_type' => 'wp_exposants',
'posts_per_page' => '1',
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'paged' => $paged
)
);
Dar ceea ce vreau să fac arată așa în SQL:
$query = "
SELECT *
FROM $wpdb->posts
WHERE $wpdb->posts.post_title LIKE '$param2%'
AND $wpdb->posts.post_type = 'wp_exposants'
ORDER BY $wpdb->posts.post_title
";
$wpdb->get_results($query);
Rezultatul afișează ceea ce mă aștept, dar folosesc obișnuitul <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
pentru a afișa rezultatele.
Iar asta nu funcționează cu $wpdb->get_results()
.
Cum pot realiza ceea ce am descris aici?
Aș rezolva acest lucru cu un filtru pe WP_Query
. Unul care detectează o variabilă suplimentară de interogare și o folosește ca prefix pentru titlu.
add_filter( 'posts_where', 'wpse18703_posts_where', 10, 2 );
function wpse18703_posts_where( $where, &$wp_query )
{
global $wpdb;
if ( $wpse18703_title = $wp_query->get( 'wpse18703_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'' . esc_sql( $wpdb->esc_like( $wpse18703_title ) ) . '%\'';
}
return $where;
}
În acest fel poți continua să apelezi WP_Query
, doar treci titlul ca argument wpse18703_title
(sau schimbă numele în ceva mai scurt).

@kaiser: A trecut mult timp, dar cred că acest lucru nu era posibil cu prepare()
. $wpdb->prepare('LIKE "%s%%"', 'banana')
ar returna "LIKE ''banana'%'"
, așa că trebuie să construim query-ul manual și să facem escaping-ul și noi.

@JanFabry Mă bucur să te văd din nooooooou! :) Treceți pe chat cândva, da? StopPress ar fi fericit să te vadă. Legat de acel prepare()
. Da, e complicat și a trebuit să încerc de mai multe ori înainte să reușesc. Din ceva ce am făcut recent: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) )
. Și sunt sigur că esc_sql()
este inutil și doar paranoic.

Se pare că nu poți căuta un șir care conține '
(apostrof). Presupun că e din cauza escapării? Încă nu am găsit soluția.

Simplificat:
function title_filter( $where, &$wp_query )
{
global $wpdb;
// 2. preiați interogarea personalizată aici:
if ( $search_term = $wp_query->get( 'search_prod_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . esc_sql( like_escape( $search_term ) ) . '%\'';
}
return $where;
}
$args = array(
'post_type' => 'product',
'posts_per_page' => $page_size,
'paged' => $page,
// 1. definiți o variabilă de interogare personalizată aici pentru a transmite termenul:
'search_prod_title' => $search_term,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC'
);
add_filter( 'posts_where', 'title_filter', 10, 2 );
$wp_query = new WP_Query($args);
remove_filter( 'posts_where', 'title_filter', 10 );
return $wp_query;

Cred că codul se explică de la sine, cel puțin pentru mine. Mulțumesc pentru partajarea scriptului complet.

@fdrv Ai dreptate, dar conform documentației WordPress, $wpdb->esc_like are în continuare nevoie de esc_sql(). Deci cred că codul corect ar fi esc_sql( $wpdb->esc_like( $search_term ) )

like_escape
învechit începând cu versiunea 4.0.0 Folosește wpdb::esc_like

am reușit să fac funcțional codul de mai sus, dar din nu știu ce motiv, când efectuam căutarea, returna date din alte post_types chiar dacă am specificat tipul de post în array-ul $args. Am rezolvat adăugând condiția direct în interogarea pentru titlu, care a funcționat pentru mine: $where .= ' OR ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\' AND '. $wpdb->posts . '.post_type = \'huguenot-family\'';

Am dorit să actualizez acest cod la care ați lucrat pentru WordPress 4.0 și versiunile superioare, deoarece esc_sql() este învechit începând cu versiunea 4.0.
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'search_prod_title' )){
/*folosim esc_like() aici în loc de esc_sql()*/
$search_term = $wpdb->esc_like($search_term);
$search_term = ' \'%' . $search_term . '%\'';
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE '.$search_term;
}
return $where;
}
Restul rămâne la fel.
De asemenea, vreau să menționez că puteți folosi variabila s în argumentele WP_Query pentru a transmite termenii de căutare, care cred că va căuta și în titlurile postărilor.
Ca în exemplul de mai jos:
$args = array(
'post_type' => 'post',
's' => $search_term,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC'
);
$wp_query = new WP_Query($args);

Ce este exact search_prod_title
? Ar trebui să-l schimb cu altceva?

De când este esc_sql
învechit? Nu este. $wpdb->escape
este însă... https://developer.wordpress.org/reference/functions/esc_sql/

Rețineți că parametrul s caută și în conținutul articolului, ceea ce poate să nu fie scopul dorit. =)

Cu unele soluții vulnerabile postate aici, vin cu o versiune puțin simplificată și mai sigură.
Mai întâi, creăm o funcție pentru filtrul posts_where
care vă permite să afișați doar postările care corespund anumitor condiții:
function cc_post_title_filter($where, &$wp_query) {
global $wpdb;
if ( $search_term = $wp_query->get( 'cc_search_post_title' ) ) {
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\'';
}
return $where;
}
Acum adăugăm cc_search_post_title
în argumentele noastre de interogare:
$args = array(
'cc_search_post_title' => $search_term, // caută doar în titlul postării
'post_status' => 'publish',
);
Și în final aplicăm filtrul în jurul interogării:
add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$query = new WP_Query($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );
Folosind get_posts()
Anumite funcții care preiau postări nu rulează filtre, astfel încât funcțiile de filtrare posts_where
pe care le atașați nu vor modifica interogarea. Dacă intenționați să folosiți get_posts()
pentru a interoga postările, trebuie să setați suppress_filters
la false în array-ul de argumente:
$args = array(
'cc_search_post_title' => $search_term,
'suppress_filters' => FALSE,
'post_status' => 'publish',
);
Acum puteți folosi get_posts()
:
add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$posts = get_posts($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );
Ce zici de parametrul s
?
Parametrul s
este disponibil:
$args = array(
's' => $search_term,
);
Deși adăugarea termenului de căutare în parametrul s
funcționează și va căuta în titlul postării, va căuta de asemenea în conținutul postării.
Ce zici de parametrul title
adăugat în WP 4.4?
Transmiterea unui termen de căutare în parametrul title
:
$args = array(
'title' => $search_term,
);
Este sensibil la majuscule și folosește LIKE
, nu %LIKE%
. Aceasta înseamnă că căutarea pentru hello
nu va returna postări cu titlul Hello World
sau Hello
.

Excelent. Căutam parametrul 'post_title' și, evident, nu am găsit nimic.

Am o eroare cu wp query sau get posts: "E_WARNING Eroare în fișierul »class-wp-hook.php« la linia 288: Parametrul 2 pentru cc_post_title_filter() se aștepta să fie o referință, a fost dată o valoare

Bazându-mă pe alte răspunsuri anterioare, pentru a oferi flexibilitate în situația în care doriți să căutați un articol care conține un cuvânt într-un câmp meta SAU în titlul articolului, ofer această opțiune prin argumentul "title_filter_relation." În această implementare, permit doar intrări "OR" sau "AND" cu o valoare implicită "AND."
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'title_filter' )){
$search_term = $wpdb->esc_like($search_term); //în loc de esc_sql()
$search_term = ' \'%' . $search_term . '%\'';
$title_filter_relation = (strtoupper($wp_query->get( 'title_filter_relation'))=='OR' ? 'OR' : 'AND');
$where .= ' '.$title_filter_relation.' ' . $wpdb->posts . '.post_title LIKE '.$search_term;
}
return $where;
}
Iată un exemplu al codului în acțiune pentru un tip de articol foarte simplu "faq" unde întrebarea este chiar titlul articolului:
add_filter('posts_where','title_filter',10,2);
$s1 = new WP_Query( array(
'post_type' => 'faq',
'posts_per_page' => -1,
'title_filter' => $q,
'title_filter_relation' => 'OR',
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'faq_answer',
'value' => $q,
'compare' => 'LIKE'
)
)
));
remove_filter('posts_where','title_filter',10,2);

Perspectivă bună, adăugarea de variabile personalizate "query vars" la argumentele query transmise către WP_Query
pentru a putea accesa acestea în cadrul filtrului posts_where
.

Cea mai optimă abordare pentru efectuarea unei interogări WP_Query cu căutare 'LIKE' pe post_title este să includeți argumentul de interogare 'search_columns'. Dacă construiți manual WP_Query, ar trebui să îl specificați astfel:
'search_columns' => ['post_title']
Puteți găsi mai multe detalii în codul sursă WordPress pe Trac: https://core.trac.wordpress.org/browser/tags/6.3/src/wp-includes/class-wp-query.php#L1426
Cu toate acestea, dacă intenția dumneavoastră este să modificați interogările generate de plugin-uri terțe sau interogări create de teme, puteți realiza acest lucru utilizând cârligul de filtrare 'post_search_columns'.
Documentația oficială oferă informații detaliate despre cum să utilizați acest cârlig: https://developer.wordpress.org/reference/hooks/post_search_columns/
