web-dev-qa-db-ja.com

検索結果にユーザープロファイルを含めますか?

フロントエンド検索に、ユーザープロファイルと投稿とページを含める必要があります。

私はこの問題の解決策を探しましたが(ここではWSEとWebの両方で)、助けにならなければ見つかりませんでした。必要な限り、私が見つけた最も近いものは 検索結果に著者のプロフィールを含めるユーザー検索を含めるためにWordPress検索を拡張する であり、どちらも合理的な答えがあります。

要件をより明確にするために、サイトには投稿、ページ、および(もちろん)ユーザーがいます。 end-userfront-endで検索を実行する場合、タイトルまたはpost_contentに検索用語が含まれる投稿およびページを検索結果に含める必要があります(通常どおり)フロントエンド検索の場合)plus検索用語に一致するユーザーのプロファイル(ユーザーの意味については以下を参照matching検索語)。

重要なポイントは、私がnot特定のユーザーによってauthoredされた投稿の検索について話しているということです!

要件の背後にある理論的根拠

whyこの要件があることを理解するために、サイトの一部にすべてのユーザープロファイルがリストされ、end-userが任意の名前ユーザーのプロファイルを表示します。ユーザープロファイルには、さまざまなusermetaname__が表示されます。 end-user'sの観点からは、フロントエンドでアクセスできるユーザープロファイルと投稿/ページに違いはありません。

特定のユーザーのユーザープロファイルに文字列 "foo"(つまり、そのユーザーに表示されるusermetaname__の1つに "foo"が含まれている)が表示されているとします。 end-userが「foo」を検索すると、そのユーザーのプロファイルが検索結果に表示されることが期待されます。

暫定的な解決策

以下は、私が思いついた解決策です。この解決策は機能しますが、これを達成するためのより良い/簡単な/壊れにくい方法が必要だと感じています:

  1. 非公開のカスタム投稿タイプ(たとえば、「my_user_profile」)を登録する
  2. user_registerにフックします。タイプ 'my_user_profile'の投稿を挿入し、値が新しく登録されたユーザーのIDであるpostmetaname__(たとえば 'my_user_id')を追加します
  3. the_postsにフックします。 is_search()がtrueの場合、さまざまなusermetaname__でend-user's検索の検索用語を検索するget_users()を実行します。次に、postmeta'my_user_id'でタイプ 'my_user_profile'の投稿に対してget_posts()を実行し、get_users()で見つかったユーザーIDを「IN」します。そして、元の検索で見つかった投稿と、myget_posts()で見つかったタイプ「my_user_profile」の投稿のマージを返します。
  4. post_type_linkget_permalink()によって呼び出されます)にフックします。 $post->post_typeが 'my_user_profile'の場合、$postの 'my​​_user_id' postmetaname__にIDがあるユーザーにget_author_posts_url()を返します。これは、テーマのsearch.phpに、上記の手順が検索結果を「拡張」した方法に関する特定の知識を持つコードを含める必要がないようにするためです。
  5. get_the_excerptにフックします。 $post->post_typeが 'my_user_profile'の場合、IDが$postの 'my​​_user_id' usermetaname__にあるユーザーの特定のpostmetaname__(たとえば 'my_excerpt')の値を返します。これは、テーマのsearch.phpに、上記の手順が検索結果を「拡張」した方法に関する特定の知識を持つコードを含める必要がないようにするためです。

暫定ソリューションのコード

[注:ステップ3のコードは、作業中のコードを転写(およびサニタイズ)するときに導入したバグを修正するために編集されています]

暫定ソリューションのコードは次のとおりです。

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

私が言ったように、上記は機能しますが、私は考えもしなかったもっと簡単な方法があると思わずにはいられません。

代替(拒否)ソリューション

私が考えた代替案の1つは、上記の解決策よりも複雑/脆弱に思われるため拒否されました。

  1. 上記の#1と同じ
  2. 上記の#2と同じ
  3. personal_options_updateにフックします。ユーザー用に既に保存している各usermetaname__について、それらをpostmetaname__として、特定のユーザーに関連付けられたタイプ 'my_user_profile'の投稿に追加します
  4. posts_joinおよびposts_whereにフックして、ステップ#3で追加されたさまざまなpostmetaname__を検索します
  5. 上記の#4と同じ
  6. 上記の#5と同じ

誰もがよりシンプルな/壊れにくいソリューションを持っていますか?

私は解決策を使う準備ができていません。しかし、私はあなたがこの中にユーザの分野を持つように、あなたは問い合わせを強化するべきだと思います。以下の例は、それをさらに実証していると思います。

2つのフィルタフックが必要で、次のようにクエリの結果を取得します。

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

また、Debug ObjectsやQuery Monitorなどのプラグインを使って、このクエリをテスト上で簡単に読むこともできます。 sqlクエリは一例にすぎず、それを使用した結果ではありません。正しい結果を得るためには、それらを一緒にプレイして以下のフックを含める必要があります。このソースは、usersテーブルから1つのフィールドdisplay_nameを追加するための例にすぎません。多分SQL "オタク"が助けることができる。

// 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