0

All of my posts have an ACF Relationship field which allows admins to select any number of FAQs, which are a custom post type, faq. On the frontend, these FAQs are displayed after the post's content, so I want my search results to include a post if the FAQ content matches the search query.

So, when an admin selects "FAQ 1" in the ACF Relationship field for "Post 1", and "FAQ 1" contains the word "foo", then a site search for "foo" using the native search feature should display "Post 1" as a result.

Currently, I setup save_post and publish_post and publish_faq to run several loops so that when a post or FAQ is being saved WP will output the linked FAQ content to a new key in the corresponding post's meta data, which then gets included in the native search via the hooks here: https://adambalee.com/search-wordpress-by-custom-fields-without-a-plugin

However, this seems a bit hacky and I was hoping someone with a better handle on SQL syntax may be able to help with a cleaner way to do this, possibly with some smart JOIN statements.

3 Answers 3

1

Unfortunately there's not really an easy way to achieve what you're looking for because you're using ACF to manage the relationships. ACF stores its data as serialized arrays in wp_postmeta, so you can't use SQL. A more performant way to manage the relationships between posts and FAQs would be to use a taxonomy.

2
  • I can appreciate that last sentence but the goal is balance between performance and end client manageability.
    – cfx
    Commented Jul 12, 2017 at 19:27
  • Would definitely be possible to add a select to a custom metabox to duplicate the ACF functionality while being less hard on the DB behind the scenes, but totally understandable that it might be stretching the budget if it was agreed based on the ACF solution. Off the top of my head the way I'd do it would be to have a taxonomy with no UI, where terms are created programmatically with the same IDs as the posts on save.
    – Chris Cox
    Commented Jul 13, 2017 at 15:52
1

I ran into this exact same issue recently and managed to produce a working solution. Coincidentally, I used the same linked article above to modify the search query but that didn't complete the job. I had to modify my search.php template to correctly list the results

In my case, I had a number of faqs related to each page and wanted to list only the page upon search of the data if any content was met. The tutorial above gets you well along the way but you'll need to modify the search query itself. If you were to modify your own search template to remove the current loop and instead change it to something as follows it should get the results back in a relatively efficient and comprehensive fashion without the need to store extra data.

  $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;

  //Change your search query here to include the FAQ post type
  $query_args = [
    'post_type'   => ['post','page','faq'],
    's'           => strip_tags($_GET['s']),
    'post_status' => 'publish',
    'paged'       => $paged
  ];

  $post_query = new WP_Query($query_args);

  if($post_query->have_posts()) {
    //You'll have to keep track of duplicates here as some text will be matched in several FAQs and will possibly result in
    //certain pages being listed twice
    $listed_pages = [];
    while($post_query->have_posts()) {
      $post_query->the_post();
      //Have you matched against an FAQ - if so, then you want to get the related page and list that in the results
      if('faq' == get_post_type()) {
        //Check the ACF relationship field - in my case the field was called display_page
        $linked_page = get_field('display_page', get_the_ID());
        //Prevent Duplicates
        if(!in_array($linked_page[0], $listed_pages)) {
          //Output the search result and make a note that this page has been listed in results
          $listed_pages[] = $linked_page[0];
        }
      } else {
        //Prevent Duplicates
        if(!in_array(get_the_ID(), $listed_pages)) {
          //Output the search result and make a note that this page has been listed in results
          $listed_pages[] = get_the_ID();
        }
      }
    }
  }

Hopefully this will help you out

1
  • This requires that each of my FAQs has a display_page field which links back to the page it displays on. Unfortunately my fields go the other way: each of my posts has a display_faq field instead. Thank you for posting though! This may have given me a brainstorm...
    – cfx
    Commented Jul 13, 2017 at 11:58
0

This is the solution I ultimately devised. If anyone has any improvements then they would be greatly appreciated:

function add_acf_fields_to_postmeta_field($post_id, $post) {
  if(defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
    return;
  }

    $post_faqs         = get_field('questions_answers', $post_id);
    $post_faq_content  = '';

    if(('post' == get_post_type($post_id) || 'press' == get_post_type($post_id)) && !$post_faqs) {
        return;
    }

    if('faq' == get_post_type($post_id)) {
        // when a faq gets saved
        // we find every posts/press that uses this faq
        // then loop through and re-save all that post/press's faq contents to the meta field
        $all_posts = new \WP_Query(array(
            'post_type'  => array('post', 'press'),
            'meta_query' => array(array(
                'key'      => 'questions_answers',
                'value'    => $post_id,
                'compare'  => 'LIKE'
            )),
            'fields'     => 'ids'
        ));
        if($all_posts->have_posts()) {
            foreach($all_posts->posts as $a) {
                $post_faqs = get_field('questions_answers', $a);
                foreach($post_faqs as $p) {
                    $post_faq_content .= strip_tags(get_the_title($p).' '.get_field('answer', $p));
                }
                update_post_meta($a, 'post_faq_content', $post_faq_content);
            }
        }
    } elseif(('post' == get_post_type($post_id) || 'press' == get_post_type($post_id)) && $post_faqs) {
        // when a post/press gets saved and it has faqs
        // loop through them all and save contents to meta field
        foreach($post_faqs as $p) {
            $post_faq_content .= strip_tags(get_the_title($p).' '.get_field('answer', $p));
        }
        update_post_meta($post_id, 'post_faq_content', $post_faq_content);
    }
}
add_action('save_post', __NAMESPACE__.'\\add_acf_fields_to_postmeta_field', 10, 2);
add_action('publish_post', __NAMESPACE__.'\\add_acf_fields_to_postmeta_field', 10, 2);
add_action('publish_press', __NAMESPACE__.'\\add_acf_fields_to_postmeta_field', 10, 2);
add_action('publish_faq', __NAMESPACE__.'\\add_acf_fields_to_postmeta_field', 10, 2);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.