Advertisement
  1. Code
  2. WordPress

Dynamic Page Templates in WordPress, Part 2

Scroll to top
Read Time: 14 min
This post is part of a series called Dynamic Page Templates in WordPress.
Dynamic Page Templates in WordPress, Part 1
Dynamic Page Templates in WordPress, Part 3

In part 1 of this tutorial series I introduced the basic concept of dynamic page templates and created a standard page template as a foundation for future work. A WordPress child theme based on the Twenty Seventeen parent theme was used to implement the page template.

In this tutorial, you'll learn more specific details about dynamic page templates and how you can use them in your own WordPress projects.

I'll also show you step by step how to extend the page template from the first tutorial and turn it into your very first working dynamic page template!

Dynamic Page Templates: A More Flexible Approach

So how can we make page templates more flexible, and why would this be useful anyway?

Let's say that you have a portfolio page template that outputs a gallery of individual portfolio items. Each item would be a fixed size, and only so many could fit onto each row.

We could, of course, add another page template to display portfolios at different sizes. But what if we wanted to show small, medium, or large portfolios? For this, we would need three separate page templates, with each one having different sizes for the portfolio items.

This could instantly be made more flexible by adding a drop-down control to select the portfolio size (i.e. Small, Medium, or Large). This is more convenient to the user as the page template box is less cluttered with unnecessary choices.

It makes more sense for the developer too, who only has one page template to maintain rather than three! This follows the Don't Repeat Yourself (DRY) software development principle.

Should All Page Templates Be Dynamic?

It's worth noting that not every page template would necessarily benefit from being dynamic. If you have a page template that does one thing, and one thing only, then that's fine. It wouldn't make sense to add custom controls, and extra logic, to a page template in this case.

But, as you'll see by the end of this tutorial, many page templates would benefit greatly from being more flexible.

Another useful example for a dynamic page template would be a contact form. There are many custom controls that could be added to make this page template highly flexible.

For example, rather than output a fixed set of form fields, page template controls could be added to allow you to omit certain fields. Or maybe a Captcha field could be optionally displayed to prevent spam form submissions? There are so just many ways you could customize a contact form.

I'll be creating a dynamic form page template in part 3 of this tutorial series. But first let's get started with creating a general purpose dynamic page template.

Our First Dynamic Page Template

To begin with, we'll be creating a basic dynamic page template to demonstrate how all the component parts fit together. Custom controls will be added to the page editor which will be used later on to control page template output.

The custom page template controls we'll be adding shortly are:

  • Text box
  • Textarea
  • Check box
  • Radio buttons
  • Drop-down select box

Ideally, the controls should be added directly below the page template drop-down box to make it obvious they're related to a page template.

However, WordPress doesn't provide any hooks for this, so you'll have to make do (for now) with adding custom page template controls to a separate custom meta box. In part three of this tutorial series, I'll show you how to get round this problem.

Hooks are fundamental to WordPress development. They allow developers to extend the code base without having to resort to editing core files, which is generally considered a bad idea. This is because any custom code would be wiped out whenever a WordPress update is performed (which can happen quite regularly).

To display our meta box on the page editor screen, add the load-post.php and load-post-new.php hooks to the child theme init() method we created in part 1.

1
<?php
2
add_action( 'load-post.php', array( $this, 'page_template_meta_box' ) );
3
add_action( 'load-post-new.php', array( $this, 'page_template_meta_box' ) );
4
add_action( 'save_post', array( $this, 'save_page_template_meta' ), 10, 2 );
5
}

We use two WordPress hooks to ensure the meta box displays on the page editor, whether you're creating a new page or editing an existing one. There is also a save_post hook that handles saving of post meta data, which I'll cover a bit later on.

Add the following four class methods to create, display, and save data for the meta box.

