最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

wp query - ACF Relationship + WP Template Parts

programmeradmin1浏览0评论

Any help with this would be greatly appreciated - I've been grappling with it for days :)

I have 2 post types - product + offer

I have an ACF relationship field related_products.

This field returns an array of post IDs Array ( [0] => 395 [1] => 120 [2] => 388 [3] => 391 )

I am using related_products on the offer post type - it's a one-way relationship (not bi-directional).

Each of my post types has a "card" template part that I use in all my loops - that's what I want to do here ... use the related_products IDs to get the product card parts and show them on the offer.

I had no luck with the ACF documentation - but that's probably because I'm a noob.

  • /
  • /

Instead, I set up a custom WP_Query that get's me close, but not there.

The if ( $custom_query->have_posts() ) IS working - when an offer has related products, the correct product cards ARE displayed.

But, when an offer has no related_products, ALL products are being shown.

My questions are:

  • Is a custom WP_QUERY the right/best way to go about this?
  • If so, what do I need to fix in the query?

Thank you for your time and help :)

// Array ( [0] => 395 [1] => 120 [2] => 388 [3] => 391 )
$related_products = get_field('related_products');

$args = array( 
    'post_type' => 'product',
    'post__in' => $related_products,    
    'fields' => 'ids',
    'cache_results'  => false,
    'update_post_meta_cache' => false, 
    'update_post_term_cache' => false, 
    'posts_per_page' => -1, 
    'paged' => false,
);

$custom_query = new WP_Query( $args );

if ( $custom_query->have_posts() ) :  
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post(); 
        get_template_part( 'parts/card', get_post_type() );
    endwhile;
else : 
    // do something else
endif; 
wp_reset_query();

Any help with this would be greatly appreciated - I've been grappling with it for days :)

I have 2 post types - product + offer

I have an ACF relationship field related_products.

This field returns an array of post IDs Array ( [0] => 395 [1] => 120 [2] => 388 [3] => 391 )

I am using related_products on the offer post type - it's a one-way relationship (not bi-directional).

Each of my post types has a "card" template part that I use in all my loops - that's what I want to do here ... use the related_products IDs to get the product card parts and show them on the offer.

I had no luck with the ACF documentation - but that's probably because I'm a noob.

  • https://www.advancedcustomfields/resources/querying-relationship-fields/
  • https://www.advancedcustomfields/resources/acf-fields-relationship-query/

Instead, I set up a custom WP_Query that get's me close, but not there.

The if ( $custom_query->have_posts() ) IS working - when an offer has related products, the correct product cards ARE displayed.

But, when an offer has no related_products, ALL products are being shown.

My questions are:

  • Is a custom WP_QUERY the right/best way to go about this?
  • If so, what do I need to fix in the query?

Thank you for your time and help :)

// Array ( [0] => 395 [1] => 120 [2] => 388 [3] => 391 )
$related_products = get_field('related_products');

$args = array( 
    'post_type' => 'product',
    'post__in' => $related_products,    
    'fields' => 'ids',
    'cache_results'  => false,
    'update_post_meta_cache' => false, 
    'update_post_term_cache' => false, 
    'posts_per_page' => -1, 
    'paged' => false,
);

$custom_query = new WP_Query( $args );

if ( $custom_query->have_posts() ) :  
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post(); 
        get_template_part( 'parts/card', get_post_type() );
    endwhile;
else : 
    // do something else
endif; 
wp_reset_query();
Share Improve this question edited Aug 7, 2019 at 2:47 Howdy_McGee 20.9k24 gold badges91 silver badges177 bronze badges asked Jul 11, 2019 at 18:22 user145691user145691
Add a comment  | 

3 Answers 3

Reset to default 3

But, when an offer has no related_products, ALL products are being shown.

This is because you're not doing anything to check if the field has a value before running your query. If this is empty:

$related_products = get_field('related_products');

Then that makes the query argument equivalent to:

'post__in' => [],

And if that argument value is empty, it doesn't query no posts, it just gets ignored, which means you're query is the equivalent of:

$args = array( 
    'post_type'              => 'product',
    'fields'                 => 'ids',
    'cache_results'          => false,
    'update_post_meta_cache' => false, 
    'update_post_term_cache' => false, 
    'posts_per_page'         => -1, 
    'paged'                  => false,
);

Which is querying all products.

So you need to check if your field has any values before doing anything, then only querying if there are any.

Your other problem is that you have fields set to ids. If you do this then the result is just an array of posts IDs, which you already have because that's what you're querying. If you want to use the loop with your results, you need to remove that line. So all you should have is:

$related_products = get_field( 'related_products' );

