author.php с ACF и CPTs


Пока я пишу это, я сделаю все возможное, чтобы избежать проблемы XY.

Что я пытаюсь сделать:

Я пытаюсь создать шаблон author.php, который:

  • отображает данные из дополнительных настраиваемых полей на Странице профиля пользователя
  • отображает сообщения автора
    • все публикации, автором которых является пользователь
    • все projects (CPT), в которых зачисляется пользователь (назначается через поле ACF для CPT)

Как я пытался решить эту проблему:

Попытка 1 (author.php шаблон)

Моя первая попытка, и она в основном сработала. Что заставило меня отказаться от этого подхода, так это разбиение на страницы. Подробнее об этом позже. У меня было три вопроса:

  • Первый получил все сообщения, написанные пользователем,
  • второй получил все проекты, на которые был зачислен пользователь,
  • И третий использовал post__in через array_unique( array_merge($query1, $query2) ) для отображения сообщений из этих два запроса одновременно, организованные по дате.

    <?php
    get_header();
    
    global $wp_query;
    if( !isset($whose_ID) ) {
        $whose_ID = $wp_query->queried_object->data->ID;
    }
    
    /*
    |==========================================================================
    | First Query gets all posts by author
    |==========================================================================
    */
    $args = array(
        'author' => $whose_ID,
        'post_type' => 'post'
    );
    $query1 = new WP_Query( $args );
    $first_set = array();
    foreach($query1->posts as $post) {
        $first_set[] = $post->ID;
    }
    wp_reset_postdata();
    
    /*
    |==========================================================================
    | Second Query gets all Projects that are credited to author
    |==========================================================================
    */
    $args2 = array(
        'post_type' => 'projects',
        'meta_key' => 'project_credits_%_user',
        'meta_value' => $whose_ID,
    );
    $query2 = new WP_Query( $args2 );
    $second_set = array();
    foreach($query2->posts as $post) {
        $second_set[] = $post->ID;
    }
    wp_reset_postdata();
    
    /*
    |==========================================================================
    | Then the two queries are merged to create a bag of posts to grab from
    |==========================================================================
    |
    | What sucks about this is the $third_query_allowed. I had to figure out
    | a way to make sure that the query didn't run on an empty array. Bah.
    |
    */
    $third_query_allowed = true;
    if( !empty($first_set) && !empty($second_set) ) {
        $merged_queries = array_unique( array_merge( $first_set, $second_set ) );
    } elseif (!empty($first_set) && empty($second_set) ) {
        $merged_queries = $first_set;
    } elseif ( empty($first_set) && !empty($second_set) ) {
        $merged_queries = $second_set;
    } else {
        $merged_queries = array();
        $third_query_allowed = false;
    }
    
    /*
    |==========================================================================
    | So then this third query does the work of getting all the posts and
    | displaying them on the author page.
    |==========================================================================
    */
    $paged = ( get_query_var( 'paged' ) ) ? get_query_var( 'paged' ) : 1;
    $args3 = array(
        'post_type' => array( 'post', 'projects' ),
        'post__in' => $merged_queries,
        'orderby' => 'post__in',
        'posts_per_page' => 2,
        'paged' => $paged
    );
    $user_connected_to_post = new WP_Query( $args3 ); ?>
    
    <div id="primary" class="">
        <main id="main" class="site-main" role="main">
    
            <?php
            // ACF Helper
            $search = 'user_'.$whose_ID;
    
            // Header Info
            // All of this Works!!
            $name = get_the_author_meta( 'display_name', $whose_ID );
            $job_title = get_field( 'job_title', $search );
            $user_favorite_color = get_field( 'user_favorite_color', $search );
            $user_featured_image = get_field( 'user_featured_image', $search );
            $user_still_image = get_field( 'user_still_image', $search );
            $user_gif = get_field( 'user_gif', $search );
            $user_mp4 = get_field( 'user_mp4', $search );
    
            if(!empty($user_featured_image) && !empty($user_favorite_color)) {
                $header_styles = "style=\"background:url('".$user_featured_image['url']."') no-repeat scroll center center ".$user_favorite_color."; background-size: 100% auto;\"";
            } elseif(!empty($user_featured_image) && empty($user_favorite_color)) {
                $header_styles = "style=\"background:url('".$user_featured_image['url']."') no-repeat scroll center center #efefef; background-size: 100% auto;\"";
            } elseif(empty($user_featured_image) && !empty($user_favorite_color)) {
                $header_styles = "style=\"background-color:".$user_favorite_color."\"";
            }
            ?>
    
            <div class="container-fluid">
                <div class="author-page-header row">
                    <div class="author-page-bg col-sm-12" <?php echo $header_styles; ?>></div>
    
                    <video class="img-circle the-vid author-page-img" width="250" height="250" loop="loop" muted="" autoplay="autoplay" poster="<?php echo $user_still_image['url']; ?>">>
                        <source type="video/mp4" data-source="<?php echo $user_mp4['url']; ?>">
                    </video>
                    <img class="img-circle the-img author-page-img" alt="" data-source="<?php echo $user_gif['url']; ?>">
                </div>
            </div>
    
            <div class="container">
                <div class="row">
                    <header class="author-page-name lower-divider text-center">
                        <h1><?php echo $name; ?></h1>
                        <h3><?php echo $job_title; ?></h3>
                    </header><!-- .author-name -->
                </div>
    
                <div class="row">
                    <div class="author-page-social">
                        <?php
                        // ACF Bug is keeping this from working at the moment.
                        echo three_sons_social_list( $search, array( 'bellyflop', 'dale-earnhardt' ) ); ?>
                    </div>
                </div>
    
                <div class="row upper-divider">
                    <div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 author-page-bio">
                        <p><?php echo get_the_author_meta( 'description', $whose_ID ); ?></p>
                    </div>
                </div>
    
                <?php
                /*
                |==========================================================================
                | here's all the author's posts (this works)
                |==========================================================================
                */
                if( $third_query_allowed === true ){
    
                    if ( $user_connected_to_post->have_posts() ) : ?>
    
                        <div class="row masonry-grid">
    
                            <?php while ( $user_connected_to_post->have_posts() ) : $user_connected_to_post->the_post();
    
                                if($post->post_type === "projects") {
                                    get_template_part( 'lib/partials/projects', 'masonry_item' );
                                } else {
                                    // default post listing
                                    get_template_part( 'lib/partials/content', 'masonry_item' );
                                }
    
                            endwhile; ?>
    
                        </div> <!-- .masonry-grid -->
    
                        <?php // THIS IS THE THING THAT DID NOT WORK ?>
                        <div class="nav-previous alignleft"><?php next_posts_link( 'Older posts', $user_connected_to_post->max_num_pages ); ?></div>
                        <div class="nav-next alignright"><?php previous_posts_link( 'Newer posts', $user_connected_to_post->max_num_pages ); ?></div>
    
                    <?php endif;
                    wp_reset_postdata();
                    wp_reset_query();
                } ?>
    
            </div><!-- .container -->
    
        </main><!-- #main -->
    </div><!-- #primary -->
    
    <?php get_footer();
    