1
/* Add meta box hook. */
2
public function page_template_meta_box() {
3
  add_action( 'add_meta_boxes', array( $this, 'add_page_template_meta_box' ) );
4
}
1
/* Register meta box. */
2
public function add_page_template_meta_box() {
3
  add_meta_box(
4
    'page-template-meta-box',
5
	esc_html__( 'Page Template Meta Box', 'twenty-seventeen-child' ),
6
	array( $this, 'display_page_template_meta_box' ),
7
	'page',
8
	'side',
9
	'default'
10
  );
11
}
1
/* Render meta box on the page editor. */
2
public function display_page_template_meta_box($object) {  
3
  wp_nonce_field( basename( __FILE__ ), 'page_template_meta_box_nonce' );
4
}

I won't go into too much detail here about WordPress meta boxes, as that could be a whole tutorial on its own, but note the following points about the code I added above:

  • The page_template_meta_box() and add_page_template_meta_box() class methods register the meta box with WordPress.
  • In add_page_template_meta_box(), the 'page' parameter specifies that this meta box will only be displayed on the 'page' post type editor in the WordPress admin.
  • The display_page_template_meta_box() class method renders the meta box and sets up a nonce to make the form controls more secure.

If all is well, you should now have a meta box displayed on the page editor, as shown below.

A new page meta boxA new page meta boxA new page meta box

It's a bit empty at the moment, though, so let's add some controls.

Adding Custom Controls

If you'll recall from above, we're going to add a text box, text area, checkbox, radio button, and select box controls to the meta box. Start by adding the following code to the display_page_template_meta_box() method underneath the nonce function.

1
<?php
2
$text = get_post_meta( $object->ID, 'page_template_text', true );
3
$textarea = get_post_meta( $object->ID, 'page_template_textarea', true );
4
$checkbox = get_post_meta( $object->ID, 'page_template_chk', true );
5
$radio = get_post_meta( $object->ID, 'page_template_radio', true );
6
$select = get_post_meta( $object->ID, 'page_template_select', true );

This retrieves the current values of our meta box controls and stores them in local variables. Now add the following HTML directly after, to render the meta box controls.

1
  ?>
2
  <div>
3
	<p>
4
	    <label for="page-template-text"><?php _e( "Text Control", 'twenty-seventeen-child' ); ?></label><br>
5
	    <input class="widefat" type="text" name="page-template-text" id="page-template-text" value="<?php echo esc_attr( $text ); ?>" />
6
	</p>
7
8
	<p>
9
	    <label for="page-template-textarea"><?php _e( "Textarea Control", 'twenty-seventeen-child' ); ?></label><br>
10
	    <textarea rows="5" class="widefat" name="page-template-textarea" id="page-template-textarea"><?php echo esc_attr( $textarea ); ?></textarea>
11
	</p>
12
13
	<p>
14
	    <input type="checkbox" name="page-template-chk" id="page-template-chk" value="1" <?php checked($checkbox, true); ?> />&nbsp;<label for="page-template-chk"><?php _e( "Checkbox Control", 'twenty-seventeen-child' ); ?></label><br>
15
	</p>
16
17
	<p>
18
	    <label for="page-template-align"><?php _e( "Radio Button Control", 'twenty-seventeen-child' ); ?></label><br>
19
	    <input type="radio" name="page-template-align" id="rdo-left" value="left" <?php checked( $radio, 'left' ); ?> ><label for="rdo-left"><?php _e( 'Left', 'twenty-seventeen-child' ); ?></label><br>
20
	    <input type="radio" name="page-template-align" id="rdo-right" value="right" <?php checked( $radio, 'right' ); ?> ><label for="rdo-right"><?php _e( 'Right', 'twenty-seventeen-child' ); ?></label><br>
21
	    <input type="radio" name="page-template-align" id="rdo-center" value="center" <?php checked( $radio, 'center' ); ?> ><label for="rdo-center"><?php _e( 'Center', 'twenty-seventeen-child' ); ?></label><br>
22
	</p>
23
24
	<p>
25
	    <label for="page-template-select">Dropdown</label>
26
	    <select name="page-template-select" class="widefat">
27
		    <option	value='one' <?php selected( 'one', $select ); ?>><?php _e( 'One', 'twenty-seventeen-child' ); ?></option>
28
		    <option	value='two' <?php selected( 'two', $select ); ?>><?php _e( 'Two', 'twenty-seventeen-child' ); ?></option>
