A cleaner WP User object

Confession - I'm actually quite new to WordPress, coming from the more framework rather foundation world of PHP. (For what I mean by that, read Tom McFarlin's excellent post ) One danger we all face whenever we switch from one language or tool to another is trying to apply our old methods to the new environment. I've been making a point of trying to shed my earlier experiences and simply learn the WordPress way of doing things. However, I've noticed a few little quirks and inconveniences and started to think about how to apply my knowledge from other places to how I work with WP plugins.

One thing I've noticed is the seemingly convenient, and yet actually somewhat awkward way of accessing user data. WP works with two underlying tables for users and posts - a normalized base table ("wp_users" or "wp_posts") and a key/value pair table ("wp_usermeta" and "wp_postmeta"). Key/value pairs are wonderfully flexible tools when you don't know ahead of time what all of your fields are going to be, or when not every record is going to need all the possible fields and you don't want to save a lot of mostly empty rows. The problem comes when you need to access all the available data for a certain user or post - you end up needing to make multiple calls. One thing I've seen in code samples is this:

$user = get_user_by( 'id', $user_id );

$email = $user->user_email;

$first_name = get_user_meta( $user_id, 'first_name', true );
$last_name = get_user_meta( $user_id, 'last_name', true );

//etc ...

Quite ugly and cumbersome, isn't it? What's worse, each of those functions is calling the database - so if you need ten bits of info about your user, you are making ten database hits. Not high performance, to say the least!

Fortunately, WordPress has already offered us a trick:

$user = get_user_by( 'id', $user_id );

$email = $user->user_email;

$user_meta = get_user_meta( $user_id );

/*
	this leaves us with the equally awkward

	Array ( [first_name] => Array ( [0] => Tom ) [last_name] => Array ( [0] => Auger) [nickname] => Array ( [0] => tomauger ) [description] => etc.... )

	and so we dereference it with:

	$user_data = array_map( function( $a ){ return $a[0]; }, get_user_meta( $user_id ) );
*/

This still leaves us with two separate arrays ($user & $user_meta) and a lot of messy code whan all we want is: "Give me the darn user data, already! "

Let's look at this whole problem a little differently. Instead of working with arrays that are basically mapped directly to tables, let's create a User class and load up all the data for each object as we need it. In your wp-content directory (whatever you may have named it as), add a directory "mu-plugins" at the same level as "plugins" and "themes"

For those of you unfamiliar with it, "mu-plugins" is "Must Use Plugins" and allows us to load code very early. Any php file listed here will automatically be loaded BEFORE the plugins and themes directories, meaning we can add classes here and they will be accessible to the rest of the system.

In our /mu-plugins directory, add the following file:


// file: user.php

class User{

	public $user;

	public $user_meta;

	public $user_data;

	protected $default = null;

	function __construct($user_id)
	{
		$this->user = (array) get_user_by( 'id', $user_id )->data;

		$this->user_meta = array_map( function( $a ){ return $a[0]; }, get_user_meta( $user_id ) );

		$this->user_data = array_merge($this->user, $this->user_meta);
	}

	function __get($var)
	{
		if (!array_key_exists($var, $this->user_data)){
			return $this->default;
		}
	    
	    return $this->user_data[$var];		
	}

}	

Okay, let's talk about that! This object is actually very simple php code, using two "Magic Methods" - __construct() & __get(). Our __construct() is where we set up all of our data; you'll notice I'm simply calling the same functions as I did before, in a slightly different manner, so I can populate two arrays that are then merged. The __get() allows us then to ask for a certain variable directly, or else returns our default value if not found on this object (more on that in a moment). So what this means in our code is:

// file: /mu-plugins/dummy-plugin.php

function my_content_filter( $content ) {

	$user = new User(2);

	var_dump($user->user_email);

	var_dump($user->first_name);

	var_dump($user->sill_var);
}

add_filter( 'the_content', 'my_content_filter' );	//view this on any page

That's a lot easier to work with, don't you think? This User class is accessible in out mu-plugins, plugins or themes directories. It has these other advantages:

1) We still use the native WP functions, so the filtering and caching is taken care of for us

2) __get() searches for our variable and handles cases where that field doesn't exist for a particular user (ex. $user->silly_var). We can set a default for these cases so that when we are working in our plugin we don't have to write that old "if (isset(...))" code everywhere.

3) It's an object, so we can do anything else we want to that's related to Users and keep the code all together. For example:


// after __get() add:
function dummy_function()
{
	echo 'This is a dummy function';
}

// in plugin.php, call:
$user->dummy_function();

So we could use __set() or another function to handle updating or other actions in a custom manner if we like.

The last thing to point out is - if you look closely, you'll realize that if you replace "user" with "post" in the above class, the entire thing would work as is for your posts, including all your custom post types (with one small change to the "get_user_by", which doesn't have a matching pair in posts). This gives you the opportunity to create an interface or apply other OOP patterns and practices.

I hope that turns out to be helpful to you, and perhaps introduces you to a new way of thinking about managing your WP data!

Contact me