Поскольку я не мог заставить разбиение на страницы работать без 404, я изменил свой подход.

Попытка 2

Прочитав несколько предложений о том, как шаблон author.php использует основной запрос, я перешел к функции, которая изменила основной цикл с помощью действия pre_get_posts. Я переместил все запросы в их собственную функцию, которая возвращала только массив сообщений, которые я хотел отобразить для страницы автора. Я использовал $query->set() и столкнулся с проблемой, когда один из моих запросов был бесконечным циклом. В моем коде не было циклов foreach или while, так что это все еще загадка. Я отказался от этой попытки из-за бесконечного цикла и функциональности русской куклы.

Попытка 3

В конце концов я остановился на этом коде, который теоретически должен работать (но не работает). Вы увидите три прямых запроса $wpdb. Поскольку я только что успешно взорвал свой ящик для бродяг в течение 45 минут с помощью цикла, который был необъяснимо бесконечным, я решил попытаться создать максимально экономичный и экономящий память запрос. Я не знаю, действительно ли это лучше, но это то, что я сделал. Дело не в этом.

merged_author_archive():

function merged_author_archive( &$query ) {

    // Only do this weird shit for the author pages.
    if ( $query->is_author ) {

        // Start with a fresh query (Whether this line is here or not makes no difference)
        wp_reset_query();

        global $wpdb;
        // holy shit this is so gd complex it drives me crazy.
        // since the only thing that gets passed to wordpress on author.php (apparently) to start is the author name, that's what we're going to query.
        $author_nice_name = $query->query['author_name'];

        // Get the Author ID from the nicename.
        $author_ID = $wpdb->get_results("SELECT id FROM $wpdb->users WHERE user_nicename LIKE '$author_nice_name'");
        $author_ID = $author_ID[0]->id;

        // so we can get this on author pages.
        set_query_var( 'three_sons_author_id', $author_ID );

        // Get the IDs from posts authored and then save all the ids to $first_set
        $authored_posts = $wpdb->get_results("SELECT id FROM $wpdb->posts WHERE post_author = '$author_ID' AND post_type = 'post'");
        foreach ($authored_posts as $post) {
            $first_set[] = $post->id;
        }

        // Get the IDS from the projects credited
        $credited_projs = $wpdb->get_results("SELECT post_id FROM $wpdb->postmeta WHERE meta_key LIKE 'project_credits_%_user' AND meta_value = '$author_ID'");
        foreach ($credited_projs as $proj) {
            $second_set[] = $proj->post_id;
        }

        // First case: Both queries resulted in an array that wasn't empty.
        if( !empty($first_set) && !empty($second_set) ) {
            $just_these_posts = array_unique( array_merge( $first_set, $second_set ) );
        }
        // Second Case: Posts were not empty, but Projects were
        elseif (!empty($first_set) && empty($second_set) ) {
            $just_these_posts = $first_set;
        }
        // Third Case: Posts were empty, but Projects were not.
        elseif ( empty($first_set) && !empty($second_set) ) {
            $just_these_posts = $second_set;
        }

        // Here's the new $wp_query. We need to define the array of posts, and the post ids to send to the author page. Then we're done here.
        $query->set( 'post_type', array('post', 'projects') );
        $query->set( 'post__in', $just_these_posts );
    }

    // if it's not an author page, remove the action.
    remove_action( 'pre_get_posts', 'three_sons_merged_author_archive' );
}
add_action( 'pre_get_posts', 'three_sons_merged_author_archive' );

