Presenting WordPress content with themes

There are a lot of ways of getting user content in WordPress. Plugins are ideal for creating that content and provide that functionality. Themes are better at presenting that content.

One of the many things we often see in theme reviews is what are known as pseudo post types. I love Justin’s post in regards to content and post types so do give a read to understand a little more. The following paragraphs will focus more on code and methods of not only getting content from the WordPress database but also on how to make use of that data for use in the customizer and on the front-end of a site.

The content

First we need to think about what we want to show on the page, right? For this example will focus on a single post. What’s super cool is that this can be applied to custom post types as well and not just the standard post. The function we will use for this is get_post_meta() and the developer documentation is a good starting point: https://developer.wordpress.org/reference/functions/get_post_meta/

What matters here is the returning value from that function:

(mixed) Will be an array if $single is false. Will be value of meta data field if $single is true.

That’s great, but what does it mean, right? Let’s say what we add a custom field to a post. Perhaps a subtitle. Depending on what you set your custom field to, you would use:

if ( $subtitle = get_post_meta( get_the_ID(), 'subtitle', true ) ) {
	printf( '<span class="subtitle">%s</span>', $subtitle );
}

That’s much easier than creating a custom meta box that would be lost when the theme is changed, right? I know you may be asking, but what about other post types? Well, we can use get_metadata() for that. The developer docs help here as well. All we do is change a small thing and we can use it in an archive style.

if ( $subtitle = get_metadata( get_post_type(), get_the_ID(), 'subtitle', true ) ) {
	printf( '<span class="subtitle">%s</span>', $subtitle );
}

Great!

Other types

So, with that you have a basic starting point of core’s built-in capabilities. Metadata is super useful and easy to get. Best part is that if that value doesn’t exist, we don’t output anything! No extra markup or code to worry about.

Now, there are tons of plugins out there that create all sorts of data and content for users. Let’s take a quick look at one that is pretty popular: testimonials.

What’s super cool is that by using a few function_exists() we can check if a plugin is active or not. The registration method may differ from plugin to plugin but the basic thing is to use the right function to check. There are a few functions we can use:

  • function_exists()
  • method_exists()
  • class_exists()
  • interface_exists()

That’s super cool and even core has one that can be useful as well:

  • post_type_exists()

Phenomenal if you want to check for a testimonial post type, or a portfolio, or a form, event, you name it. For this example I’ll use the Easy Testimonials plugin to test.

if ( class_exists( 'easyTestimonials' ) && post_type_exists( 'testimonial' ) ) {
	$testimonials = new WP_Query( array( 'post_type' => 'testimonial', 'posts_per_page' => 3 ) );
	if ( $testimonials->have_posts() ) {
		while ( $testimonials->have_posts() ) {
			$testimonials->the_post();
			the_title( '<h3 class="testimonial-title">', '</h3>' );
		}
	}
	wp_reset_postdata();
}

Super quick example. You can see what it is doing at the beginning. We are checking to see if the class easyTestimonials exists as well as the post type. If the functions return true then we can run our query. What’s nice is that now we are in control of how to present that content however we desire.

But what about the customizer?

I mentioned this a little earlier and we can use this as callback for a control. Yes, conditionally show a control based on what is active and available. Super awesome, right?

In this example we will use the custom fields suite plugin for a page customizer control. This is going to be a unique example in that we are going to check if the plugin is active and if a field is populated on a page. Now, it may not be the best example but it will give you an idea of how it works.

add_action( 'customize_register', function( $wp_customize ) {
	$wp_customize->add_setting( 'hero-location',
		array(
			'default' => 'above',
			'sanitize_callback' => 'jmc_sani_location'
		) );
	$wp_customize->add_control( 'hero-location-control',
		array(
			'settings'        => 'hero-location',
			'label'           => __( 'Hero Image Location', 'text-domain' ),
			'description'     => __( 'Where will the hero image show? Above or below the secondary menu location', 'text-domain'),
			'section'         => 'layout',
			'type'            => 'select',
			'active_callback' => 'jmc_check_field',
			'choices' => array(
				'above' => __( 'Above', 'text-domain' ),
				'below' => __( 'Below', 'text-domain' )
			)
		) );
}, 10 );
function jmc_check_field(){
	// bail if it's not a page
	if ( !is_page() ) {
		return false;
	}

	// check for the function and value
	if ( function_exists( 'CFS' ) && CFS()->get( 'hero-image' ) ) {
		return true;
	}

	// if nothing we won't show the control
	return false;
}

Let’s break it down a little bit. The part that really matters is:

'active_callback' => 'jmc_check_field'

