0

I've a custom post type Clinics and Doctors. Each clinic has own clinic_id (through custom fields). Each doctor has the same field clinic_id with numbers of clinics where he works.

I need on single-clinic.php to display all doctors, that working here (compare clinic_id of a doctor with clinic_id of current page).

To do this I create new WP_Query object with list of all doctors and then if clinic_id (on clinic page) == clinic_id (on doctors page), display them.

The thing is that there are a lot of doctors (more 5k pages), so when I try to set 'posts_per_page' => -1 it's not working.

$args = array(
            'posts_per_page' => -1,
            'post_type' => 'doctors',
            'orderby' => "ID",
            'nopaging' => true
        );
$listDoctors = new WP_Query($args);

Have you any idea how to do this working?

  • You should use a taxonomy to store the clinic_id, rather than post meta, this will make querying significantly faster and more efficient – Tom J Nowell Aug 14 '17 at 13:41
  • Yes, I was thinking about that, but there are a lot of clinics, so each clinic must be added as like custom taxonomy.... – Влад Aug 14 '17 at 14:15
  • just as much work as a custom field, if you've already got the data, there are ways of converting it without it being a manual process – Tom J Nowell Aug 14 '17 at 15:02
  • How it can be done? – Влад Aug 14 '17 at 19:39
  • that's a good question, and you should ask it as an independent question on the site rather than in the comments so you get a good answer "Converting my post meta data into taxonomy terms" would be the title – Tom J Nowell Aug 14 '17 at 22:27
1

You add the necessary parameters to ask only for posts with the relevant clinic ID.

e.g.

$q = new WP_Query( [
    'post_type'  => 'doctors',
    'meta_key'   => 'clinic_id',
    'meta_value' => get_queried_object_id(),
] );

However, you have a fundamental mistake in how the data structure is designed. Filterable fields are supposed to be stored as terms/taxonomies, not post meta.

Post meta is built to be fast when you already know the ID of the post, but you don't know the ID here, that's what you're querying for. This makes this query extremely expensive and slow. The server will struggle under moderate or even light load without a great caching system, and will have fundamental problems if you reach thousands of posts ( of all types combined not just doctors), possibly more.

Instead, use a taxonomy to store the clinic IDs, where the term slug is the ID. Don't use post meta for it.

Here is code that will automatically create the term when a clinic is created:

add_action( 'save_post', function ( $post_id, $post ) {
    $post_type = get_post_type( $post_id );

    // If this isn't a 'clinic' post, skip
    if ( 'clinic' != $post_type ) {
        return;
    }
    wp_insert_term( $post->post_title, 'clinics', [
        'slug' => $post_id
    ]);
} );

This will delete the term when the clinic is deleted:

add_action( 'before_delete_post', function ( $post_id ) {
    $post_type = get_post_type( $post_id );

    // If this isn't a 'clinic' post, skip
    if ( 'clinic' != $post_type ) {
        return;
    }
    $term = get_term_by( 'slug', strval( $post_id ) ,'clinics' );
    if ( false !== $term ) {
        wp_delete_term( $term_id, 'clinics' );
    }
} );

This function will convert a post from post meta to using the taxonomy:

function wpse276842_convert_clinic_meta_to_terms( $post_id ) {
    $clinic_ids = get_post_meta( $post_id, 'clinic_id', false );
    if ( empty( $clinic_ids ) ) {
        return; // none were found!
    }
    $slugs = [];
    foreach ( $clinic_ids as $id ) {
        $slugs[] = strval( $id );
    }
    wp_set_object_terms( $post_id, $slugs, 'clinics', true );
}

If you want to retrieve which clinics a post/doctor appears in:

$terms = wp_get_object_terms( $post_id, 'clinics' );
foreach( $terms as $term ) {
    echo '<p>'.$term->name.'</p>';
    echo get_term_link( $term, 'clinics' );
    $clinic = get_post( $term->slug ); // gets the original clinic post
}

If you want to fetch all doctors belonging to a particular clinic

$term_id = ... the post ID of the clinic...;
$q = new WP_Query([
    'post_type' => 'doctors',
    'clinics' => strval( $term_id ),
]);
  • As I understood 'clinics' is taxonomy into 'doctors' post type, right? And what is it 'clinic'? – Влад Aug 15 '17 at 8:55
  • I assumed the clinic ID was the ID of a post of type clinic. This code assumes that, and the registration of a non-hierarchical taxonomy named clinics – Tom J Nowell Aug 15 '17 at 12:37
1

You're trying to query 5000 posts at once, when you only need a fraction. Your server's likely choking on the query. Even if it did work it would be horrifically inefficient. Use WP Query to query just the posts you need.

$args = array(
    'posts_per_page' => -1,
    'post_type'  => 'doctors',
    'orderby'    => 'ID',
    'nopaging'   => true,
    'meta_key'   => 'clinic_id',
    'meta_value' => get_queried_object_id(),
);
$listDoctors = new WP_Query($args);

Note that this code assumes that clinic_id is the correct meta_key for your field, and that said ID matches the ID of the current clinic page.

  • 1
    Filtering is the correct approach, but a taxonomy would be orders of magnitude faster and more scalable – Tom J Nowell Aug 14 '17 at 13:40
  • It's working, but one more thing. 'meta_key' is text field made by Custom Fields plugin. Is there any way to set few clinicsID on doctor page (if they working in few clinics)? – Влад Aug 14 '17 at 14:17
  • 1
    You'll definitely want to go with a taxonomy if that's what you need to do. – Jacob Peattie Aug 14 '17 at 14:38
  • This is not right way with Taxonomy, I guess. Every time when will be added new Clinic to post type, it need to be added as like taxonomy too – Влад Aug 14 '17 at 15:00
  • You don't have to add it as a post meta at all, the taxonomy is more than enough on its own – Tom J Nowell Aug 14 '17 at 22:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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