...и его последующее author.php:

get_header();

// getting the author ID from the query. was set in lib/inc/author-functions.php
$whose_ID = get_query_var('three_sons_author_id');

// Header Info
$name = get_the_author_meta( 'display_name', $whose_ID );

// ACF
$search                     = 'user_'.$whose_ID;
$job_title                  = get_field( 'job_title', $search );
$user_favorite_color        = get_field( 'user_favorite_color', $search );
$user_featured_image        = get_field( 'user_featured_image', $search );
$user_still_image           = get_field( 'user_still_image', $search );
$user_gif                   = get_field( 'user_gif', $search );
$user_mp4                   = get_field( 'user_mp4', $search );

if(!empty($user_featured_image) && !empty($user_favorite_color)) {
    $header_styles = "style=\"background:url('".$user_featured_image['url']."') no-repeat scroll center center ".$user_favorite_color."; background-size: 100% auto;\"";
} elseif(!empty($user_featured_image) && empty($user_favorite_color)) {
    $header_styles = "style=\"background:url('".$user_featured_image['url']."') no-repeat scroll center center #efefef; background-size: 100% auto;\"";
} elseif(empty($user_featured_image) && !empty($user_favorite_color)) {
    $header_styles = "style=\"background-color:".$user_favorite_color."\"";
}
?>

<div id="primary" class="">
    <main id="main" class="site-main" role="main">

        <div class="container-fluid">
            <div class="author-page-header row">
                <div class="author-page-bg col-sm-12" <?php echo $header_styles; ?>></div>

                <video class="img-circle the-vid author-page-img" width="250" height="250" loop="loop" muted="" autoplay="autoplay" <? /*poster="<?php echo $user_still_image['url']; ?>" */ ?>>
                    <source type="video/mp4" data-source="<?php echo $user_mp4['url']; ?>">
                </video>
                <img class="img-circle the-img author-page-img" alt="" data-source="<?php echo $user_gif['url']; ?>">
            </div>
        </div>

        <div class="container">
            <div class="row">
                <header class="author-page-name lower-divider text-center">
                    <h1><?php echo $name; ?></h1>
                    <h3><?php echo $job_title; ?></h3>
                </header><!-- .author-name -->
            </div>

            <div class="row">
                <div class="author-page-social">
                    <!-- just for now... -->
                    <p class="text-center">Social Links will go here but they're currently broken for authors.</p>
                    <?php // echo three_sons_social_list( $search, array( 'bellyflop', 'dale-earnhardt' ) ); ?>
                </div>
            </div>

            <div class="row upper-divider">
                <div class="col-xs-10 col-xs-offset-1 col-sm-8 col-sm-offset-2 author-page-bio">
                    <p><?php echo get_the_author_meta( 'description', $whose_ID ); ?></p>
                </div>
            </div>

            <?php
            if ( have_posts() ) : ?>

                <div class="row masonry-grid">

                    <?php while ( have_posts() ) : the_post();

                        if($post->post_type === "projects") {
                            get_template_part( 'lib/partials/projects', 'masonry_item' );
                        } else {
                            // default post listing
                            get_template_part( 'lib/partials/content', 'masonry_item' );
                        }

                    endwhile; ?>

                </div> <!-- .masonry-grid -->

                <div class="nav-previous alignleft"><?php next_posts_link( 'Older posts' ); ?></div>
                <div class="nav-next alignright"><?php previous_posts_link( 'Newer posts' ); ?></div>

            <?php endif; ?>

        </div><!-- .container -->

    </main><!-- #main -->
</div><!-- #primary -->


<?php wp_reset_query();
get_footer();

По сути, я считаю, что этот последний код должен работать. Но это не так. Что-то не переводится. Если я сделаю var_dump($wp_query); die; на author.php, я увижу это (я удалил ненужные или чувствительные биты):

