Version 1.3

Improved API

In this release, I focused on making the API more uniform; everything works for both posts-to-posts and posts-to-users connections now. This is possible because you can pass whole post and user objects, instead of just IDs.

Previously:

$connected = get_users( array(
	'connected_type' => 'posts_to_users',
	'connected_items' => $post->ID
) );

Now:

$connected = get_users( array(
	'connected_type' => 'posts_to_users',
	'connected_items' => $post
) );

This small change gives P2P more information to work with, which means fewer database queries are needed.

Shortcodes!

The plugin now comes with a pair of shortcodes for conveniently listing connected items anywhere.

Say you defined a connection type called ‘posts_to_pages’. Here’s how the shortcode would look like:

[p2p_connected type=posts_to_pages]

If you insert that into a post, it will display a list of connected pages. If you insert it into a page, it will display a list of connected posts.

And there’s one for related posts too:

[p2p_related type=posts_to_pages]

Shortcodes and widgets also have filters for modifying the HTML output.

Default field values

You can now set default values for the connection fields in the metabox, likes so:

p2p_register_connection_type( array(
	'name' => 'posts_to_pages',
	'from' => 'post',
	'to' => 'page',
 
	'fields' => array(
		'color' => array(
			'title' => 'Color',
			'type' => 'checkbox',
			'values' => array( 'green', 'yellow', 'blue', 'white' ),
			'default' => 'blue'
		),
	)
) );

In total, there were 16 resolved issues in this release.

Version 1.2

The main attraction in this release is the new admin screen that gives an overview of all the connection types found in the system:

Connection Types screen

Another thing to note is that queries like this no longer work:

$query = new WP_Query( array(
  'connected_items' => get_queried_object_id()
) );

You have to set the ‘connected_type’ parameter:

$query = new WP_Query( array(
  'connected_type' => 'posts_to_pages',
  'connected_items' => get_queried_object_id()
) );

Also, you can pass an array of connection types and P2P will pick the ones that make sense.

$query = new WP_Query( array(
  'connected_type' => array( 'posts_to_pages', 'actors_to_movies' ),
  'connected_items' => get_queried_object_id()
) );

As with every release, it contains a bunch of smaller enhancements and bug fixes. See the changelog for more details.

Version 1.1

I’m pleased to announce that the Posts 2 Posts plugin now also supports posts-to-users connections. (It supports users-to-users connections as well, but there’s no UI for them).

This is made possible by a new p2p_type column on the wp_p2p table, with which we can clearly distinguish between user ids and post ids. If you’re upgrading from a previous version of Posts 2 Posts, all the existing connections will be automatically updated to have the correct value for this column.

For the next version, I’m thinking of supporting connections between posts on different blogs in a multisite network.

Version 1.0

It’s been a year and a half since the initial 0.1 release. Posts 2 Posts has come a long way since then and it’s been a very interesting journey.

Here’s what’s new in version 1.0:

Indeterminate connection types

Initially, connection types were organised exclusively around post types. Since version 0.9.5, you can use arbitrary query variables to define the sides of a connection type.

And with this release, there’s a clear distinction between regular connection types and indeterminate connection types, which are a lot easier to control now.

Improved support for ordered connections

You can now order connections both ways. Also, you can get the previous and next post in the list. See Connection ordering.

Related posts

If you have actors and movies, it’s a lot easier now to get other actors that have played in the same movies as a particular actor. See the wiki page about related posts.

Additionally, you can show related posts using the widget.

More connection field types

In previous versions, a connection field could be either a text input or a dropdown. Now, you can also add radio buttons, checkboxes and textareas. See Connection information.

You can see all the tickets closed for this release on github:

https://github.com/scribu/wp-posts-to-posts/issues?milestone=2&state=closed

Version 0.9.5

In short, this version improves the connection type based API introduced in 0.9 and removes the old API.

Connection Type IDs

Previously, you had to keep track of connection type instances yourself, making it hard to work with more than one connection type.

Old:

global $my_connection_type;
 
$my_connection_type = p2p_register_connection_type( array(
	'from' => 'post',
	'to' => 'page'
) );

New:

p2p_register_connection_type( array(
	'id' => 'posts_to_pages',
	'from' => 'post',
	'to' => 'page'
) );

And, to get a list of connected items, instead of:

global $my_connection_type;
 
$connected = $my_connection_type->get_connected( get_queried_object_id() );

you can now do it in one line:

