Form search query – displaying ACF “Post Object” field as results

OK so I have had this problem with searching custom fields data.

The thing is with the native WordPress search, is that is really good, fast and powerful, but it’s limitations is that it only searches the wp_posts table. It does not frickin’ query the wp_postmeta table!

So to make the most of the native WordPress search, what you can do is tailor the exact search terms you want to be searchable by saving specific searchable custom field data to the native post_content as a json encoded array.

If you are already using the native post_content field (in wp_posts table) for general job profile guff (in cpt job), then just migrate this data to a ACF Wysiwyg Editor field with the field name job_post_content. This new ACF version of post_content won’t be searchable now. Which is probably a good thing in your situation, as you will have job profile nonsense, which you probably don’t want to be picked up in the job search query anyway. If you still want this content searchable then you can still include this data in our searchable json array, if you wish (i’ve not shown this but it is simple to include).

So for storing any searchable data in native post_content, this is where the magic happens. Follow my comments in code…

Add this to your functions.php

// process cpt job post when saving or updating job post
add_action('acf/save_post', 'acf_save_job', 20);

/**
 * action to run modifications when saving or updating the job
 * @param int $post_id
 * @return void
 */
function acf_save_job($post_id) {

    // get our current global post object
    global $post;

    // check we are on the cpt job else return now
    if($post->post_type <> 'job') return;

    // if job post status is publish or draft
    if($post->post_status == 'publish' || $post->post_status == 'draft') {

        // create job object from current post object
        $job = $post;

        // set an empty searchable data array
        $searchable_job_data = [];

        // we dont need to add the native post title for cpt job as this is already searchable

        // get our secondary title
        $secondary_title = get_field('secondary_title',$post_id);

        // if secondary title exists
        if($secondary_title) {

            // add secondary title to searchable data array
            $searchable_job_data['stitle'] = $secondary_title;

        }

        // get our job links post object field, acf must return this field as post object
        $job_links = get_field('job_links',$post_id);

        // I am assuming you are allowing multiple job links to be selected in job post admin...
        // so we have to loop through each array object and get the job link post title

        // if job links var is an array
        if(is_array($job_links)) {

            // foreach job link item as job object
            foreach ($job_links as $key => $job_link_object) {

                // if job link object has post title
                if($job_link_object->post_title) {

                    // add job link title to our searchable data array
                    $searchable_job_data['jlinks'][] = $job_link_object->post_title;

                }

            }

        }
        
        // add more searchable data to $searchable_job_array here if you wish

        // convert our searchable data array to json
        $searchable_job_json = json_encode($searchable_job_data,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);

        // temp remove the save job action
        remove_action('acf/save_post','acf_save_job',20);

        // update our native job post content with searchable data
        $job->post_content = $searchable_job_json;
        
        // update the current job with new job data
        wp_update_post($job);

        // re add the save job action
        add_action('acf/save_post', 'acf_save_job', 20);

    }

}

So when you create or save a cpt job post, you will notice the native post_content now contains something like this (as an unformatted string)…

{
    "stitle": "Front-end developer",
    "jlinks": [
        "Web developer",
        "Web designer"
    ]
} 

Using cpt or acf you can hide the native post content visually from your admin job post page, but the search data will still exist in the database.

After looking at your custom jobs page, would it not be better to turn job_links, to a taxonomy, rather than a post type. So you can also filter by jobs by selecting job link/service. In my custom jobs archive page example (below) I will also demonstrate this…

Here is a search form html to search jobs only, this can go in your header.php or anywhere…

<form action="/jobs" method="get">
    <input type="search" name="search" placeholder="Search jobs" />
</form>

Here is some jobs query functions to add to your functions.php

/**
 * simple method to get request parameter value
 * @param mixed $name key name to find
 * @param mixed $default default value if no value is found
 * @return mixed
 */
function get_url_var($name = false, $default = false)
{
    // if name is false
    if($name === false) {
        return $_REQUEST;
    }
    // request name var name is set
    if(isset($_REQUEST[$name])) {
        return $_REQUEST[$name];
    }
    // return default
    return $default;
}

/**
 * job custom archive page query
 * @return array
 */
function job_query() {

    // some vars
    $jobs_per_page = 24;

    // check url for these filter keys and get value
    $job_search = get_url_var('search',false);
    $job_link = get_url_var('service',false);
    // add more url parameter filters here

    // build our jobs query
    $job_query = [
        'post_type'         => 'job',
        'post_status'       => 'publish',
        'posts_per_page'    => $jobs_per_page,
        'orderby'           => 'ASC',
        'tax_query'         => [
            'relation' => 'AND'
        ]
    ];

    // filter by search query
    if($job_search) {
        // extend our jobs query with search
        $job_query['s'] = $job_search;
    }

    // if you convert job_links to taxonomy then...
    // filter by job link taxonomy term slug (service)
    if($job_link) {
        // extend our tax query array
        $job_query['tax_query'][] = [
            'taxonomy'  => 'job_link',
            'field'     => 'slug',
            'terms'     => [  ],
        ];
    }

    return $job_query;

}

Now create a page called Jobs with page slug/name jobs. Then create a php page template called page-jobs.php in your theme root, and then add the below php to it…

<?php

// products wp query
$jobs = new WP_Query(job_query());

// get our page header
get_header();

?>
    <main>
        <?php if($jobs->have_posts()): ?>
            <?php while($jobs->have_posts()): $jobs->the_post() ?>
                <article id="job-<?=get_the_ID()?>">
                    <?php the_title(); ?>
                    <?php the_field('secondary_title'); ?>
                    <?php the_field('jobs_links'); ?>
                </article>
            <?php endwhile; ?>
        <?php else: ?>
           No jobs found matching criteria.
        <?php endif; ?>
    </main>
<?php

// get our footer
get_footer();

Now run some search tests with the url format below, or alternatively use the html search form…

https://yoursite.com/jobs?search=designer

Also if you converted your job_links post type to a taxonomy then you can filter cpt jobs like this… (obviously only if you set the ACF job_links Taxonomy field to save/load terms)

https://yoursite.com/jobs?service=web-developer
https://yoursite.com/jobs?service=web-designer


One other thing, i’m not sure of your level of php, but the above could be so much more if you used object oriented php classes. The above code is for your basic function.php user. But if you know how to use php classes then let me know and I can update code to object oriented class format.