object(WP_Query)#258 (51) {
  ["query"]=>
  array(1) {
    ["author_name"]=>
    string(15) "author-slug"
  }
  ["query_vars"]=>
  array(64) {
    ["author_name"]=>
    string(15) "author-slug"
    ["post__in"]=>
    array(9) {
      [0]=>
      string(1) "1"
      [1]=>
      string(3) "102"
      [2]=>
      string(3) "160"
      [3]=>
      string(3) "196"
      [4]=>
      string(3) "199"
      [5]=>
      string(3) "201"
      [6]=>
      string(3) "206"
      [7]=>
      string(3) "162"
      [8]=>
      string(3) "198"
    }
    ["three_sons_author_id"]=>
    string(1) "3"
    ["post_type"]=>
    array(2) {
      [0]=>
      string(4) "post"
      [1]=>
      string(8) "projects"
    }
    ["order"]=>
    string(4) "DESC"
  }
  ["date_query"]=>
  bool(false)
  ["queried_object"]=>
  object(WP_User)#152 (7) {
    ["data"]=>
    object(stdClass)#151 (10) {
      ["ID"]=>
      string(1) "3"
      ["user_login"]=>
      string(6) "author"
      ["user_nicename"]=>
      string(15) "author-slug"
      ["user_email"]=>
      string(16) "[email protected]"
      ["user_url"]=>
      string(17) "http://example.com"
      ["user_registered"]=>
      string(19) "2016-05-09 19:12:24"
      ["user_status"]=>
      string(1) "0"
      ["display_name"]=>
      string(15) "Author Name"
    }
    ["ID"]=>
    int(3)
    ["caps"]=>
    array(1) {
      ["administrator"]=>
      bool(true)
    }
    ["cap_key"]=>
    string(25) "wp_capabilities"
    ["roles"]=>
    array(1) {
      [0]=>
      string(13) "administrator"
    }
  }
  ["queried_object_id"]=>
  int(3)
  ["request"]=>
  string(489) "SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  WHERE 1=1  AND wp_posts.ID IN (1,102,160,196,199,201,206,162,198) AND (wp_posts.post_author = 3) AND wp_posts.post_type IN ('post', 'projects') AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10"
  ["posts"]=>
  &array(1) {
    [0]=>
    object(WP_Post)#144 (24) {
      ["ID"]=>
      int(1)
      ["post_author"]=>
      string(1) "3"
      ["post_date"]=>
      string(19) "2016-05-03 20:12:01"
      ["post_date_gmt"]=>
      string(19) "2016-05-04 00:12:01"
      ["post_content"]=>
      string(85) "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!"
      ["post_title"]=>
      string(12) "Hello world!"
      ["post_excerpt"]=>
      string(0) ""
      ["post_status"]=>
      string(7) "publish"
      ["comment_status"]=>
      string(6) "closed"
      ["ping_status"]=>
      string(6) "closed"
      ["post_password"]=>
      string(0) ""
      ["post_name"]=>
      string(11) "hello-world"
      ["to_ping"]=>
      string(0) ""
      ["pinged"]=>
      string(0) ""
      ["post_modified"]=>
      string(19) "2016-06-15 14:39:12"
      ["post_modified_gmt"]=>
      string(19) "2016-06-15 18:39:12"
      ["post_content_filtered"]=>
      string(0) ""
      ["post_parent"]=>
      int(0)
      ["guid"]=>
      string(19) "http://3sm.dev/?p=1"
      ["menu_order"]=>
      int(0)
      ["post_type"]=>
      string(4) "post"
      ["post_mime_type"]=>
      string(0) ""
      ["comment_count"]=>
      string(1) "1"
      ["filter"]=>
      string(3) "raw"
    }
  }
  ["post_count"]=>
  int(1)
  ["current_post"]=>
  int(-1)
  ["in_the_loop"]=>
  bool(false)
  ["post"]=>
  object(WP_Post)#144 (24) {
    ["ID"]=>
    int(1)
    ["post_author"]=>
    string(1) "3"
    ["post_date"]=>
    string(19) "2016-05-03 20:12:01"
    ["post_date_gmt"]=>
    string(19) "2016-05-04 00:12:01"
    ["post_content"]=>
    string(85) "Welcome to WordPress. This is your first post. Edit or delete it, then start writing!"
    ["post_title"]=>
    string(12) "Hello world!"
    ["post_excerpt"]=>
    string(0) ""
    ["post_status"]=>
    string(7) "publish"
    ["comment_status"]=>
    string(6) "closed"
    ["ping_status"]=>
    string(6) "closed"
    ["post_password"]=>
    string(0) ""
    ["post_name"]=>
    string(11) "hello-world"
    ["to_ping"]=>
    string(0) ""
    ["pinged"]=>
    string(0) ""
    ["post_modified"]=>
    string(19) "2016-06-15 14:39:12"
    ["post_modified_gmt"]=>
    string(19) "2016-06-15 18:39:12"
    ["post_content_filtered"]=>
    string(0) ""
    ["post_parent"]=>
    int(0)
    ["guid"]=>
    string(19) "http://3sm.dev/?p=1"
    ["menu_order"]=>
    int(0)
    ["post_type"]=>
    string(4) "post"
    ["post_mime_type"]=>
    string(0) ""
    ["comment_count"]=>
    string(1) "1"
    ["filter"]=>
    string(3) "raw"
  }
}