29
		    <option	value='three' <?php selected( 'three', $select ); ?>><?php _e( 'Three', 'twenty-seventeen-child' ); ?></option>
30
		    <option	value='four' <?php selected( 'four', $select ); ?>><?php _e( 'Four', 'twenty-seventeen-child' ); ?></option>
31
	    </select>
32
	</p>
33
</div><?php

Each control is contained inside a paragraph tag, and its current value is updated via the local variable we created earlier. This ensures that the meta box controls always display the correct settings.

However, this won't happen unless we save the current meta box control data to the WordPress database.

A little earlier, I registered a hook to execute a class method every time the page editor was updated. Let's add that method to our child theme class now.

1
<?php
2
/* Save meta box data. */
3
public function save_page_template_meta( $post_id, $post ) {
4
5
    if ( ! ( isset( $_POST[ 'page_template_meta_box_nonce' ] ) && wp_verify_nonce( $_POST[ 'page_template_meta_box_nonce' ], basename( __FILE__ ) ) ) ) {
6
	    return $post_id;
7
    }
8
9
    if ( ! current_user_can( 'edit_post', $post_id ) ) {
10
        return $post_id;
11
    }
12
13
    if( 'page' != $post->post_type ) {
14
	    return $post_id;
15
    }
16
17
    $page_template_text_value = isset( $_POST[ 'page-template-text' ] ) ? $_POST[ 'page-template-text' ] : '';
18
    update_post_meta( $post_id, 'page_template_text', $page_template_text_value );
19
20
    $page_template_textarea_value = isset( $_POST[ 'page-template-textarea' ] ) ? $_POST[ 'page-template-textarea' ] : '';
21
    update_post_meta( $post_id, 'page_template_textarea', $page_template_textarea_value );
22
23
    $page_template_chk_value = isset( $_POST[ 'page-template-chk' ] ) ? $_POST[ 'page-template-chk' ] : '';
24
    update_post_meta( $post_id, 'page_template_chk', $page_template_chk_value );
25
26
    $page_template_radio_value = isset( $_POST[ 'page-template-align' ] ) ? $_POST[ 'page-template-align' ] : '';
27
    update_post_meta( $post_id, 'page_template_radio', $page_template_radio_value );
28
29
    $page_template_select_value = isset( $_POST[ 'page-template-select' ]) ? $_POST[ 'page-template-select' ] : '';
30
    update_post_meta( $post_id, 'page_template_select', $page_template_select_value );
31
}

The save_page_template_meta() class method handles saving the meta box control data. It only saves the meta box data if the nonce is verified, the current users can edit posts, and we are on the page editor admin screen.

If those conditions are satisfied, then we extract the data for each control which is stored in the global $_POST variable. This variable is set every time a form is submitted.

Finally, the meta box control data is saved to the WordPress database as meta data for the current page.

With the custom page template controls added, our meta box should look like this.

Adding controls to the meta boxAdding controls to the meta boxAdding controls to the meta box

Enter some text for the text box and textarea, and make selections for the check box, radio button, and select box. Hit update to save your changes, and when the page editor reloads, your meta box controls should show the data you just entered.

Populating the meta boxPopulating the meta boxPopulating the meta box

The full source code for the child theme functions.php file is shown below.

1
<?php
2
3
/**

4
 * Twenty Seventeen child theme class.

5
 *

6
 * DPT = D[ynamic] P[age] T[emplates].

7
 */
