web-dev-qa-db-fra.com

inclure des profils d'utilisateurs dans les résultats de recherche?

J'ai besoin d'inclure des profils d'utilisateurs, ainsi que des publications et des pages, dans les recherches frontales.

J'ai cherché des solutions à ce problème (à la fois ici sur WSE et ailleurs sur le Web) et n'ai pas trouvé doit aider. Les plus proches que j'ai trouvés, dans la mesure de mes besoins, sont Inclure le profil de l'auteur dans les résultats de la recherche et Etendre la recherche WordPress à la recherche d'utilisateurs , qui n'ont pas de réponses raisonnables.

Pour être plus clair sur l'exigence: le site a des publications, des pages et (bien sûr) des utilisateurs. Lorsqu'un utilisateur final effectue une recherche sur le front-end, les résultats de la recherche doivent contenir des publications et des pages dont les titres ou post_content contiennent les termes de la recherche (comme il est normal pour les recherches frontales). plus les profils des utilisateurs qui correspond les termes de recherche (voir ci-dessous la signification d'un utilisateur correspondant les termes de recherche).

Le point important est que je pas parle de la recherche de messages qui ont été créés par un utilisateur donné!

Justification de l'exigence

Pour aider à comprendre pourquoi j'ai cette exigence, il peut être utile de savoir qu'une partie du site répertorie tous les profils d'utilisateur et qu'un utilisateur final peut cliquer sur le nom de n'importe quel utilisateur pour voir son profil. . Sur le profil utilisateur, plusieurs usermetaname __ sont affichés. Du point de vue de l'utilisateur final, il n'y a pas de différence entre les profils utilisateur et les articles/pages auxquels ils peuvent accéder sur le front-end.

Supposons que le profil utilisateur d’un utilisateur donné affiche la chaîne "foo" (c’est-à-dire que l’un des usermetaname __ affichés pour cet utilisateur contient "foo"). Lorsqu'un utilisateur final recherche "foo", il s'attend alors à ce que son profil apparaisse dans les résultats de la recherche.

Solution provisoire

Voici la solution que j'ai proposée. Cette solution fonctionne, mais j'estime qu'il doit exister un moyen meilleur, plus simple et moins fragile pour ce faire:

  1. enregistrer un type de message personnalisé non public (par exemple, 'mon_utilisateur_profil')
  2. accrocher dans user_register. insérez une publication de type 'mon_utilisateur_profil' et ajoutez un postmeta(dites 'mon_utilisateur_id') dont la valeur est l'ID du nouvel utilisateur enregistré
  3. accrocher dans the_posts. Lorsque is_search() est true, effectuez une get_users() qui recherche dans usermetales termes recherchés dans la recherche end-user; faites ensuite un get_posts() pour les posts de type 'mon_utilisateur_profil' avec postmeta'mon_utilisateur_id' "IN" les identifiants utilisateur trouvés dans le get_users(); puis renvoyez la fusion des publications trouvées par la recherche d'origine avec les publications de type 'mon_utilisateur_profil' trouvées par ma get_posts().
  4. accrocher dans post_type_link (qui est appelé par get_permalink()). Lorsque le $post->post_type est 'mon_utilisateur_profil', retournez get_author_posts_url() sur l'utilisateur dont l'ID est indiqué dans 'mon_utilisateur_id' postmetanom__ de $post. Ceci afin que le search.php du thème ne doive pas contenir de code avec une connaissance spécifique de la manière dont les étapes ci-dessus ont "augmenté" les résultats de la recherche.
  5. accrocher dans get_the_excerpt. Lorsque $post->post_type est 'mon_utilisateur_profil', retournez la valeur d'un usermeta(dites 'my_excerpt') spécifique pour l'utilisateur dont l'ID se trouve dans 'my_user_id' postmetaof $post. Ceci afin que le search.php du thème ne doive pas contenir de code avec une connaissance spécifique de la manière dont les étapes ci-dessus ont "augmenté" les résultats de la recherche.

Code de solution provisoire

[note: le code de l'étape 3 a été modifié pour corriger un bogue introduit lors de la transcription (et de la désinfection) de mon code de travail]

Voici le code de ma solution provisoire:

// step #1
add_action( 'init', 'wpse_register_post_type' );
function wpse_register_post_type() {
    $args = array(
        'public' => false,
        'show_ui' => false,
        'supports' => array(
            'title',
            'author',
        ),
    );
    register_post_type( 'my_user_profile', $args );
}

// step #2
add_action( 'user_register', 'wpse_add_user_profile_post' );
function wpse_add_user_profile_post( $my_user_id ) {
    $user = get_user_by( 'ID', $my_user_id );
    $args = array(
        'post_type' => 'my_user_profile',
        // so that I can find them easier when manually looking thru the wp_posts table
        'post_title' => $user->display_name,
        'post_status' => 'publish',
    );
    $post_id = wp_insert_post( $args );

    if ( ! is_wp_error( $post_id ) ) {
        update_post_meta( $post_id, 'my_user_id', $my_user_id );
    }

    return;
}

// step #3
add_filter( 'the_posts', array( $this, 'wpse_user_search' ), 10, 2 );
function wpse_user_search( $posts, $query ) {
    if ( ! is_search() ) {
        return $posts;
    }

    $search_terms = explode( ' ', $query->get( 's' ) );
    $user_meta_keys = array( /* my usermeta keys */ );

    $args = array(
        'fields' => 'ID',
        'meta_query' => array( 'relation' => 'OR' ),
    );
    // build the meta_query
    foreach ( $user_meta_keys as $meta_key ) {
        foreach ( $search_terms as $search_term ) {
            $args['meta_query'][] = array(
                'key' => $meta_key,
                'value' => $search_term,
                'compare' => 'LIKE',
            );
        }
    }
    $users = get_users( $args );

    // get the my_user_profile posts associated with $users
    $args = array(
        'post_type' => 'my_user_profile',
        'meta_query' => array(
            array(
                'key' => 'my_user_id',
                'value' => $users,
                'compare' => 'IN',
            ),
        )
    );

    // make sure we don't call ourselves in the get_posts() below
    remove_filter( 'the_posts', array( $this, 'user_search' ) );

    $user_posts = get_posts( $args );

    add_filter( 'the_posts', array( $this, 'user_search' ), 10, 2 );

    $posts = array_merge( $posts, $user_posts );

    return $posts;
}

// step 4
add_filter( 'post_type_link', array( $this, 'wpse_user_profile_permalink' ), 10, 2 );
function wpse_user_profile_permalink( $post_link, $post ) {
    if ( 'my_user_profile' !== $post->post_type ) {
        return $post_link;
    }

    // rely on WP_Post::__get() magic method to get the postmeta
    return get_author_posts_url( $post->my_user_id );
}

// step 5
add_filter( 'get_the_excerpt', array( $this, 'wpse_user_profile_excerpt' ), 10, 2 );
function wpse_user_profile_excerpt( $excerpt, $post ) {
    if ( 'my_user_profile' !== $post->post_type ) {
        return $excerpt;
    }

    // rely on WP_Post::__get() magic method to get the postmeta
    $user = get_user_by( 'ID', $post->my_user_id );

    // rely on WP_User::__get() magic method to get the usermeta
    return $user->my_excerpt;
}

Comme je l'ai dit, ce qui précède fonctionne, mais je ne peux pas m'empêcher de penser qu'il existe un moyen plus simple auquel je n'ai pas pensé.

Solution alternative (rejetée)

Une alternative à laquelle j’ai pensé, mais que j’ai rejetée parce qu’elle semble plus complexe/fragile que la solution ci-dessus, est la suivante:

  1. identique au n ° 1 ci-dessus
  2. comme au n ° 2 ci-dessus
  3. accrocher dans personal_options_update. Pour chaque usermetaque je suis déjà en train de stocker pour un utilisateur, ajoutez-le en tant que postmetaà la publication de type 'mon_utilisateur_profil' associée à l'utilisateur donné.
  4. connectez-vous à posts_join et posts_where pour rechercher les différents postmetaajoutés à l’étape 3
  5. comme au n ° 4 ci-dessus
  6. comme au n ° 5 ci-dessus

Quelqu'un a-t-il une solution plus simple/moins fragile?

Je n'ai pas de solution prête à l'emploi. Cependant, je pense que vous devriez améliorer la requête, de sorte que vous ayez le champ des utilisateurs à l'intérieur. Je pense que l'exemple suivant le démontre plus.

Les deux crochets de filtre sont nécessaires et donnent un résultat pour la requête comme ceci:

SELECT SQL_CALC_FOUND_ROWS wpbeta_posts.ID
FROM wpbeta_posts JOIN wpbeta_users
WHERE 1=1 
AND (((wpbeta_posts.post_title LIKE '%search_string%')
OR (wpbeta_posts.post_excerpt LIKE '%search_string%')
OR (wpbeta_posts.post_content LIKE '%search_string%'))) 
AND wpbeta_posts.post_type IN ('post', 'page', 'attachment')
AND (wpbeta_posts.post_status = 'publish'
OR wpbeta_posts.post_status = 'private')
OR (wpbeta_users.display_name LIKE '%search_string%') 
ORDER BY wpbeta_posts.post_title LIKE '%search_string%' DESC, wpbeta_posts.post_date DESC
LIMIT 0, 10

Vous pouvez également lire cette requête sur vos tests très facilement via un plugin, comme des objets de débogage ou un moniteur de requêtes. La requête SQL n'est qu'un exemple et ne constitue certainement pas le résultat. Vous devez jouer avec eux et inclure les crochets ci-dessous pour obtenir le bon résultat. La source ne reçoit qu'un exemple pour ajouter un champ de la table users, le display_name. Peut-être qu'un "nerd" sql peut aider.

// Enhance the JOIN clause of the WP Query with table users.
add_filter( 'posts_join', function( $join, $wp_query ) {

    // No search string, exit.
    if ( empty( $wp_query->query_vars['s'] ) ) {
        return $join;
    }

    global $wpdb;
    $join .= ' JOIN ' . $wpdb->users;

    return $join;
}, 10, 2 );

// Enhance WHERE clause of the WP Query with user display name. 
add_filter( 'posts_where', function( $where, $wp_query ) {

    // No search, exit.
    if ( ! is_search() ) {
        return $where ;
    }

    global $wpdb;
    $where .= ' OR (' . $wpdb->users . '.display_name LIKE \'%' . esc_sql( $wp_query->query_vars['s'] ) . '%\')';

    return $where ;
}, 10, 2 );
1
bueltge