Пожалуйста, обратите внимание, что массив post__in содержит правильные сообщения... но по какой-то причине в posts есть только "Привет, мир". К этому автору также должно быть приложено не менее трех проектов. Похоже, они есть в запросе, но не возвращаются. Для пользователей, которые не создали ни одного post, в цикле вообще ничего не отображается. Для User ID 1, posts И projects появляется.

Может быть, это как-то связано с request?

"SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  WHERE 1=1  AND wp_posts.ID IN (1,102,160,196,199,201,206,162,198) AND (wp_posts.post_author = 3) AND wp_posts.post_type IN ('post', 'projects') AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'acf-disabled' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')  ORDER BY wp_posts.post_date DESC LIMIT 0, 10"

Я знаю, что это сложно и о многом нужно просить, но любая помощь в следующих шагах, которые нужно предпринять, или ошибки в моем коде будут чрезвычайно оценены. Спасибо!


Обновление, после ответа @Pietergoosen:

Мне пришлось внести несколько изменений в его код, чтобы поддерживать PHP <5.4 (тьфу - клиентские серверы!). Единственное существенное изменение заключается в использовании WP_Query вместо get_posts() для второго запроса, потому что get_posts() не подключается к действию posts_where, которое мне было нужно, потому что в meta_key есть подстановочный знак (из-за расширенного поля повторителя настраиваемых полей):

function posts_where_credited( $where ) {
    $where = str_replace("meta_key = 'project_credits_%_user", "meta_key LIKE 'project_credits_%_user", $where);
    return $where;
}
add_filter( 'posts_where' , 'posts_where_credited' );

Итак тогда вот функция pre_get_posts:

function author_pre_get_posts ( $q ) {
    // Remove action
    remove_action( 'pre_get_posts', __FUNCTION__ );

    // Only target the main query, front end only on author archive pages
    if (    !is_admin()
         && $q->is_main_query()
         && $q->is_author()
    ) {
        wp_reset_query();
        wp_reset_postdata();

        // Now we can run our custom queries to get post ID's
        $author_name = sanitize_title_for_query( $q->query['author_name'] );

        // This section is also used by WP_Query to get user id
        $author = get_user_by( 
            'slug', 
            $author_name 
        );
        $author_id = absint( $author->ID );

        // Get the posts
        $args_1 = array(
            'author'                 => $author_id, // Get from referenced query
            'fields'                 => 'ids', // Only get the Post's IDs, faster, lighter, better, stronger
            'posts_per_page'         => -1, // Get all posts
            'cache_results'          => false, // Do not update post cache
            'update_post_meta_cache' => false, // Do not update post meta cache
            'update_post_term_cache' => false, // Do not cache post terms
        );
        $post_type_post_ids = get_posts( $args_1 );

        // Get the projects
        $args_2 = array(
            'post_type'              => 'projects',
            'fields'                 => 'ids', // Only get the Post's IDs, faster, lighter, better, stronger
            'posts_per_page'         => -1, // Get all posts
            'cache_results'          => false, // Do not update post cache
            'update_post_meta_cache' => false, // Do not update post meta cache
            'update_post_term_cache' => false, // Do not cache post terms
            'meta_query' => array(
                array(
                    'key'           => 'project_credits_%_user',
                    'value'         => $author_id,
                )
            )
        );
        $post_type_projects_ids = new WP_Query( $args_2 );
        // retrieve *JUST* the IDs
        $post_type_projects_ids = $post_type_projects_ids->posts;

        // It is now just a matter of merging the two arrays of ids
        $combined = array_unique( 
            array_merge( 
                $post_type_post_ids, 
                $post_type_projects_ids 
            ) 
        );

        // We need to make 100% sure we have id's, otherwise post__in will return all posts
        if ( !$combined ) {
            return;
        }

        var_dump($combined);

        // Lets build the query
        $q->set( 'post_type', array('post', 'projects') );
        $q->set( 'post__in', $combined );
    }
}
add_action( 'pre_get_posts', 'author_pre_get_posts' );

var_dump($combined) даст мне идентификаторы posts и projects, а также отобразит эти сообщения и проекты, как я и ожидал...но только для идентификатора пользователя 1. Для страницы любого другого автора author.php отображаются только объекты с post_type из post - ни один projects не будет отображаться для любого автора/пользователя , КРОМЕ для пользователя с идентификатором пользователя 1.

Так что это единственное, что осталось исправить, и я не уверен, как это исправить.