8
class DPT_Twenty_Seventeen_Child {
9
10
    /**

11
	 * Register hooks.

12
	 */
13
	public function init() {
14
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_parent_theme_styles' ) );
15
		add_action( 'load-post.php', array( $this, 'page_template_meta_box' ) );
16
		add_action( 'load-post-new.php', array( $this, 'page_template_meta_box' ) );
17
		add_action( 'save_post', array( $this, 'save_page_template_meta' ), 10, 2 );
18
	}
19
20
	/* Enqueue parent theme styles. */
21
	public function enqueue_parent_theme_styles() {
22
		wp_enqueue_style( 'twenty-seventeen-css', get_template_directory_uri() . '/style.css' );
23
	}
24
25
	/* Add meta box hook. */
26
	public function page_template_meta_box() {
27
	  add_action( 'add_meta_boxes', array( $this, 'add_page_template_meta_box' ) );
28
	}
29
30
	/* Register meta box. */
31
	public function add_page_template_meta_box() {
32
	  add_meta_box(
33
		'page-template-meta-box',
34
		esc_html__( 'Page Template Meta Box', 'twenty-seventeen-child' ),
35
		array( $this, 'display_page_template_meta_box' ),
36
		'page',
37
		'side',
38
		'default'
39
	  );
40
	}
41
42
	/* Render meta box on the page editor. */
43
	public function display_page_template_meta_box( $object ) {
44
45
	  wp_nonce_field( basename( __FILE__ ), 'page_template_meta_box_nonce' );
46
47
	  $text = get_post_meta( $object->ID, 'page_template_text', true );
48
	  $textarea = get_post_meta( $object->ID, 'page_template_textarea', true );
49
      $checkbox = get_post_meta( $object->ID, 'page_template_chk', true );
50
      $radio = get_post_meta( $object->ID, 'page_template_radio', true );
51
      $select = get_post_meta( $object->ID, 'page_template_select', true );
52
	  ?>
53
	  <div>
54
		<p>
55
			<label for="page-template-text"><?php _e( "Text Control", 'twenty-seventeen-child' ); ?></label><br>
56
			<input class="widefat" type="text" name="page-template-text" id="page-template-text" value="<?php echo esc_attr( $text ); ?>" />
57
		</p>
58
59
		<p>
60
			<label for="page-template-textarea"><?php _e( "Textarea Control", 'twenty-seventeen-child' ); ?></label><br>
61
			<textarea rows="5" class="widefat" name="page-template-textarea" id="page-template-textarea"><?php echo esc_attr( $textarea ); ?></textarea>
62
		</p>
63
64
		<p>
65
			<input type="checkbox" name="page-template-chk" id="page-template-chk" value="1" <?php checked($checkbox, true); ?> />&nbsp;<label for="page-template-chk"><?php _e( "Checkbox Control", 'twenty-seventeen-child' ); ?></label><br>
66
		</p>
67
68
		<p>
69
			<label for="page-template-align"><?php _e( "Radio Button Control", 'twenty-seventeen-child' ); ?></label><br>
70
			<input type="radio" name="page-template-align" id="rdo-left" value="left" <?php checked( $radio, 'left' ); ?> ><label for="rdo-left"><?php _e( 'Left', 'twenty-seventeen-child' ); ?></label><br>
71
			<input type="radio" name="page-template-align" id="rdo-right" value="right" <?php checked( $radio, 'right' ); ?> ><label for="rdo-right"><?php _e( 'Right', 'twenty-seventeen-child' ); ?></label><br>
72
			<input type="radio" name="page-template-align" id="rdo-center" value="center" <?php checked( $radio, 'center' ); ?> ><label for="rdo-center"><?php _e( 'Center', 'twenty-seventeen-child' ); ?></label><br>
73
		</p>
74
75
		<p>
76
			<label for="page-template-select">Dropdown</label>
77
			<select name="page-template-select" class="widefat">
78
				<option	value='one' <?php selected( 'one', $select ); ?>><?php _e( 'One', 'twenty-seventeen-child' ); ?></option>
79
				<option	value='two' <?php selected( 'two', $select ); ?>><?php _e( 'Two', 'twenty-seventeen-child' ); ?></option>
80
				<option	value='three' <?php selected( 'three', $select ); ?>><?php _e( 'Three', 'twenty-seventeen-child' ); ?></option>
81
				<option	value='four' <?php selected( 'four', $select ); ?>><?php _e( 'Four', 'twenty-seventeen-child' ); ?></option>
82
			</select>
83
		</p>
84
	  </div><?php
85
	}
86
87
	/* Save meta box data. */
88
	public function save_page_template_meta( $post_id, $post ) {
89
90
	  if ( ! ( isset( $_POST[ 'page_template_meta_box_nonce' ] ) && wp_verify_nonce( $_POST[ 'page_template_meta_box_nonce' ], basename( __FILE__ ) ) ) ) {
91
		return $post_id;
92
	  }
93
94
	  if ( ! current_user_can( 'edit_post', $post_id ) ) {
95
		return $post_id;
96
	  }
97
98
	  if( 'page' != $post->post_type ) {
99
		return $post_id;
100
	  }
101
102
	  $page_template_text_value = isset( $_POST[ 'page-template-text' ] ) ? $_POST[ 'page-template-text' ] : '';
103
	  update_post_meta( $post_id, 'page_template_text', $page_template_text_value );
104
105
  	  $page_template_textarea_value = isset( $_POST[ 'page-template-textarea' ] ) ? $_POST[ 'page-template-textarea' ] : '';
106
	  update_post_meta( $post_id, 'page_template_textarea', $page_template_textarea_value );
107
108
  	  $page_template_chk_value = isset( $_POST[ 'page-template-chk' ] ) ? $_POST[ 'page-template-chk' ] : '';
109
	  update_post_meta( $post_id, 'page_template_chk', $page_template_chk_value );
110
111
   	  $page_template_radio_value = isset( $_POST[ 'page-template-align' ] ) ? $_POST[ 'page-template-align' ] : '';
112
	  update_post_meta( $post_id, 'page_template_radio', $page_template_radio_value );
113
114
   	  $page_template_select_value = isset( $_POST[ 'page-template-select' ]) ? $_POST[ 'page-template-select' ] : '';
115
	  update_post_meta( $post_id, 'page_template_select', $page_template_select_value );
116
	}
117
}
118
119
$ts_child_theme = new DPT_Twenty_Seventeen_Child();
120
$ts_child_theme->init();