if ( $related_products ) {
    $args = array( 
        'post_type' => 'product',
        'post__in'  => $related_products,    
    );

    $custom_query = new WP_Query( $args );

    if ( $custom_query->have_posts() ) :  
        while ( $custom_query->have_posts() ) : 
            $custom_query->the_post(); 
            get_template_part( 'parts/card', get_post_type() );
        endwhile;
    else : 
    // do something else
    endif;

    wp_reset_postdata();
}

But doing this query at all is unnecessary. If you set the field to return post objects, rather than IDs, you can just loop through those:

$related_products = get_field( 'related_products' );

if ( $related_products ) {
    global $post; // Necessary.

    foreach ( $related_products as $post ) : // Must be called $post. 
        setup_postdata( $post ); 
        get_template_part( 'parts/card', get_post_type() );
    endforeach;

    wp_reset_postdata();
}

This is the same as Faye's answer, I'm just including it for completeness. If you're receiving any errors from that, then the issue is somewhere else. Probably inside your card template.

I could be wrong, but it looks like you're trying to do something much simpler than what you've got going there. I use the method shown here on a regular basis and it works just fine.

<?php 

$posts = get_field('related_products');

if( $posts ): ?>

    <?php foreach( $posts as $post): // variable must be called $post (IMPORTANT) ?>
        <?php setup_postdata($post); ?>

        <?php get_template_part( 'parts/card', get_post_type() );?> 
        <!-- this is the only thing I'm not sure if you'd want to just directly call 'parts/card', 'product' -->

    <?php endforeach; ?>
    <?php wp_reset_postdata(); // IMPORTANT - reset the $post object so the rest of the page works correctly ?>
<?php endif; ?>

My OP is about using the ACF relationship field in WP_QUERY. The ACF documentation leaves out one helpful bit that @JacobPeattie supplied.

My final working code looks like this:

// ACF field is set to return IDs.
// Array ( [0] => 395 [1] => 120 [2] => 388 [3] => 391 )

$related_product = get_field( 'related_product' );

if ( $related_product ) :

    global $post; // missing from ACF documentation

    foreach ( $related_product as $post ) : // Must be called $post. 
        setup_postdata( $post ); 
        get_template_part( 'parts/card', get_post_type() );
    endforeach;

    wp_reset_postdata();

endif;

However, part of Jacob's explanation and subsequent comments are wrong. Because I rely on this community for accurate information, I can't accept his answer as correct.

Jacob incorrectly asserts that passing an array of IDs to WP_QUERY and then using the fields parameter to return only IDs is redundant and won't work.

You can use any number of things to inform WP_QUERY which posts to GET - terms, post type, IDs. This is completely separate from what you tell the query to RETURN - there is nothing redundant about passing then returning IDs.

The fields parameter defaults to returning all fields. Alternatively, you can set fields to return ids (note plural).

Deciding to return all fields or just IDs is a matter of optimization - if you don't need all fields, then don't return them.

Here are two examples from my live site demonstrating that YOU DO NOT NEED ALL FIELDS in many common scenarios. You can easily try these for yourself and I've linked to more info below.

This example displays a field - the_title ... super common, I use it all the time and you don't need to return all fields to do it.

$args = array( 
    'post_type' => 'product',
    'fields' => 'ids',
);

$custom_query = new WP_Query( $args );

if ( $custom_query->have_posts() ) :  

    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post(); 
        the_title();
    endwhile;

else : 
    // do something else
endif; 

wp_reset_query();

Here's an even more complex / optimized example. This one is for displaying a template part. This part has standard stuff like the_title + ACF fields + an Add Favorite feature + pulls in other parts. The results are filterable by FacetWP.

Again, you only need WP_QUERY to return IDs.

$args = array( 
    'post_type' => 'product',
    'fields' => 'ids',
    'cache_results'  => false,
    'update_post_meta_cache' => false, 
    'update_post_term_cache' => false, 
    'facetwp' => true, 
);

$custom_query = new WP_Query( $args );

if ( $custom_query->have_posts() ) : 

    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post(); 
        get_template_part( 'parts/card', get_post_type() );                           
    endwhile;

else :
    // do something else
endif;

wp_reset_query();

You can read more about optimizing WP_QUERY here (and lots of other places):

  • https://kinsta/blog/wp-query/

Thinking about it, these examples make perfect sense - things like global Spost and setup_postdata($post) and $custom_query->the_post() are specifically for setting context and ensuring the correct ID is referenced so you can then get the right field, template etc ...

And why would a database need anything more than an ID to get a record's associated fields? ... The only answer I can think of is if the database is poorly designed ... In fact, I have never had a scenario where returning just IDs hasn't worked.

I'm not claiming to be an expert - I only know that you can return just IDs because I read about it, tried it in my own projects, and it works.

发布评论

评论列表(0)

  1. 暂无评论