Author: ethfun, 2016-06-16

1 answers

ПЕРЕРАБОТАННЫЙ ПОДХОД

Проблема с исходным ответом заключается в том, что, хотя мы передаем идентификатор записи параметру post__in, находясь на странице автора, основной запрос удаляет все записи, которые не принадлежат автору. Мы можем передать пустую строку в author_name через фильтр pre_get_posts, но это по своей сути разрушает объект запроса, который является нет-нет.

Это требует нового подхода, и вот что мы сделаем:

  • Мы позволим основному запросу выполняться в обычном режиме, то есть он вернет все записи из типа записи по умолчанию post, автором которых был текущий просматриваемый автор. Это автоматически решит проблему 2. Проблема с отображением настраиваемых полей решается в оригинальном подходе.

    Мы можем использовать pre_get_posts здесь, если нам нужно добавить пользовательские типы сообщений к исходному основному запросу, но, насколько я понимаю, вам нужны только обычные сообщения, поэтому в этом не будет необходимости

  • Чтобы отсортировать вторую проблему, где нам нужны только сообщения из типа сообщения projetcs, где упоминается просматриваемый автор, мы скорее изменим сгенерированный SQL-запрос, чем добавим наши сообщения через pre_get_posts

    Для этого мы будем использовать фильтр posts_where. Мы запустим пользовательский запрос, чтобы вернуть идентификаторы всех сообщений, в которых упоминается автор из projects, затем мы возьмем эти идентификаторы и изменим предложение SQL-запроса WHERE, чтобы также получить эти сообщения.

    Мы по-прежнему будем использовать наш пользовательский триггер для запуска пользовательского запроса чтобы также изменить сгенерированный SQL для meta_query.

Итак, для кода вы можете удалить весь код, который я опубликовал в своем первоначальном подходе, и заменить его нашим новым фильтром. ( Просто еще раз, нам нужен PHP 5.4)

add_filter( 'posts_where', 'posts_where_credited', 10, 2 ); 
function posts_where_credited( $where, \WP_Query $q )
{
    // Lets first check our trigger and change the SQL if needed
    if ( true === $q->get( 'wpse_trigger' ) )
        $where = str_replace(
            "meta_key = 'project_credits_%_user",
            "meta_key LIKE 'project_credits_%_user",
            $where
        );

    // Make sure that we target the main query, front end on author pages
    if (    !is_admin()
         && $q->is_main_query()
         && $q->is_author()
    ) {
        // Get the current author
        $author_name = sanitize_title_for_query( $q->get( 'author_name' ) );
        // Just to make sure we actually have a valid uathor name, if not, bail
        if ( !$author_name )
            return $where;

        $author = get_user_by( 
            'slug',
            $author_name
        );
        $author_id = absint( $author->ID );

        // Get the posts in which the author is mentioned from our post type
        $args = [
            'wpse_trigger'           => true, // Our custom trigger
            'post_type'              => 'projects',
            'posts_per_page'         => -1,
            'fields'                 => 'ids',
            'suppress_filters'       => false,
            'cache_results'          => false,
            'update_post_term_cache' => false,
            'update_post_meta_cache' => false,
            'meta_query'             => [
                [
                    'key'   => 'project_credits_%_user',
                    'value' => $author_id
                ]
            ]
        ];
        $post_ids = get_posts( $args );
        // Make sure we have id's, else bail
        if ( !$post_ids )
            return $where;

        $post_ids = implode( ',', array_map( 'absint', $post_ids ) );

        // We have id's, lets adjust the SQL WHERE clauses
        global $wpdb;

        $where .= " OR ( $wpdb->posts.ID IN ( $post_ids ) ) ";
    }
    return $where;
}

ОРИГИНАЛЬНЫЙ ПОДХОД

Вы очень близки в своих подходах, просто здесь и там случаются сбои. Давайте разберем все это

Отображает данные из дополнительных настраиваемых полей на Странице профиля пользователя

Это должно быть прямолинейно. К сожалению, я никогда не работал с ACF, поэтому я не знаю, как хранятся данные. Однако, короче говоря, все, что вам нужно, - это текущий идентификатор просматриваемой страницы архива автора. У вас уже есть этот идентификатор (+1 для использования запрашиваемого объекта).

Альтернативным и гораздо более надежным способом получения запрашиваемого объекта будет использование основного объекта запроса, хранящегося в $GLOBALS['wp_the_query']. $GLOBALS['wp_the_query'] намного надежнее, чем $GLOBALS['wp_query'], поскольку последнее может быть изменен дерьмовыми функциями, такими как query_posts. Короче говоря, на странице вашего архива авторов вы можете использовать следующее

if( !isset($whose_ID) ) {
    $whose_ID = absint( $GLOBALS['wp_the_query']->queried_object_id );
}

