WP_Query avec "post_title LIKE 'quelquechose%'"?

44

Je dois faire un WP_Queryavec un LIKEsur le post_title.

J'ai commencé avec cette habituée WP_Query:

$wp_query = new WP_Query( 
    array (
        'post_type'        => 'wp_exposants',
        'posts_per_page'   => '1',
        'post_status'      => 'publish',
        'orderby'          => 'title', 
        'order'            => 'ASC',
        'paged'            => $paged
    )
); 

Mais ce que je veux réellement faire ressemble à ceci en 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);

La sortie imprime les résultats que je m'attendais, mais j'utilise la normale <?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>pour afficher les résultats.
Et cela ne fonctionne pas avec $wpdb->get_results().

Comment puis-je atteindre ce que j'ai décrit ici?

Ludo
la source

Réponses:

45

Je résoudrais cela avec un filtre WP_Query. Celui qui détecte une variable de requête supplémentaire et l’utilise comme préfixe du titre.

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

De cette façon, vous pouvez toujours appeler WP_Query, il vous suffit de passer le titre en wpse18703_titleargument (ou de changer le nom en un nom plus court).

Jan Fabry
la source
Celui-ci manque en quelque sorte le $wpdb->prepare().
Kaiser
@ Kaiser: Cela fait longtemps, mais je pense que ce n'était pas possible avec prepare(). $wpdb->prepare('LIKE "%s%%"', 'banana')reviendrait "LIKE ''banana'%'", alors nous devons construire la requête nous-mêmes et faire la fuite aussi.
Jan Fabry
1
@JanFabry Heureux de vous voir agaaaaaaaain! :) Venez faire un tour en chat, hm? StopPress serait heureux de vous voir. À ce sujet prepare(). Ouais, c'est délicat et j'ai dû essayer cela plusieurs fois avant de le contourner. De quelque chose que je viens de faire: $wpdb->prepare( ' AND {$wpdb->posts}.post_title LIKE %s ', esc_sql( '%'.like_escape( trim( $term ) ).'%' ) ). Et je suis à peu près sûr que esc_sql()c'est inutile et simplement paranoïaque.
Kaiser
Il semble que vous ne pouvez pas rechercher une chaîne avec '(apostrophe) à l'intérieur. Je suppose que c'est à cause de s'échapper? Je n'ai pas encore trouvé la solution
Vincent Decaux
19

Simplifié:

function title_filter( $where, &$wp_query )
{
    global $wpdb;
    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,
    '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, 2 );
return $wp_query;
Rao
la source
13
S'il vous plaît inclure une explication avec votre code.
s_ha_dum
2
Grande simplification
Timo Huovinen
1
Le code est je pense auto-expliqué, au moins pour moi. Merci de partager le script complet.
Hassan Dad Khan
Utilisez '$ wpdb-> esc_like (' au lieu de 'esc_sql (like_escape ('
fdrv
@fdrv Vous avez raison, mais selon les documents wp, $ wpdb-> esc_like a toujours besoin de esc_sql (). Donc, je pense que le bon code serait esc_sql ($ wpdb-> esc_like ($ search_term))
Waqas Bukhary
16

Je voulais mettre à jour ce code sur lequel vous avez travaillé pour wordpress 4.0 et versions supérieures, car esc_sql () est obsolète dans la version 4.0.

function title_filter($where, &$wp_query){
    global $wpdb;

    if($search_term = $wp_query->get( 'search_prod_title' )){
        /*using the esc_like() in here instead of other esc_sql()*/
        $search_term = $wpdb->esc_like($search_term);
        $search_term = ' \'%' . $search_term . '%\'';
        $where .= ' AND ' . $wpdb->posts . '.post_title LIKE '.$search_term;
    }

    return $where;
}

Le reste est pareil.

De plus, je tiens à souligner que vous pouvez utiliser la variable s dans les arguments WP_Query pour transmettre les termes de recherche, ce qui permettra également de rechercher le titre de l'article, je crois.

Comme ça:

$args = array(
    'post_type' => 'post',
    's' => $search_term,
    'post_status' => 'publish',
    'orderby'     => 'title', 
    'order'       => 'ASC'        
);
$wp_query = new WP_Query($args);
Ashan Jay
la source
C'est quoi exactement search_prod_title? Devrais-je changer cela en quelque chose d'autre?
Antonios Tsimourtos
Depuis quand est-il esc_sqldépris? Ce n'est pas. $wpdb->escapeest si ... developer.wordpress.org/reference/functions/esc_sql
Jeremy
Notez que le paramètre s recherche également dans le contenu de la publication, ce qui peut ne pas être l'objectif souhaité. =)
Christine Cooper
10

Avec une solution vulnérable publiée ici, je viens avec une version un peu simplifiée et assainie.

Tout d'abord, nous créons une fonction pour le posts_wherefiltre qui vous permet d'afficher uniquement les publications correspondant à des conditions spécifiques:

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

Maintenant, nous ajoutons cc_search_post_titledans nos arguments de requête:

$args = array(
    'cc_search_post_title' => $search_term, // search post title only
    'post_status' => 'publish',
);

Et enfin, enroulez le filtre autour de la requête:

add_filter( 'posts_where', 'cc_post_title_filter', 10, 2 );
$query = new WP_Query($args);
remove_filter( 'posts_where', 'cc_post_title_filter', 10 );

Utiliser get_posts ()

Certaines fonctions qui récupèrent des publications n'utilisent pas de filtres. Par conséquent, les fonctions de filtre posts_where que vous attachez ne modifieront pas la requête. Si vous envisagez d’ get_posts()interroger vos publications, vous devez définir la valeur suppress_filtersfalse dans votre tableau d’arguments:

$args = array(
    'cc_search_post_title' => $search_term,
    'suppress_filters' => FALSE,
    'post_status' => 'publish',
);

Maintenant vous pouvez utiliser 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 );

Qu'en est-il du sparamètre?

Le sparamètre est disponible:

$args = array(
    's' => $search_term,
);

Lors de l'ajout de votre terme de recherche dans les sparamètres de travail, le titre de la recherche sera également recherché , mais également le contenu de la publication.

Qu'en est-il du titleparamètre ajouté avec WP 4.4?

Passer un terme de recherche dans le titleparamètre:

$args = array(
    'title' => $search_term,
);

Est sensible à la casse et LIKEnon %LIKE%. Cette recherche moyenne hellone renverra pas de message avec le titre Hello Worldou Hello.

Christine Cooper
la source
Excellent. Je cherchais 'post_title' comme paramètre et, évidemment, je n'ai rien trouvé.
MastaBaba le
7

En me basant sur d’autres réponses que j’ai eues avant, pour offrir une certaine flexibilité dans les situations où vous souhaitez rechercher un article qui contient un mot dans un champ méta OU dans le titre de l’article, je donne cette option via l’argument "title_filter_relation". Dans cette implémentation, je n'autorise que les entrées "OU" ou "ET" avec la valeur par défaut de "ET".

function title_filter($where, &$wp_query){
    global $wpdb;
    if($search_term = $wp_query->get( 'title_filter' )){
        $search_term = $wpdb->esc_like($search_term); //instead of 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;
}

Voici un exemple de code en action pour un type de message très simple "faq" où la question est le titre du message lui-même:

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);
David Choy
la source
1
Bonne perspicacité, en ajoutant des "vars de requête" personnalisés aux arguments de requête passés WP_Queryafin de pouvoir y accéder dans le posts_wherefiltre.
Tom Auger