This callback is what will handle our logic. This ultimately determines if that control will show or not. We want to begin with the assumption that it will not show so we always return false at the end. What’s super cool is that this can be done with sections as well. This next example is nice because we check for the available post types and can use a new WP_Query on the theme to get the content.

add_action( 'customize_register', 'jmc_customize' );
function jmc_customize( $wp_customize ) {
	// get list of post types
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );

	if ( ! empty( $post_types ) ) {
		$types = array();
		foreach( $post_types as $type ) {
			$types[$type->name] = $type->label;
		}

		$wp_customize->add_section( 'section-one',
			array(
				'title'           => __( 'Front Page sections', 'text-domain' ),
				'description'     => __( 'Choose what else shows on the front', 'text-domain' ),
				'active_callback' => 'jmc_haz_post_types'
			) );

		$wp_customize->add_setting( 'section-one-type',
			array(
				'default'           => '',
				'sanitize_callback' => 'jmc_post_validate',
				)
		);

		$wp_customize->add_control( 'section-one-control',
			array(
				'label'       => __( 'The post type for section one', 'text-domain' ),
				'description' => __( 'Choose what post type you would like to show', 'text-domain' ),
				'settings'    => 'section-one-type',
				'type'        => 'select',
				'choices'     => $types,
				'section'     => 'section-one'
			)
		);
	}
}

function jmc_haz_post_types() {
	$types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' )
	return empty( $types );
}

function jmc_post_validate( $value, $setting ){
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );
	if ( array_key_exists( $value, $post_types ) ){
		return $value;
	} else {
		return $setting->default;
	}
}

Then on the front-end we could do something like:

if ( ! get_theme_mod( 'section-one-type' ) === false ) {
	$type = new WP_Query(
	array( 'post_type' => get_theme_mod( 'section-one-type' ),
		'posts_per_page' => 3
		)
	);

	if ( $type->have_posts() ):
		while( $type->have_posts() ): $type->the_post();
		the_title( '<h3>', '</h3>' );
		endwhile;
	endif;
	wp_reset_postdata();
}

The thing to remember is that content should remain when the theme is switched. There should be no reason for a user to have to input the same thing over and over again on every theme switch – after all they are after the design and not the content of the theme. A good practice to keep in mind when creating your options is:

Do I give them decision A or decision B?

Two or three column section

A while back I wrote a quick and dirty tutorial on how to display more than one page. This can be done in a number of different ways. You can read about them in other tutorials and guides because this time I’ll go over another method using the WP_Query method. The codex page some really great ways and examples of using it to your advantage, I highly recommend you give it a skim and return to this.

So, on to the code!

What I’ll be using is a switch statement, an active callback, and of course the customizer API.

First we will create the settings and controls:

$wp_customize->add_section( 'demo-front-sections',
				 array(
					 'title' => __( 'Front page sections', 'text-domain' ),
					 'description' => __( 'Choose what pages to show on the front page panels', 'text-domain' ),
					 'priority' => 1,
					 'active_callback' => 'is_front_page'
				 ) );
$wp_customize->add_setting( 'col-count',
				 array(
					'default' => 2,
					'sanitize_callback' => 'absint'
				 ) );
$wp_customize->add_control( 'panel-count',
				 array(
					'settings' => 'col-count',
					'label' => __( 'How many panels on this section', 'text-domain' ),
					'type' => 'radio',
					'choices' => array( '2' => 2, '3' => 3 ),
					'section' => 'demo-front-sections',

				 ) );

// Register the page settings to get the IDs
$wp_customize->add_setting( 'page-1',
				 array(
					'default' => 0,
					'sanitize_callback' => 'absint'
				 ) );
$wp_customize->add_setting( 'page-2',
				 array(
					'default' => 0,
					'sanitize_callback' => 'absint'
				 ) );
$wp_customize->add_setting( 'page-3',
				 array(
					'default' => 0,
					'sanitize_callback' => 'absint'
				 ) );

// Set the controls
$wp_customize->add_control( 'panel-1',
				 array(
					 'settings' => 'page-1',
					 'label' => __( 'Pick what page you would like to showcase', 'text-domain' ),
					 'type' => 'dropdown-pages',
					 'section' => 'demo-front-sections'
				 ) );
$wp_customize->add_control( 'panel-2',
				 array(
					 'settings' => 'page-2',
					 'label' => __( 'Pick what page you would like to showcase', 'text-domain' ),
					 'type' => 'dropdown-pages',
					 'section' => 'demo-front-sections'
				 ) );
$wp_customize->add_control( 'panel-3',
				 array(
					 'settings' => 'page-3',
					 'label' => __( 'Pick what page you would like to showcase', 'text-domain' ),
					 'type' => 'dropdown-pages',
					 'section' => 'demo-front-sections',
					 'active_callback' => 'panel_countcheck'
				 ) );