Затем вы можете использовать $whose_ID в своих функциях get_field() для возврата данных ACF. Из вашего кода кажется, что ключ имеет префикс user, поэтому

$search = 'user_' . $whose_ID;

Должно подойти.

Отображает сообщения автора все сообщения, созданные пользователем

Именно здесь во всех ваших подходах отсутствует одна-две мелочи или просто требуется некоторая доработка. Ваш первый подход и последний подход должны были быть объединены в один. Здесь нам нужно иметь в виду одну вещь: WP_Query изначально не поддерживает тип запроса, который вам нужен, поэтому мы не можем изначально сделать это в одном запросе, поэтому нам понадобится более одного запроса. Это, в свою очередь, требует от нас быть умными, так как это может дорого обойтись и даже сорвать банк.

Что мы будем делать здесь, чтобы решить этот раздел, так это:

  • Используйте get_posts, чтобы получить все идентификаторы сообщений из тип сообщения post, написанного автором. Это может быть дорогостоящими накладными расходами, поэтому здесь нам нужно быть очень умными. get_posts быстрее, чем WP_Query, так как это юридически нарушает подкачку, но нам нужно сделать это еще быстрее, так как нам не нужно ломать банк.

    Что мы сделаем, так это сделаем это очень быстро, мы передадим 'fields' => 'ids' в наши get_posts аргументы запроса. Это укажет запросу возвращать только массив идентификаторов сообщений. Это делает запрос очень быстрым, так как мы не возвращаем никаких других почтовых данных. Мы можем кроме того, сделать это еще быстрее, указав запросу не кэшировать условия публикации и метаданные. Это все неуместные вещи, которые нам не нужны

Итак, давайте рассмотрим этот запрос ( ПОЖАЛУЙСТА, ОБРАТИТЕ ВНИМАНИЕ: Весь код непроверен и требует PHP 5.4+)

$author_name = sanitize_title_for_query( $q->query['author_name'] );

$args_1 = [
    'author_name'            => $author_name, // Get from referenced query
    'posts_per_page'         => -1, // Get all posts
    'cache_results'          => false, // Do not update post cache
    'update_post_meta_cache' => false, // Do not update post meta cache
    'update_post_term_cache' => false, // Do not cache post terms
];
$post_ids_1 = get_posts( $args_1 );

$post_ids_1 теперь будет содержать массив идентификаторов записей из записей, которые автор просматривал в данный момент, созданных с использованием типа записи по умолчанию post

  • все проекты (CPT), в которых пользователь зачислен (назначается через поле ACF для CPT)

В этом разделе мы будем следовать тому же самому процессу, что и выше, поэтому я не буду здесь вдаваться в подробности. Давайте посмотрим на код

// This section is also used by WP_Query to get user id
$author = get_user_by( 
    'slug', 
    $author_name 
);
$author_id = absint( $author->ID );

$args_2 = [
    'post_type'              => 'projects',
    'posts_per_page'         => -1, // Get all posts
    'cache_results'          => false, // Do not update post cache
    'update_post_meta_cache' => false, // Do not update post meta cache
    'update_post_term_cache' => false, // Do not cache post terms
    'meta_query' => [
        [
            'key'   => 'project_credits_%_user',
            'value' => $author_id
        ]
    ]
];
$post_ids_2 = get_posts( $args_2 );

Теперь у вас есть все идентификаторы записей, в которых был упомянут автор, из типа записи projects

Теперь, когда у нас есть все соответствующие идентификаторы, пришло время собрать все воедино в нашем pre_get_posts действии

add_action( 'pre_get_posts', function ( $q )
{
    // Remove action
    remove_action( current_action(), __FUNCTION__ );

    // Only target the main query, front end only on author archive pages
    if (    !is_admin()
         && $q->is_main_query()
         && $q->is_author()
    ) {
        // Now we can run our custom queries to get post ID's

        $author_name = sanitize_title_for_query( $q->query['author_name'] );

        $args_1 = [
            'author_name'            => $author_name, // Get from referenced query
            'posts_per_page'         => -1, // Get all posts
            'cache_results'          => false, // Do not update post cache
            'update_post_meta_cache' => false, // Do not update post meta cache
            'update_post_term_cache' => false, // Do not cache post terms
        ];
        $post_ids_1 = get_posts( $args_1 );

        // This section is also used by WP_Query to get user id
        $author = get_user_by( 
            'slug', 
            $author_name 
        );
        $author_id = absint( $author->ID );

        $args_2 = [
            'post_type'              => 'projects',
            'posts_per_page'         => -1, // Get all posts
            'cache_results'          => false, // Do not update post cache
            'update_post_meta_cache' => false, // Do not update post meta cache
            'update_post_term_cache' => false, // Do not cache post terms
            'meta_query' => [
                [
                    'key'   => 'project_credits_%_user',
                    'value' => $author_id
                ]
            ]
        ];
        $post_ids_2 = get_posts( $args_2 );

        // It is now just a matter of merging the two arrays of ids
        $combined = array_unique( 
            array_merge( 
                $post_ids_1, 
                $post_ids_2 
            ) 
        );

        // We need to make 100% sure we have id's, otherwise post__in will return all posts
        if ( !$combined )
            return;

        // Lets build the query
        $q->set( 'post_type', ['post', 'projects'] );
        $q->set( 'post__in',  $combined            );
    }
});

