I recently worked with a client of mine, who works as a professional consultant in her area of work. She asked if I could implement a Q&A system, or a FAQ page, to be exact. I said, "Sure, we can just create a page and paste the questions and answers there with different styling," but she said she would create different pages and categorize questions and answers, and for the sake of being more organized, she needed a different approach.
To that end, I'm going to show you how I handled her requests with some simple lines of code using custom post types, taxonomies, and shortcodes.
The Custom Post Type and the Taxonomy
What do we need to build an FAQ system?
- We need fields for questions and answers.
- We need categories to classify and separate different types of questions and their answers.
- In our case, we need a shortcode to embed these question groups or all questions in a page or a post.
Let's begin by creating the custom post type.
Step 1: Creating the Custom Post Type
Naturally, we're going to start off by setting up a custom post type for our FAQ items. We're going to create a new custom post type with the help of the register_post_type()
function, but if you want a GUI for creating your post type, you can generate it with GenerateWP's Post Type Generator tool as I did in this example:
<?php if ( ! function_exists( 'tuts_faq_cpt' ) ) { // register custom post type function tuts_faq_cpt() { // these are the labels in the admin interface, edit them as you like $labels = array( 'name' => _x( 'FAQs', 'Post Type General Name', 'tuts_faq' ), 'singular_name' => _x( 'FAQ', 'Post Type Singular Name', 'tuts_faq' ), 'menu_name' => __( 'FAQ', 'tuts_faq' ), 'parent_item_colon' => __( 'Parent Item:', 'tuts_faq' ), 'all_items' => __( 'All Items', 'tuts_faq' ), 'view_item' => __( 'View Item', 'tuts_faq' ), 'add_new_item' => __( 'Add New FAQ Item', 'tuts_faq' ), 'add_new' => __( 'Add New', 'tuts_faq' ), 'edit_item' => __( 'Edit Item', 'tuts_faq' ), 'update_item' => __( 'Update Item', 'tuts_faq' ), 'search_items' => __( 'Search Item', 'tuts_faq' ), 'not_found' => __( 'Not found', 'tuts_faq' ), 'not_found_in_trash' => __( 'Not found in Trash', 'tuts_faq' ), ); $args = array( // use the labels above 'labels' => $labels, // we'll only need the title, the Visual editor and the excerpt fields for our post type 'supports' => array( 'title', 'editor', 'excerpt', ), // we're going to create this taxonomy in the next section, but we need to link our post type to it now 'taxonomies' => array( 'tuts_faq_tax' ), // make it public so we can see it in the admin panel and show it in the front-end 'public' => true, // show the menu item under the Pages item 'menu_position' => 20, // show archives, if you don't need the shortcode 'has_archive' => true, ); register_post_type( 'tuts_faq', $args ); } // hook into the 'init' action add_action( 'init', 'tuts_faq_cpt', 0 ); } ?>
Tip: If your project is going to involve more custom post types that can be more complex than this simple FAQ post type, I can suggest a cool tool called SuperCPT which allows you to create new post types with even simpler code. I've written a tutorial about SuperCPT, too, you can check it out here.
Step 2: Creating the Custom Taxonomy
In order to separate different types of questions (like my client's questions and answers about miscarriage and postpartum depression), we're going to need a category system. As many of you already know, WordPress provides this functionality with custom taxonomies.
The essential function here is register_taxonomy()
but again, you can use GenerateWP's Taxonomy Generator tool if you need a graphical interface.
Here's the code:
<?php if ( ! function_exists( 'tuts_faq_tax' ) ) { // register custom taxonomy function tuts_faq_tax() { // again, labels for the admin panel $labels = array( 'name' => _x( 'FAQ Categories', 'Taxonomy General Name', 'tuts_faq' ), 'singular_name' => _x( 'FAQ Category', 'Taxonomy Singular Name', 'tuts_faq' ), 'menu_name' => __( 'FAQ Categories', 'tuts_faq' ), 'all_items' => __( 'All FAQ Cats', 'tuts_faq' ), 'parent_item' => __( 'Parent FAQ Cat', 'tuts_faq' ), 'parent_item_colon' => __( 'Parent FAQ Cat:', 'tuts_faq' ), 'new_item_name' => __( 'New FAQ Cat', 'tuts_faq' ), 'add_new_item' => __( 'Add New FAQ Cat', 'tuts_faq' ), 'edit_item' => __( 'Edit FAQ Cat', 'tuts_faq' ), 'update_item' => __( 'Update FAQ Cat', 'tuts_faq' ), 'separate_items_with_commas' => __( 'Separate items with commas', 'tuts_faq' ), 'search_items' => __( 'Search Items', 'tuts_faq' ), 'add_or_remove_items' => __( 'Add or remove items', 'tuts_faq' ), 'choose_from_most_used' => __( 'Choose from the most used items', 'tuts_faq' ), 'not_found' => __( 'Not Found', 'tuts_faq' ), ); $args = array( // use the labels above 'labels' => $labels, // taxonomy should be hierarchial so we can display it like a category section 'hierarchical' => true, // again, make the taxonomy public (like the post type) 'public' => true, ); // the contents of the array below specifies which post types should the taxonomy be linked to register_taxonomy( 'tuts_faq_tax', array( 'tuts_faq' ), $args ); } // hook into the 'init' action add_action( 'init', 'tuts_faq_tax', 0 ); } ?>
That's it! Now you have an FAQ post type with a taxonomy called "FAQ Categories" linked to each other! Check your administration panel and you'll see the "FAQ Categories" menu item under the "FAQ".
Just like regular post categories, you can add, edit or remove them in the "FAQ Categories" page, or you can add new categories while you're writing a new FAQ item.
Step 3: Creating the [faq]
Shortcode
Here comes the fun part: building the shortcode. (If you've read my previous posts, you know that I'm a huge fan of WordPress shortcodes.) We're basically going to make the FAQ items embeddable into posts and pages.
Here's what's going to happen:
- query inside our new custom post type,
- filter its categories with a shortcode parameter,
- display the questions and answers as titles and content,
- show an excerpt of the answer with a "More..." link, controlled by another shortcode parameter.
Let's begin building the shortcode. Like the code above, I'm going to include some helpful comments:
<?php if ( ! function_exists( 'tuts_faq_shortcode' ) ) { function tuts_faq_shortcode( $atts ) { extract( shortcode_atts( array( // category slug attribute - defaults to blank 'category' => '', // full content or excerpt attribute - defaults to full content 'excerpt' => 'false', ), $atts ) ); $output = ''; // set the query arguments $query_args = array( // show all posts matching this query 'posts_per_page' => -1, // show the 'tuts_faq' custom post type 'post_type' => 'tuts_faq', // show the posts matching the slug of the FAQ category specified with the shortcode's attribute 'tax_query' => array( array( 'taxonomy' => 'tuts_faq_tax', 'field' => 'slug', 'terms' => $category, ) ), // tell WordPress that it doesn't need to count total rows - this little trick reduces load on the database if you don't need pagination 'no_found_rows' => true, ); // get the posts with our query arguments $faq_posts = get_posts( $query_args ); $output .= '<div class="tuts-faq">'; // handle our custom loop foreach ( $faq_posts as $post ) { setup_postdata( $post ); $faq_item_title = get_the_title( $post->ID ); $faq_item_permalink = get_permalink( $post->ID ); $faq_item_content = get_the_content(); if( $excerpt == 'true' ) $faq_item_content = get_the_excerpt() . '<a href="' . $faq_item_permalink . '">' . __( 'More...', 'tuts_faq' ) . '</a>'; $output .= '<div class="tuts-faq-item">'; $output .= '<h3 class="tuts-faq-item-title">' . $faq_item_title . '</h3>'; $output .= '<div class="tuts-faq-item-content">' . $faq_item_content . '</div>'; $output .= '</div>'; } wp_reset_postdata(); $output .= '</div>'; return $output; } add_shortcode( 'faq', 'tuts_faq_shortcode' ); } ?>
That's it! Now we have a neat shortcode to embed our questions and answers. You can style it with the class names tuts-faq
, tuts-faq-item
, tuts-faq-item-title
, and tuts-faq-item-content
. Although, it should be fine even if you don't include additional styling.
Step 4: Wrapping Up the Code
Since these bits of code aren't just about styling the front-end but also introducing new functionality, it counts as plugin territory. That's why we must save the code as a plugin. And while we're at it, we should also flush the rewrite rules upon activation and deactivation.
Here's the full code:
<?php /* Plugin Name: Simple FAQ System Plugin URI: http://code.tutsplus.com/ Description: Helps you create an FAQ section for your WordPress website. Shortcode usage: <code>[faq]</code> Version: 1.0 Author: Barış Ünver Author URI: http://hub.tutsplus.com/authors/baris-unver License: Public Domain */ if ( ! function_exists( 'tuts_faq_cpt' ) ) { // register custom post type function tuts_faq_cpt() { // these are the labels in the admin interface, edit them as you like $labels = array( 'name' => _x( 'FAQs', 'Post Type General Name', 'tuts_faq' ), 'singular_name' => _x( 'FAQ', 'Post Type Singular Name', 'tuts_faq' ), 'menu_name' => __( 'FAQ', 'tuts_faq' ), 'parent_item_colon' => __( 'Parent Item:', 'tuts_faq' ), 'all_items' => __( 'All Items', 'tuts_faq' ), 'view_item' => __( 'View Item', 'tuts_faq' ), 'add_new_item' => __( 'Add New FAQ Item', 'tuts_faq' ), 'add_new' => __( 'Add New', 'tuts_faq' ), 'edit_item' => __( 'Edit Item', 'tuts_faq' ), 'update_item' => __( 'Update Item', 'tuts_faq' ), 'search_items' => __( 'Search Item', 'tuts_faq' ), 'not_found' => __( 'Not found', 'tuts_faq' ), 'not_found_in_trash' => __( 'Not found in Trash', 'tuts_faq' ), ); $args = array( // use the labels above 'labels' => $labels, // we'll only need the title, the Visual editor and the excerpt fields for our post type 'supports' => array( 'title', 'editor', 'excerpt', ), // we're going to create this taxonomy in the next section, but we need to link our post type to it now 'taxonomies' => array( 'tuts_faq_tax' ), // make it public so we can see it in the admin panel and show it in the front-end 'public' => true, // show the menu item under the Pages item 'menu_position' => 20, // show archives, if you don't need the shortcode 'has_archive' => true, ); register_post_type( 'tuts_faq', $args ); } // hook into the 'init' action add_action( 'init', 'tuts_faq_cpt', 0 ); } if ( ! function_exists( 'tuts_faq_tax' ) ) { // register custom taxonomy function tuts_faq_tax() { // again, labels for the admin panel $labels = array( 'name' => _x( 'FAQ Categories', 'Taxonomy General Name', 'tuts_faq' ), 'singular_name' => _x( 'FAQ Category', 'Taxonomy Singular Name', 'tuts_faq' ), 'menu_name' => __( 'FAQ Categories', 'tuts_faq' ), 'all_items' => __( 'All FAQ Cats', 'tuts_faq' ), 'parent_item' => __( 'Parent FAQ Cat', 'tuts_faq' ), 'parent_item_colon' => __( 'Parent FAQ Cat:', 'tuts_faq' ), 'new_item_name' => __( 'New FAQ Cat', 'tuts_faq' ), 'add_new_item' => __( 'Add New FAQ Cat', 'tuts_faq' ), 'edit_item' => __( 'Edit FAQ Cat', 'tuts_faq' ), 'update_item' => __( 'Update FAQ Cat', 'tuts_faq' ), 'separate_items_with_commas' => __( 'Separate items with commas', 'tuts_faq' ), 'search_items' => __( 'Search Items', 'tuts_faq' ), 'add_or_remove_items' => __( 'Add or remove items', 'tuts_faq' ), 'choose_from_most_used' => __( 'Choose from the most used items', 'tuts_faq' ), 'not_found' => __( 'Not Found', 'tuts_faq' ), ); $args = array( // use the labels above 'labels' => $labels, // taxonomy should be hierarchial so we can display it like a category section 'hierarchical' => true, // again, make the taxonomy public (like the post type) 'public' => true, ); // the contents of the array below specifies which post types should the taxonomy be linked to register_taxonomy( 'tuts_faq_tax', array( 'tuts_faq' ), $args ); } // hook into the 'init' action add_action( 'init', 'tuts_faq_tax', 0 ); } if ( ! function_exists( 'tuts_faq_shortcode' ) ) { function tuts_faq_shortcode( $atts ) { extract( shortcode_atts( array( // category slug attribute - defaults to blank 'category' => '', // full content or excerpt attribute - defaults to full content 'excerpt' => 'false', ), $atts ) ); $output = ''; // set the query arguments $query_args = array( // show all posts matching this query 'posts_per_page' => -1, // show the 'tuts_faq' custom post type 'post_type' => 'tuts_faq', // show the posts matching the slug of the FAQ category specified with the shortcode's attribute 'tax_query' => array( array( 'taxonomy' => 'tuts_faq_tax', 'field' => 'slug', 'terms' => $category, ) ), // tell WordPress that it doesn't need to count total rows - this little trick reduces load on the database if you don't need pagination 'no_found_rows' => true, ); // get the posts with our query arguments $faq_posts = get_posts( $query_args ); $output .= '<div class="tuts-faq">'; // handle our custom loop foreach ( $faq_posts as $post ) { setup_postdata( $post ); $faq_item_title = get_the_title( $post->ID ); $faq_item_permalink = get_permalink( $post->ID ); $faq_item_content = get_the_content(); if( $excerpt == 'true' ) $faq_item_content = get_the_excerpt() . '<a href="' . $faq_item_permalink . '">' . __( 'More...', 'tuts_faq' ) . '</a>'; $output .= '<div class="tuts-faq-item">'; $output .= '<h2 class="faq-item-title">' . $faq_item_title . '</h2>'; $output .= '<div class="faq-item-content">' . $faq_item_content . '</div>'; $output .= '</div>'; } wp_reset_postdata(); $output .= '</div>'; return $output; } add_shortcode( 'faq', 'tuts_faq_shortcode' ); } function tuts_faq_activate() { tuts_faq_cpt(); flush_rewrite_rules(); } register_activation_hook( __FILE__, 'tuts_faq_activate' ); function tuts_faq_deactivate() { flush_rewrite_rules(); } register_deactivation_hook( __FILE__, 'tuts_faq_deactivate' ); ?>
Room for Improvement
My client was happy with the results when I showed her how to use it. But here, we can expand the code with more functionality, like...
- Accordion Effect: If you'd like to make your FAQ sections more attractive with some toggle effects, you can use some terrific jQuery plugins. If you want to use jQuery UI, there's an amazing tutorial by Shane Osbourne that shows how to do it.
-
Pagination: If you have a lot of questions and answers for a category and don't want to display all the items at once, you can limit the number of posts by changing the
posts_per_page
parameter in the custom query of our shortcode, and add the required code for pagination links below the line with thewp_reset_postdata();
code. Remember to remove the'no_found_rows' => true,
line, though - pagination will not work if you don't remove that! -
Random Question: Let's say you want to display one random question and answer on homepage and you want it to change every page refresh. All you need to do is to head to the custom query, change the
posts_per_page
parameter from-1
to1
and add another line with the code'orderby' => 'random',
and you're good to go!
Conclusion
This is how you build a simple FAQ system in WordPress through the use of custom post types, custom taxonomies, and shortcodes. I hope you enjoyed this tutorial and you can utilize it in your next project. Don't forget to share the article, if you liked it!
Do you have any ideas to improve this FAQ system? Share your comments below!
Comments