There are quite a few solutions to relate posts in WordPress. Most of them plugins, adding a post2post table, some using taxonomies or post_metadata. Some days ago, I build a website for one of our clients, who wanted to manually relate posts to each other. The existing plugins didn’t properly match the requirements, so I had to implement related posts without plugin.

My approach was quite simple, I implemented two hooks which are mirroring all published pages in a related-posts-taxonomy, adding a new term for each post that gets published and delete a term when the related post gets removed. First the code to add a related_posts taxonomy, by adding this code to our functions.php:

add_action( 'init', 'register_taxonomy_related_posts' );
function register_taxonomy_related_posts() {
    $labels = array( 
        'name' => _x( 'Related posts', 'related_posts' ),
        'singular_name' => _x( 'Related post', 'related_posts' ),
        'search_items' => _x( 'Search Related posts', 'related_posts' ),
        'popular_items' => _x( 'Popular Related posts', 'related_posts' ),
        'all_items' => _x( 'All Related posts', 'related_posts' ),
        'parent_item' => _x( 'Parent Related post', 'related_posts' ),
        'parent_item_colon' => _x( 'Parent Related post:', 'related_posts' ),
        'edit_item' => _x( 'Edit Related post', 'related_posts' ),
        'update_item' => _x( 'Update Related post', 'related_posts' ),
        'add_new_item' => _x( 'Add New Related post', 'related_posts' ),
        'new_item_name' => _x( 'New Related post', 'related_posts' ),
        'separate_items_with_commas' => _x( 'Separate related posts with commas', 'related_posts' ),
        'add_or_remove_items' => _x( 'Add or remove related posts', 'related_posts' ),
        'choose_from_most_used' => _x( 'Choose from the most used related posts', 'related_posts' ),
        'menu_name' => _x( 'Related posts', 'related_posts' ),
    );

    $args = array( 
        'labels' => $labels,
        'public' => true,
        'show_in_nav_menus' => false,
        'show_ui' => true,
        'show_tagcloud' => false,
        'hierarchical' => true,
        'rewrite' => false,
        'query_var' => true
    );
    register_taxonomy( 'related_posts', array('post'), $args );
}

Now we have to hook into the save-post-action to add a term to our taxonomy, when a post gets published, or to update a term when a posts title gets changed. We use the posts title as name for the term, which is visible to the user in the taxonomy widget. But since the post-title might not be unique, the relation has to be established via the post-id. That’s why we save the post-id as the terms slug. Add the following code to the functions.php.

add_action( 'save_post', 'add_relatable_post' );
function add_relatable_post( $post_id ) {
	if ( get_post($post_id)->post_status == 'publish' ) {
		if (($term = get_term_by('slug', $post_id, 'related_posts'))) {
			wp_update_term($term->term_id, 'related_posts', array(
				'name' => get_the_title( $post_id ),
				'slug' => $post_id
			));
		} else {
			wp_insert_term( get_the_title( $post_id ), 'related_posts', array(
				'slug' => $post_id
			));
		}
	}
}

Finally we have to remove a term if its related post gets deleted. This can be done by hooking into the delete_post action, which is triggered when a post gets deleted (not moved to trash).

add_action('delete_post', 'remove_relatable_post', 10 );
function remove_relatable_post( $post_id ) {
	wp_delete_term( get_term_by('slug', $post_id, 'related_posts')->term_id, 'related_posts');
}

The admin interface is now prepared to relate posts to each other. On every post you see a widget, showing the related_posts taxonomy which should contain a list of all existing posts. Remember, that you have to update existing posts to get them to show up in the taxonomy box. To relate a post to another, you just check the checkbox of the post you want to relate to.

The last thing to do, is to retrieve the related posts in your theme. That is done by a simple taxonomy query in your template files.

if (have_posts()) : while (have_posts()): the_post();
	the_title();
	
	$related_posts = new WP_Query(array(
		'post_type' => 'post',
		'post_status' => 'publish',
		'tax_query' => array(array(
	        'taxonomy' => 'related_posts',
	        'terms' => get_the_ID(),
	        'field' => 'slug'		
		))
	));

	if ($related_posts->have_posts()) : while ($related_posts->have_posts()): $related_posts->the_post();
		// print your related posts
	endwhile; endif;

endwhile; endif; 

