WP_Query con "post_title LIKE 'something%'"?
Ho bisogno di fare una WP_Query
con un LIKE
sul post_title
.
Ho iniziato con questa WP_Query
standard:
$wp_query = new WP_Query(
array (
'post_type' => 'wp_exposants',
'posts_per_page' => '1',
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC',
'paged' => $paged
)
);
Ma quello che voglio fare in realtà assomiglia a questo in 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);
L'output stampa i risultati che mi aspetto, ma io uso il regolare <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>
per visualizzare i risultati.
E questo non funziona con $wpdb->get_results()
.
Come posso ottenere ciò che ho descritto qui?
Risolverei questo problema con un filtro su WP_Query
. Un filtro che rileva una variabile di query extra e la utilizza come prefisso del titolo.
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;
}
In questo modo puoi ancora chiamare WP_Query
, devi solo passare il titolo come argomento wpse18703_title
(o cambiare il nome in qualcosa di più breve).

@kaiser: È passato molto tempo, ma penso che questo non fosse possibile con prepare()
. $wpdb->prepare('LIKE "%s%%"', 'banana')
restituirebbe "LIKE ''banana'%'"
, quindi dobbiamo costruire la query manualmente e fare anche l'escape.

@JanFabry Felice di rivedertiiiiii! :) Passa in chat qualche volta, eh? StopPress sarebbe felice di vederti. Riguardo a quel prepare()
. Sì, è complicato e ho dovuto provare diverse volte prima di capire come fare. Da qualcosa che ho appena creato: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) )
. E sono abbastanza sicuro che esc_sql()
sia inutile e solo paranoico.

Sembra che non si possa cercare una stringa con un '
(apostrofo) all'interno. Immagino sia a causa dell'escape? Non ho ancora trovato la soluzione

Semplificato:
function title_filter( $where, &$wp_query )
{
global $wpdb;
// 2. estrai la query personalizzata qui:
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. definisci una variabile di query personalizzata qui per passare il tuo termine:
'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;

Penso che il codice sia autoesplicativo, almeno per me. Grazie per aver condiviso lo script completo.

@fdrv Hai ragione ma secondo la documentazione di wp, $wpdb->esc_like ha ancora bisogno di esc_sql(). Quindi penso che il codice corretto sarebbe esc_sql( $wpdb->esc_like( $search_term ) )

sono riuscito a far funzionare il codice sopra ma per qualche motivo quando eseguivo la mia ricerca restituiva dati da altri post_type anche se avevo specificato il tipo di post nell'array $args. Alla fine ho aggiunto la condizione alla query del titolo e ha funzionato $where .= ' OR ' . $wpdb->posts . '.post_title LIKE \'%' . $wpdb->esc_like( $search_term ) . '%\' AND '. $wpdb->posts . '.post_type = \'huguenot-family\'';

Volevo aggiornare questo codice su cui avete lavorato per WordPress 4.0 e versioni successive, poiché esc_sql() è deprecato dalla 4.0 in poi.
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'search_prod_title' )){
/*utilizzo esc_like() qui invece di esc_sql()*/
$search_term = $wpdb->esc_like($search_term);
$search_term = ' \'%' . $search_term . '%\'';
$where .= ' AND ' . $wpdb->posts . '.post_title LIKE '.$search_term;
}
return $where;
}
Il resto rimane invariato.
Vorrei anche far notare che puoi utilizzare la variabile s all'interno degli argomenti di WP_Query per passare i termini di ricerca, che credo includeranno anche il titolo del post nella ricerca.
In questo modo:
$args = array(
'post_type' => 'post',
's' => $search_term,
'post_status' => 'publish',
'orderby' => 'title',
'order' => 'ASC'
);
$wp_query = new WP_Query($args);

Cos'è esattamente search_prod_title
? Dovrei cambiarlo con qualcos'altro?

Da quando esc_sql
è deprecato? Non lo è. $wpdb->escape
invece sì... https://developer.wordpress.org/reference/functions/esc_sql/

Nota che il parametro s cerca anche nel contenuto del post, il che potrebbe non essere l'obiettivo desiderato. =)

Con alcune soluzioni vulnerabili pubblicate qui, propongo una versione leggermente semplificata e sanificata.
Per prima cosa, creiamo una funzione per il filtro posts_where
che permette di mostrare solo i post che corrispondono a condizioni specifiche:
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;
}
Ora aggiungiamo cc_search_post_title
ai nostri argomenti della query:
$args = array(
'cc_search_post_title' => $search_term, // cerca solo nel titolo del post
'post_status' => 'publish',
);
E infine avvolgiamo il filtro attorno alla query:
add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$query = new WP_Query($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );
Utilizzo di get_posts()
Alcune funzioni che recuperano i post non eseguono i filtri, quindi le funzioni del filtro posts_where che aggiungi non modificheranno la query. Se intendi usare get_posts()
per interrogare i tuoi post, devi impostare suppress_filters
su false nell'array degli argomenti:
$args = array(
'cc_search_post_title' => $search_term,
'suppress_filters' => FALSE,
'post_status' => 'publish',
);
Ora puoi usare 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 );
E il parametro s
?
Il parametro s
è disponibile:
$args = array(
's' => $search_term,
);
Sebbene aggiungere il tuo termine di ricerca nel parametro s
funzioni e cerchi nel titolo del post, cercherà anche nel contenuto del post.
E il parametro title
aggiunto con WP 4.4?
Passare un termine di ricerca nel parametro title
:
$args = array(
'title' => $search_term,
);
È sensibile alle maiuscole/minuscole e utilizza LIKE
, non %LIKE%
. Questo significa che una ricerca per hello
non restituirà post con titolo Hello World
o Hello
.

Eccellente. Stavo cercando 'post_title' come parametro e, ovviamente, non ho trovato nulla.

Sto riscontrando un errore con wp query o get posts: "E_WARNING Errore nel file »class-wp-hook.php« alla riga 288: il parametro 2 per cc_post_title_filter() dovrebbe essere un riferimento, invece è stato passato un valore

Basandomi sulle risposte precedenti, per fornire flessibilità nel caso in cui si desideri cercare un post che contenga una parola in un campo meta OPPURE nel titolo del post, offro questa opzione tramite l'argomento "title_filter_relation". In questa implementazione, permetto solo input "OR" o "AND" con un valore predefinito di "AND".
function title_filter($where, &$wp_query){
global $wpdb;
if($search_term = $wp_query->get( 'title_filter' )){
$search_term = $wpdb->esc_like($search_term); //invece di 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;
}
Ecco un esempio del codice in azione per un semplice post type "faq" dove la domanda è il titolo stesso del post:
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);

Ottima intuizione, aggiungere "query vars" personalizzate agli argomenti della query passati a WP_Query
per poterli accedere all'interno del filtro posts_where
.

L'approccio ottimale per eseguire una query WP_Query con una ricerca 'LIKE' su post_title consiste nell'includere l'argomento 'search_columns'. Se stai costruendo manualmente la WP_Query, dovresti specificarlo nel seguente modo:
'search_columns' => ['post_title']
Puoi trovare maggiori dettagli nel codice sorgente di WordPress su Trac: https://core.trac.wordpress.org/browser/tags/6.3/src/wp-includes/class-wp-query.php#L1426
Tuttavia, se l'intenzione è modificare query generate da plugin di terze parti o temi, puoi ottenere questo risultato sfruttando l'hook filter 'post_search_columns'.
La documentazione ufficiale fornisce informazioni dettagliate su come utilizzare questo hook: https://developer.wordpress.org/reference/hooks/post_search_columns/