You’ll notice I used a basic active callback for the section so that the section will only show up on the front page. You may also notice that I’ve added a callback to the third control. We haven’t created that callback so let’s do it:

function panel_countcheck( $value ){
	if ( get_theme_mod( 'col-count' ) > 2 ){
		return true;
	}
	return false;
}

Great! Now what does it do? Let’s break it down a little. So, if the first value ( col-count ) is greater than 2, then let’s show this control, otherwise we won’t. Pretty self-explanatory, right?

Great now we just need to display this on a page template, or the front page ( in our case ). We use get_theme_mod to get the values and the code will look a little like:

// get setting for how many
$count = intval( get_theme_mod( 'col-count', 2 ) );
// create an array for the new query
$ids= array();
// get the pages
$ids[] = intval( get_theme_mod( 'page-1', 0 ) );
$ids[] = intval( get_theme_mod( 'page-2', 0 ) );
if ( $count > 2 ){
	$ids[] = intval( get_theme_mod( 'page-3', 0 ) );
}

$query = new WP_Query( array( 'post_type' => 'page', 'post__in' => $ids, 'orderby' => 'post__in' ) );

switch( $count ){
	case 2:
		while( $query->have_posts() ): $query->the_post();
			echo '<div class="col-2">';
				the_title( '<h2>', '</h2>'); the_content();
			echo '</div>';
		endwhile;
	wp_reset_postdata();
	break;
	case 3:
		while( $query->have_posts() ): $query->the_post();
			echo '<div class="col-3">';
				the_title( '<h2>', '</h2>'); the_content();
			echo '</div>';
		endwhile;
	wp_reset_postdata();
	break;
}

Yeah, it is a little messy but we will break it down and explain what does what.

The first setting we get is the column count. You’ll see that I am using intval because we want to make sure that we are setting an integer. The value being passed from the customizer setting is an ID number that corresponds to a page ID, so we make sure that it really is an integer.

$count = intval( get_theme_mod( 'col-count', 2 ) );

Next up, we get our pages and build up the array that we will pass our new WP_Query. As you can see, we first get the first two pages and conditionally add the third if the $count is greater than 2. Remember this is the setting that the user picks.

$ids[] = intval( get_theme_mod( 'page-1', 0 ) );
$ids[] = intval( get_theme_mod( 'page-2', 0 ) );
if ( $count > 2 ){
	$ids[] = intval( get_theme_mod( 'page-3', 0 ) );
}

From there, we build up the new query. Pretty simple. We make sure that the post type is a page, and the order is maintained by what we pass it.

$query = new WP_Query( array( 'post_type' => 'page', 'post__in' => $ids, 'orderby' => 'post__in' ) );

Now we get to the switch statement! This is where the fun sort of begins.

switch( $count ){
	case 2:
		while( $query->have_posts() ): $query->the_post();
			echo '<div class="col-2">';
				the_title( '<h2>', '</h2>'); the_content();
			echo '</div>';
		endwhile;
	wp_reset_postdata();
	break;
	case 3:
		while( $query->have_posts() ): $query->the_post();
			echo '<div class="col-3">';
				the_title( '<h2>', '</h2>'); the_content();
			echo '</div>';
		endwhile;
	wp_reset_postdata();
	break;
}

Okay. Let’s look over the lines a bit. As you can see we pass it the $count variable we first created, which houses the option of whether to show 2 pages or 3. We create a case for each one and as you can see the only real difference here is the class. For showing two pages it is 2-col and for the 3 it is 3-col. You can create a quick wrapper for that if you want so it can be extended by a child theme like:

function jc_classes(){
	$classes = array();
	$classes[] = 'span';
	$cols = get_theme_mod( 'col-count', 2 );
	switch( $cols ){
		case 2:
		$classes[] = 'col-2';
		break;
		case 3:
		$classes[] = 'col-2';
		break;
	}
	
	return 'class="' . esc_attr( implode( ' ', apply_filters( 'jc_custom_classes', $classes ) ) ) . '"';
}

From there we can lose the switch statement and use:

$query = new WP_Query( array( 'post_type' => 'page', 'post__in' => $ids, 'orderby' => 'post__in' ) );
while( $query->have_posts() ): $query->the_post();
	echo '<div ' . jc_classes() .'>';
		the_title( '<h2>', '</h2>'); the_content();
	echo '</div>';
endwhile;
wp_reset_postdata();

What’s really nice is you can use basic CSS to style it. A quick snippet:

.col-2 {
	float: left;
	width: 50%;
}

.col-3 {
	float: left;
	width: 33%;
}

So, go on, experiment, and fiddle around with the code a bit. Do remember I used basic things, so be sure to add the proper settings, text, and values for your needs.