When we deployed our new site at Internetavdelningen last week, one of our goals was to get better at measuring conversions. Naturally, one of the main goals of the site is to convince potential clients to get in contact with us. Because our contact form is powered by the (absolutely awesome) Gravity Forms WordPress-plugin, we had to find a way to setup Gravity Forms Analytics Goals.

The easy way – a separate confirmation page

If that suits your page design and structure, it is easiest to just configure your Gravity form with a confirmation page. See the form settings screenshot below. You just have to create a page, which thanks for the submission or something like that and choose this page in the form settings.

Now it’s only left to add the page as goal to your Google Analytics profile. Click on the “Admin” item in the top menu on the right site and than on your profile in the Profiles tab, in your profile, choose the “Goals” tab and add a goal in one of the sets. Name and activate the goal, choose “URL Destination” and copy the permalink of your confirmation site into the “Goal URL” field. Now, after a few hours (or max one day) your conversion statistics should appear under “Conversions” in the “Standard Reporting” section.

How to do it without a confirmation page

As usual the easy way might not be working in your situation. So it was for us, we wanted to use the text confirmation method (see screenshot below) and hadn’t a clue how to implement that with analytics goals.

The solution was the somewhat unintuitive goal-type called “Event”. You select it instead of “URL Destination” in the goal settings.

Fortunately there is a great generator to do almost all the work for you. If you follow all the steps, including the described goal settings, the only task left is to add the generated tracking code to the submit button. That’s quite easily done with the Gform-submit-button-filter, provided by Gravity Forms.

Here is the code snippet that adds the tracking code with an onclick-handler to the submit button of the form with id=1. Add it to your themes functions.php.

EDIT:There seems to be an Gravity Forms upgrade that breaks the original code. That is because there is already an onclick-event on the submit-button in the new version. So we just have to append our tracking code to the onclick-event. Note that onclick is written in all non-capital letters, in the original version of my snippet it was onClick, which confused the HTML-parser. Thanks to @MrPeyler for sending me a note and helping me testing.

add_filter("gform_submit_button_1", "add_conversion_tracking_code", 10, 2);
function add_conversion_tracking_code($button, $form) {
    $dom = new DOMDocument();
    $dom->loadHTML($button);
    $input = $dom->getElementsByTagName('input')->item(0);
    if ($input->hasAttribute('onclick')) {
        $input->setAttribute("onclick","_gaq.push(['_trackEvent', 'Contact', 'Information Request', 'Contact Form',, false]);".$input->getAttribute("onclick"));
    } else {
        $input->setAttribute("onclick","_gaq.push(['_trackEvent', 'Contact', 'Information Request', 'Contact Form',, false]);");
    }
    return $dom->saveHtml();
}

And here the original WordPress filter, which doesn’t work with newer Gravity Forms versions:

add_filter("gform_submit_button_1", "add_conversion_tracking_code", 10, 2);
function add_conversion_tracking_code($button, $form) {
    $dom = new DOMDocument();
    $dom->loadHTML($button);
    $input = $dom->getElementsByTagName('input')->item(0);
    $input->setAttribute("onClick","_gaq.push(['_trackEvent', 'Contact', 'Information Request', 'Contactform',, false]);");
    return $dom->saveHtml();
}

Happy conversion tracking, with your Gravity Forms Analytics Goals!

Building web-apps for some years now, I’ve worked a lot with multiple MVC-frameworks. Most recently with CakePHP, which is great. As WordPress plugin developer I sometimes miss that strictly structured way of coding, a WordPress MVC pattern. While the code of a lot plugins I see, is organised very well generally, I really don’t like the way many developers mixing HTML-output with their plugin functions and classes. Here an examples, from the Hello Dolly plugin.

// This just echoes the chosen line, we'll position it later
function hello_dolly() {
    $chosen = hello_dolly_get_lyric();
    echo "<p id='dolly'>$chosen</p>";
}

Another one from the akismet plugin, included in a standard WordPress installation:

function akismet_stats_display() {
    global $akismet_api_host, $akismet_api_port, $wpcom_api_key;
    $blog = urlencode( get_bloginfo('url') );

    $url = 'http://';
    if ( is_ssl() )
        $url = 'https://';

    $url .= 'akismet.com/web/1.0/user-stats.php';
    $url .= "?blog={$blog}&api_key=" . akismet_get_key();
    ?>
    <div class="wrap">
    <iframe src="<?php echo $url; ?>" width="100%" height="100%" frameborder="0" id="akismet-stats-frame"></iframe>
    </div>
    <?php
}