Вы можете нормально отображать свои записи на странице архива авторов с помощью цикл по умолчанию. Просто обратите внимание, согласно указателю синтаксиса сайта, похоже, что в коде в вашем author.php

РЕДАКТИРОВАТЬ

Я просмотрел вашу правку, и есть пара проблем, но это не обязательно означает, что это решит проблему

  • Вам не нужно вызывать wp_reset_postdata() и wp_reset_query(). Последнее используется только с query_posts(), который вы никогда не должны использовать

  • get_posts() допускает изменение фильтрами, но это это не поведение по умолчанию. get_posts() по умолчанию передает 'suppress_filters' => true в WP_Query, которые подавляют фильтры, изменяющие запрос. Вы можете переопределить это, просто передав 'suppress_filters' => false вам get_posts аргументы. Это позволит фильтрам изменять запрос

  • По умолчанию любой из фильтров в WP_Query изменит все экземпляры WP_Query ( который включает в себя основной запрос). Всегда рекомендуется использовать какой-либо триггер для запуска фильтра, предназначенного только для соответствующего запроса. Я бы определенно сделайте это с помощью вашего фильтра posts_where.

    Что мы можем сделать, так это вызвать наш триггер wpse_trigger. Мы можем передать 'wpse_trigger' => true в наш аргумент запроса запроса, на который мы хотели бы нацелиться с помощью нашего фильтра. Все, что нам нужно сделать сейчас, это проверить внутри нашего фильтра, установлен ли наш триггер и установлено ли для него значение true. Помните, что текущий запрос передается по ссылке на фильтр в качестве второго параметра.

    Давайте посмотрим на код

    function posts_where_credited( $where, \WP_Query $q ) 
    {
        // Lets remove the filter
        remove_filter( current_filter(), __FUNCTION__ );
    
        // Lets see if our trigger is set and if the value is true, if not, bail
        if ( true !== $q->get( 'wpse_trigger' ) )
            return $where
    
        // Our trigger is set and true, lets alter this query
        $where = str_replace(
            "meta_key = 'project_credits_%_user", 
            "meta_key LIKE 'project_credits_%_user", 
            $where
        );
    
        return $where;
    }
    add_filter( 'posts_where' , 'posts_where_credited', 10, 2 );
    

    Ваши аргументы запроса для вашего второго get_posts экземпляра место, где вы делаете мета-запрос, теперь может выглядеть примерно так:

    $args_2 = [
        'wpse_trigger'    => true,
        'suppress_filter' => false,
        // Rest of your query args
    ];
    
  • Я не знаю, действительно ли ваш фильтр правильно работает с meta_key LIKE, но вы можете взглянуть на этот пост для некоторых альтернатив, если на самом деле это второй запрос, который завершается неудачей

Что довольно странно, так это то, что все работает для автора 1, но не для любого другого автора. Вот несколько вещей, которые вы можете проверить

  • Сохраните свой экземпляр WP_Query для второй запрос внутри вашего действия pre_get_posts. Затем после этого запроса ( сразу после $post_type_projects_ids = new WP_Query();), сделайте var_dump( $post_type_projects_ids->request ); и загрузите страницу автора. Это приведет к печати SQL-запроса в верхней части страницы вашего автора. Проверьте, совпадает ли результирующий SQL-запрос на всех других страницах автора и на странице автора 1. Если они отличаются, у вас проблема где-то в плагине или в теме с плохим фильтром (, напримерposts_where или posts_clauses), действие (плохое pre_get_posts) или проблема с возможностями вашего пользовательского типа записи. Примечание, вы должны переключиться на WP_Query, так как это не будет работать с get_posts

  • Если SQL-запросы проверяются, выполните var_dump( $post_type_projects_ids->posts ); и проверьте, действительно ли у вас есть сообщения, возвращаемые из запроса. Это будет просто массив с идентификаторами, если есть сообщения

  • Если вышеперечисленное подтвердится, перейдите на страницу вашего автора и в любом месте на этой странице добавьте var_dump( $wp_query->request );. Это приведет к печати SQL-запроса, сгенерированного основным запросом. Опять же, поскольку автор 1 работает, сравните этот SQL-запрос с SQL-запросом страниц других авторов. За исключением идентификаторов записи и автора, запросы должны совпадать. Если нет, обратитесь к первому пункту для отладки

 2
Author: Pieter Goosen, 2017-04-13 12:37:42