$connected = p2p_type( 'posts_to_pages' )->get_connected( get_queried_object_id() );

One-To-Many Connections

There’s a new ‘cardinality’ argument:

p2p_register_connection_type( array(
  'from' => 'post',
  'to' => 'page',
  'cardinality' => 'one-to-many',
  'reciprocal' => true
) );

When editing a page, you will only be able to connect it to a single post and only to a post that doesn’t have a connected page yet.

'cardinality' => 'many-to-one' has the same effect, in reverse.

Arbitrary query variables

You can now distinguish between connection types based on any query variable, not just ‘post_type’:

$types = array(
    'bug' => 'Bug',
    'feature' => 'Feature'
);
 
foreach ( $types as $type => $title ) {
    p2p_register_connection_type( array(
        'from' => 'contact',
        'to' => 'ticket',
        'to_query_vars' => array(
            'meta_key' => 'type',
            'meta_value' => $type
        ),
        'title' => array( 'from' => $title ),
    );
}

The above code will create two meta boxes: in one, contacts will only be able to create connections to ‘bug’-type tickets and in the other only to ‘feature’-type tickets.

There’s a matching ‘from_query_vars’ argument as well, so you could, for example, create a connection type between posts from category X to posts with tag Y etc.

Removed old API

I went ahead and removed the old API. Here’s the migration path:

Old New
p2p_connect() p2p_type( ‘my_connection_type’ )->connect()
p2p_disconnect() p2p_type( ‘my_connection_type’ )->disconnect()
p2p_get_connected() p2p_type( ‘my_connection_type’ )->get_connected()

The benefit, of course, is that each of the new methods enforces the rules establised when registering the connection type.

Finally, I set up a code reference site, just to play around with DocBlox. Let me know if you find it useful.

Version 0.9

Admin Box Enhancements

admin box

In the above screenshot you can see the following new features:

Multiple Connections Per Post Type

Besides the ‘fields’ parameter, there’s also a ‘data’ parameter. This can be used to distinguish between connection types:

p2p_register_connection_type( array(
	'from' => 'actor',
	'to' => 'actor',
	'reciprocal' => true,
	'title' => array( 'from' => 'Doubles', 'to' => 'Main Actor' ),
	'data' => array( 'type' => 'doubles' ),
) );
 
p2p_register_connection_type( array(
	'from' => 'actor',
	'to' => 'actor',
	'reciprocal' => true,
	'title' => 'Friends with',
	'data' => array( 'type' => 'friends' )
) );

API Overhaul

Should I use ‘connected_to’ or ‘connected_from’? Erm… I’ll just use ‘connected’.

Even I was tired of having to think about it. No more.

In previous versions, connection types were tightly coupled with the admin box code. I extracted all the logic into a P2P_Connection_Type class, which should make querying for posts a lot easier.

The old way of doing things still works, except in the following cases:

  • p2p_register_connection_type() returns a P2P_Connection_Type instance now. Therefore, 'from' => array( ... ) and 'to' => array( ... ) are not accepted anymore.
  • p2p_each_connected() functions and 'each_connected' query vars are gone. Use P2P_Connection_Type->each_connected() instead:

Old:

$my_query = new WP_Query( array(
    'ignore_sticky_posts' => true,
    'post_type' => 'artist',
    'each_connected' => array(
        'post_type' => 'art',
    )
) );

New:

$my_query = new WP_Query( array(
    'ignore_sticky_posts' => true,
    'post_type' => 'artist',
) );
 
$my_connection_type->each_connected( $my_query );

All the tutorials on the wiki have been updated to use the hot new API, so feel free to dig in. :)

Version 0.8

UI Changes

With the WP 3.2 admin refresh, the connection box UI also received an overhaul:

A significant enhancement in this version is the ability to create draft posts from the connection box. The idea is to be able to make the connections first and then write the content. Hat tip to Oren Kolker.

Additionally, when a post is not published, you will see it’s status. Props Michael Fields for the suggestion.

API Changes

p2p_each_connected() has been revamped to, hopefully, make it easier to use. There’s a detailed tutorial on the wiki:

http://github.com/scribu/wp-posts-to-posts/wiki/Looping-The-Loop

Also, there’s a new function called p2p_list_posts() which takes a WP_Query object or an array of posts as the first argument and outputs a simple unordered list of links to those posts.

Finally, there are 3 new query vars that can be used to get connected posts ordered by a particular connection field. More details on the wiki:

http://github.com/scribu/wp-posts-to-posts/wiki/Connection-ordering

Version 0.7

As promised, this version comes with a much sexier metabox for managing connections:

Now you can:

  • Go to the connected post editing screen in one click.
  • See recent posts using the Recent button.
  • OMG! pagination.

It comes with an enhanced API to boot:

Connection information

Storing arbitrary data related to a particular connection has been possible since version 0.4, but being able to edit that data through the UI was far from easy.

So, this release brings a simple way to do that, with a new argument called ‘fields’:

p2p_register_connection_type( array(
	'from' => 'actor',
	'to' => 'movie',
 
	'fields' => array(
		'role' => 'Role',
		'role_type' => 'Role Type'
	),
	'context' => 'advanced'
) );

Here we just defined a connection type between actors and movies, with each connection having two additional fields: ‘role’ – the role of the actor in that particular movie and ‘role type’.

Duplicates

If you want to enable more than one connection between the same two posts, set 'prevent_duplicates' => false when registering the connection type:

p2p_register_connection_type( array(
	'from' => 'actor',
	'to' => 'movie',
	'fields' => array(
		'role' => 'Role',
		'role_type' => 'Role Type'
	),
	'context' => 'advanced',
 
	'prevent_duplicates' => false
) );

This will allow you to have actors with two or more different roles in the same movie, for example.

Distinct titles

Finally, for reciprocal connections, you can set different titles for the metabox, depending on which screen you’re on:

p2p_register_connection_type( array(
	'from' => 'actor',
	'to' => 'movie',
	'fields' => array(
		'role' => 'Role',
		'role_type' => 'Role Type'
	),
	'context' => 'advanced',
	'prevent_duplicates' => false,
	'reciprocal' => true,
 
	'title' => array(
		'from' => 'Played In',
		'to' => 'Cast'
	)
) );

With all those settings in place, you get something like this:

Acknowledgements

Special thanks to ciobi for helping with the UI awesomeness.

Thanks to Alexey Egorov for the icons.

Version 0.6

Nested Queries

If, for each post in a loop, you find yourself doing a subquery using the ‘connected’ query vars, I’ve got good news for you.

Replace something like this:

while ( have_posts() ) : the_post();
	$connected_writers = get_posts( array(
		'post_type' => 'writer',
		'nopaging' => true,
		'connected_to' => $post->ID,
		'suppress_filters' => false
	) );
 
	foreach ( $connected_writers as $writer ) {
		echo $writer->post_title;
	}
endwhile;

with this:

p2p_each_connected( 'to', 'writers', array(
	'post_type' => 'writer',
	'nopaging' => true,
) );
 
while ( have_posts() ) : the_post();
	foreach ( $post->connected_writers as $writer ) {
		echo $writer->post_title;
	}
endwhile;

Notice how each $post now has a connected_writers propery, which is an array containing the connected posts.

The main advantage of this method is that it reduces the number of SQL queries dramatically.

Here’s the original forum thread that spurred this feature.

Several bug fixes also went into this release:

  • fixed p2p_is_connected() returning incorrect results
  • made p2p_get_connected() return p2p_ids even with $direction = 'any'
  • made compatible with Proper Network Activation

In the next version, I will be focusing on improving the default admin UI.

Version 0.5

This release is focused on enhancing the API, making it easier to leverage the p2pmeta table.

First of all, a new variable for WP_Query is now available: connected_meta. It allows you to restrict connections based on what meta data they have. Here’s an example:

$my_query = new WP_Query( array(
    'post_type' => 'book',
    'connected_to' => 'any',
    'connected_meta' => array(
        'connection_date' => 'long ago',
    )
) );

This will retrieve any book that has a connection to any other post. Also, the connections have to have a custom field with the key connection_date and the value long ago.

And, if that’s not enough, you can use the advanced meta query syntax:

$my_query = new WP_Query( array(
    'post_type' => 'book',
    'connected_to' => 'any'
    'connected_meta' => array(
        array(
            'key' => 'connection_date',
            'value' => array( 'long ago', 'yesterday' ),
            'compare' => 'IN'
        )
    )
) );

Also, after you do the query, you can access the rest of the connection meta data via the p2p_id property, assigned to each found post:

while ( $my_query->have_posts() ) : $my_query->the_post();
 
    $connection_type = p2p_get_meta( $post->p2p_id, 'connection_type', true );
 
endwhile;