Querying relationship fields

Overview

This tutorial will cover the techniques to query relationship fields in both directions. The example used will be “Doctors” and “Locations”.

Screenshots

To save time, this tutorial will not cover the setup process for the 2 post types and their custom fields. However, the screenshots bellow show the data we will be working with.

archive-doctor.php

First, we will create a template file in our theme to display all the doctors. This file is called “archive-doctor” and can be viewed by going to the url “http://website.com/?post_type=doctor” or “http://website.com/doctor” depending on your permalink structure.

Screenshot

Code

<?php 
/**
 * The template for displaying Archive pages.
 *
 * Theme: Twenty Eleven
 */

get_header(); ?>

	<section id="primary">
		<div id="content" role="main">

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

			<header class="page-header">
				<h1 class="page-title">Doctors</h1>
			</header>

			<?php twentyeleven_content_nav( 'nav-above' ); ?>

			<?php /* Start the Loop */ ?>
			<?php while ( have_posts() ) : the_post(); ?>

				<article>
					<header class="entry-header">
						<?php 

						$photo = get_field('photo');

						?>
						<h1 class="entry-title">
							<a href="<?php the_permalink(); ?>">
								<img class="alignleft" src="<?php echo $photo['url']; ?>" alt="<?php echo $photo['alt']; ?>" width="50" />
								<?php the_title(); ?>
							</a>
						</h1>
					</header>
				</article>

			<?php endwhile; ?>

			<?php twentyeleven_content_nav( 'nav-below' ); ?>

		<?php endif; ?>

		</div><!-- #content -->
	</section><!-- #primary -->

<?php get_sidebar(); ?>
<?php get_footer(); ?>

single-doctor.php

Next, we will create a template file to view each doctor’s information. On this template page, we will show links o the locations where this doctor works.

Screenshot

Code

<?php
/**
 * The Template for displaying single posts.
 *
 * Theme: Twenty Eleven
 */

get_header(); ?>

	<div id="primary">
		<div id="content" role="main">

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

				<article>
					<header class="entry-header">
						<h1 class="entry-title"><?php the_title(); ?></h1>
					</header>

					<div class="entry-content">
						<h2>Photo</h2>
						<?php 

						$photo = get_field('photo');

						?>
						<img class="alignleft" src="<?php echo $photo['url']; ?>" alt="<?php echo $photo['alt']; ?>" />

						<h2>Works at</h2>
						<?php 

						$locations = get_field('location');

						?>
						<?php if( $locations ): ?>
							<ul>
							<?php foreach( $locations as $location ): ?>
								<li>
									<a href="<?php echo get_permalink( $location->ID ); ?>">
										<?php echo get_the_title( $location->ID ); ?>
									</a>
								</li>
							<?php endforeach; ?>
							</ul>
						<?php endif; ?>

					</div>

				</article>

			<?php endwhile; // end of the loop. ?>

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

<?php get_footer(); ?>

single-location.php

Last but not least, we will create the single location template file (as we can now navigate to this page from a doctor). So far, we have created a simple “forward facing” relationship between locations and doctors. Now we will do the opposite. On each location page, we will query the WP database for all the doctors that work in that location.

This will be accomplished by using the “get_posts” function. This function makes use of the WP_Query class to fetch posts. See the parameters section of the WP_Query documentation for a list of parameters that this function accepts.

Screenshot

Code

<?php
/**
 * The Template for displaying single posts.
 *
 * Theme: Twenty Eleven
 */

get_header(); ?>

	<div id="primary">
		<div id="content" role="main">

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

				<article>
					<header class="entry-header">
						<h1 class="entry-title"><?php the_title(); ?></h1>
					</header>

					<div class="entry-content">
						<h2>Address</h2>
						<p><?php the_field('address'); ?></p>

						<h2>Doctors that work here</h2>
						<?php 

						/*
						*  Query posts for a relationship value.
						*  This method uses the meta_query LIKE to match the string "123" to the database value a:1:{i:0;s:3:"123";} (serialized array)
						*/

						$doctors = get_posts(array(
							'post_type' => 'doctor',
							'meta_query' => array(
								array(
									'key' => 'location', // name of custom field
									'value' => '"' . get_the_ID() . '"', // matches exaclty "123", not just 123. This prevents a match for "1234"
									'compare' => 'LIKE'
								)
							)
						));

						?>
						<?php if( $doctors ): ?>
							<ul>
							<?php foreach( $doctors as $doctor ): ?>
								<?php 

								$photo = get_field('photo', $doctor->ID);

								?>
								<li>
									<a href="<?php echo get_permalink( $doctor->ID ); ?>">
										<img src="<?php echo $photo['url']; ?>" alt="<?php echo $photo['alt']; ?>" width="30" />
										<?php echo get_the_title( $doctor->ID ); ?>
									</a>
								</li>
							<?php endforeach; ?>
							</ul>
						<?php endif; ?>

					</div>

				</article>

			<?php endwhile; // end of the loop. ?>

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

<?php get_footer(); ?>

Understanding the query

The above “get_posts” query finds all posts that are of the type “doctor”. It then finds the “location” custom field for each doctor and runs a LIKE query on the value.

The relationship field saves it’s data as a serialized array. If you are not familiar with this format, please look up the stored value in your database. It will look something like this:

  • a:2:{i:0;s:2:”35″;i:1;s:2:”33″;}

This serialized array has 2 values, the first is “35”, the second is “33”. As this is the “location” field for a “doctor”, these 2 numbers must be the ID’s of location post types.

In our query, the LIKE value was ‘”‘ . get_the_ID() . ‘”‘, if the location ID was 35 then the LIKE value would become ‘”35″‘. In this case, the query would match against the doctor’s location value (the serialized array shown above) and the doctor will be returned.