Even if I don’t think that is a big deal, I see at least a readability issue. That’s why I prefer to store my HTML in separate files, just as you would do in every MVC-framework. It just needs a little view-class and this gets really easy. It has three logical parts: The first one is to locate the file, containing the HTML-view, the second one makes it possible to set variables in the views context and the last one renders the view.

WordPress MVC – The views location

I like to store my views in a folder called views in my plugin directory and I even add the fact that it is a view to the filename. Thus, in my plugins a settings view would be located in plugins/my-plugin/views/settings.view.php. Now, creating an instance of that view in one of my plugin-functions, I don’t want to add the complete path, the relevant part “settings” should do, and the view-class does the rest in the constructor:

public function __construct($view) {
    $path = $plugin_path."/views/".$view.".view.php";
    if (file_exists($path")) {
        $this->view = $path;
    } else {
        wp_die(__("View ".$path." not found"));
    }	
}

WordPress MVC – Setting variables to use in the view

Having the location of the file, we can now add data, that we want to render the view with. Take the Hello Dolly snippet as example, the content of our view file would look like:

<p id="dolly">
    <?php echo $chosen; ?>
</p>

Now we have to set the variable $chosen in the views context. Therefore we use an array called vars containing all data coming in via the set-function of the view-class.

public function set($name,$value) {
    $this->vars[$name] = $value;
}

WordPress MVC – Rendering the view

Finally we need a function to print out the view. It just registers the variables set in the vars-array and uses the output-buffering-functions of php to include and echo the view:

public function render() {
    extract($this->vars,EXTR_SKIP);
    ob_start();
    include $this->view;
    echo ob_get_clean();
}	

WordPress MVC – The view class

Setting all together in a view class (for copy and paste use, see github-link below), enables you to cleanly separate your views from your plugin-code. So, in a plugin, we can instantiate the view, set variables and render it, like this:

include "classes/MyView.class.php";
$view = new My_view("dolly-example");
$view->set("chosen","Lorem ipsum");
$view->render();

Using WordPress as a framework for a web app, often requires some creativity on applying your desired data model to the standard WordPress database setup. This article wants to explore possible ways of creating WordPress Post-to-Post relationships. But, be warned, it is not about best practices, performance or perfect database modelling. Its more an attempt to list some options to consider before adding custom tables and queries and working around all the secure and tested WordPress API functions.

Consider the enhanced entity relationship (ERR) diagram below. It shows some simple relationships of an exemplary author database. There is a many-to-many (n:m) relation of authors and books and in addition a one-to-many relation of publishers and authors.

There are several ways to implement such a data-model without adding any custom tables to the WordPress database. The one thing you have to do in any case is to add appropriate custom post types, extended with suitable meta data.

Implementing custom post types and fields

WordPress gives us custom post types and fields to build our own data-structures into the database. In our example, custom post types correspond to the three main tables (authors, books and publishers). Following our model, we would define three post types, named author, book and publisher, like the one in this, somewhat generic, code example:

register_post_type('author',
	array(
		'labels' => array(
			'name' => 'Author',
		),
		'public' => true,
		'has_archive' => true,
		'supports' => array('title','editor','custom-fields')
	)
);

That would provide us with the extra post types in the admin main menu. Posts of these new types have a title field and an editor field by default and you are able to add whichever meta-data you like, through custom fields.

Relating Posts

That was the easy part, but now it gets more tricky. How do we relate posts of the custom post types, just by using the WordPress API and no custom tables? There are a some of candidates among all the WordPress components that might work.

hierarchical posts
WordPress posts, and so posts of custom post types, are already designed to have an relation, if you want them to. It is a simple parent-child relationship that you might know from WordPress pages. Thus, all of our custom posts can have one parent and, luckily, it mustn’t be the same post type.
meta data
As you read above, we can use post meta data to add any kind of information to a post. It could be a ISBN-number to a book, but it could as well be information about relations between one post and another. As you will see we can realize all significant relation-types with metadata.
custom taxonomies
In contrast to posts, which are holding content, taxonomies are organizing it. You already know the two default taxonomies WordPress provides: categories and tags. Even taxonomies can be used to establish relations, but as I think meta-data is the cleaner way, there will be no example in this article.

Lets have a look at a simplified WordPress data model and see how these component are build. Hierarchical posts are realized by a post_parent column in the wp_posts table, which can be set to the ID of any post. Post-metadata is implemented through the wp_postmeta table, which is able to hold multiple metadata rows for each post.

Now, what we want to do, is to utilize the existing database schema, to get the relations between posts of varied types to work. But, our goal is to do this only by using the API, so we can’t just start to hack SQL-queries. Lets go through the types of relations we want to build one by one and take a look at how it can be done.

Implementing an one-to-many Relation, using the post_parent-column

Now, how to implement an one-to-many relation, like the one between publishers and authors in our example model? The section on hierarchical posts might have given you the idea. An 1:n relation is nothing more than a parent-child relationship. One parent post can have multiple children, but one child post can only have one parent. So, lets say, we are inserting an author-post, the only thing we have to do is to set the post_parent property and we are done.

$publishers_id = 22; // insert the real id of a publisher-post here
$author = array(
   'post_parent'    => $publishers_id,
   'post_status'    => 'publish',
   'post_title'     => 'This is an example author',
   'post_type'      => 'author'
);
wp_insert_post( $author );

Retrieving parent posts is equally simple. Just use a custom WP_Query object for getting all the authors related to a publisher:

   $args = array(
      'post_type' => 'author',
      'post_parent' => $publishers_id
   );
   $authors = new WP_Query($args);

The drawback of this method is that you aren’t able not relate a post to more than one parent, because all the posts are in one table. Thus you can only build a parent-child relation between one post type and another and not one post type and multiple others. Do you have a more complex model than we have, chances are big that you have more than one 1:n relation on some post types. For example, if we wanted to add labor unions to the model. That would mean that you had to set both a publisher and an authors union as parent, which isn’t possible.

Multiple one-to-many Relations, using post-metadata

If you look at the WordPress database scheme above, you can see that one post can be related to multiple wp_postmeta rows. The idea is now, to use this existing relation in the database for our relation of multiple post types – we want, in our concrete case, to relate some authors to both a publisher and a union. Our implementation is basically a variation of the post_parent method, as we will just create additionally post_parent-meta-data for every post type we are relating.

// insert the real post ids here
$publishers_id = 1;
$unions_id = 2;
$author_1_id = 3;
$author_2_id = 4;

// add relations, author 1 has only a publisher, author 2 has both publisher and union
add_post_meta($author_1_id, 'publisher_id', $publishers_id);
add_post_meta($author_2_id, 'publisher_id', $publishers_id);
add_post_meta($author_2_id, 'unions_id', $unions_id);

Even retrieving the posts again is only slightly different from the post_parent-method we used above. Instead of querying the posts by post_parent we have to query them by meta_key.

   // getting a publishers authors
   $args = array(
      'post_type' => 'author',
      'meta_key' => 'publisher_id'
      'meta_value' => $publishers_id
   );
   $authors = new WP_Query($args);

   // getting a unions authors
   $args = array(
      'post_type' => 'author',
      'meta_key' => 'union_id'
      'meta_value' => $unions_id
   );
   $authors = new WP_Query($args);

A note on 1:1 relations: Implementing one to one relations is basically the same as implementing one-to-many relations. The only difference is that you manually have to set a constraint, limiting the application to relate not more than one post to another, so that a post can’t set as parent if it already is the parent of  another post.

Many-to-many relations, using post-metadata

Many-to-many relations can’t be created with the parent_post-method. They are usually created by using a junction table, which holds the keys of two related posts in each row. In the author database model at the beginning of this article, you can see a junction table between authors and books, which physically enables a n:m-relation via two 1:n relations to the junction table. Now, take a look at the simplified WordPress database scheme and the wp_posts/wp_postmeta relation. There is a one-to-many relationship between them, but, since we relating author-posts to book-posts, stored in the same table, there actually are two one-to-many relations and the wp_postmeta table is working precisely as a junction table. The only thing we have to do, is to adjust the meta-data example above, to save multiple related post ids, instead of one. Luckily, the WordPress API enables us to do that.

// insert the real post ids here
$book_1_id = 1;
$book_2_id = 2;
$author_1_id = 3;
$author_2_id = 4;

// add relations, author 1 has only a publisher, author 2 has both publisher and union
add_post_meta($author_1_id, 'authors_books', $book_1_id);
add_post_meta($author_2_id, 'authors_books', $book_1_id);
add_post_meta($author_2_id, 'authors_books', $book_2_id);

That was all. Retrieving the ids of the books of an author is as simple as reading the authors_books-meta-data of an author post. If the author is related to multiple books, you will get an array of ids, which you can use to read the book-posts. Getting all the authors of a book requires a custom WP_Query with meta_key-option, just as the one we used on 1:n relations.

   // getting a books authors
   $args = array(
      'post_type' => 'author',
      'meta_key' => 'authors_books'
      'meta_value' => $book_id
   );
   $authors = new WP_Query($args);

Many-to-many relations with additional relationship information

Sometimes you need to add a piece of information to the relationship itself. How many chapters of the book are written by each of its authors, would be an (uninspired) example. Usually you just add columns to the junction table, but that’s not possible in our case, because we don’t want to change the database setup.

Since the only way to dynamically add custom data are custom post types, we have to create one, just to store the relation-data. Lets call it author_book. Now, when we create a relation between an author-post and a book-post, we even have to create an author_book-post, store some data in it and connecting it to the relation itself. We are doing this, again, with post-metadata.

// post ids, of authors and books
$author = 1;
$book = 2;

// insert relationship post
$author_book = array(
   'post_status'    => 'publish',
   'post_title'     => 'Information about the relation between author and book',
   'post_type'      => 'author_book'
);
author_book_id = wp_insert_post( $author_book );

// relate posts
add_post_meta($author_book_id,'author',$author);
add_post_meta($author_book_id,'book',$book);

The author_book-post is now related to both, the author– and the book-post. Retrieving the data, has to be done in several steps now, which might be ineffective – but, as I said before, this article is not about finding the most performant solution.

// retrieve authors of a book, plus information post
$book_id = 1;

$args = array(
   'post_type' => 'author_book',
   'meta_key' => 'book'
   'meta_value' => $book_id
);
$author_books = get_posts($args);

foreach ($author_books as $author_book) {
   $author = get_post(get_post_meta($author_book->ID));
   // do sth with the author
}

Conclusion

Relating posts of different posts types is one of the key features for using WordPress as an web-application framework. Using post-metadata and some API functions, it is possible to implement 1:n, 1:1, n:m and even data-holding n:m relations. The pros are, that you can base on the reliable, tested and continuously bug-fixed WordPress API. The cons are that you might run into performance problems, not using an optimized database. However, I think the discussed solutions for one-to-many, one-to-one and many-to-many relations are at least considerable and could be used in production. While the last case of simulating a junction-table with content columns was more of a showcase whats possible.

I had to implement some WP-ecommerce themes (download WP-ecommerce), a couple of weeks ago. The admin interface happens to be quite good and simple, but I wanted to build my theme from scratch, controlling the code to a 100%. So all the javascript and css stuff, that automatically gets included, was just a lot of overhead for me.

It took a while to find all the scripts and styles, so I thought sharing here could help someone with the same problem, just add the following lines to your functions.php.

add_action('wp_print_styles','deregister_styles',100);
function deregister_styles() {
	if (!is_admin()) {
		wp_deregister_style('wpsc-thickbox');
		wp_deregister_style('wpsc-theme-css');
		wp_deregister_style('wpsc-theme-css-compatibility');
		wp_deregister_style('wp-e-commerce-dynamic');
	}
}

add_action('wp_print_scripts','deregister_script',100);
function deregister_script() {
	if (!is_admin()) {
		wp_deregister_script('jQuery');
		wp_deregister_script('wp-e-commerce');
		wp_deregister_script('infieldlabel');
		wp_deregister_script('wp-e-commerce-ajax-legacy');
		wp_deregister_script('wp-e-commerce-legacy');
		wp_deregister_script('wp-e-commerce-dynamic');
		wp_deregister_script('livequery');
		wp_deregister_script('wpsc-thickbox');
	}
}

All done. Now we just have to load our own styles and can begin to work on our cleaned up WP-ecommerce themes.