The final piece of the puzzle is to use the meta box control data in our page template on the front end. Open up the test-page-template.php file we created in part 1 and replace the contents with this updated code.

1
<?php
2
/**

3
 * Template Name: Test Page Template

4
 *

5
 * @package WordPress

6
 * @subpackage Twenty_Seventeen

7
 * @since 1.0

8
 */
9
10
get_header(); ?>
11
12
<div class="wrap">
13
    <div id="primary" class="content-area">
14
		<main id="main" class="site-main" role="main">
15
16
			<?php
17
			while ( have_posts() ) : the_post();
18
19
				$text = get_post_meta( get_the_ID(), 'page_template_text', true );
20
				$textarea = get_post_meta( get_the_ID(), 'page_template_textarea', true );
21
				$checkbox = get_post_meta( get_the_ID(), 'page_template_chk', true );
22
				$radio = get_post_meta( get_the_ID(), 'page_template_radio', true );
23
				$select = get_post_meta( get_the_ID(), 'page_template_select', true );
24
25
				echo "<p>Text Box: " . $text . "</p>";
26
				echo "<p>Text Area: " . $textarea . "</p>";
27
				echo "<p>Checkbox: " . $checkbox . "</p>";
28
				echo "<p>Radio Buttons: " . $radio . "</p>";
29
				echo "<p>Dropdown: " . $select . "</p>";
30
31
				echo "<h2>Sitemap</h2>";
32
				echo "<ul>" . wp_list_pages( array( 'title_li' => '' ) ) . "</ul>";
33
34
			endwhile; // End of the loop.

35
			?>
36
37
		</main><!-- #main -->
38
	</div><!-- #primary -->
39
</div><!-- .wrap -->
40
41
<?php get_footer();

Make sure the 'Test Page Template' is the currently selected page template and view the page on the front end.

Viewing the output of the meta boxViewing the output of the meta boxViewing the output of the meta box

As you can see, the page template now includes the values just set for the meta box controls on the page editor. This is core to the rest of the tutorial as we'll build on this basic example and create various examples of fully working dynamic page templates that you can use in your own WordPress projects.

Conclusion

In this tutorial, we covered how to build a functioning dynamic page template. At the moment, even though it's functional, our basic page template isn't terribly useful.

In the third and final part of this tutorial series, I'll show you how to build various dynamic page templates, from start to finish, that you can use (and expand upon) in your own WordPress projects.

If you have any questions, please leave me a message in the comments below. I'd love to hear your thoughts on the tutorial.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.