Create a Simple CRM in WordPress: Adding Columns to WP_List_Table

We’ve been looking at how to create a simple CRM system in WordPress. In the last part of this series, we used Advanced Custom Fields to add custom fields to our Contacts custom post type. We learned how to use Advanced Custom Fields and configure it to work programmatically.

Today, we’ll cover how to display the data stored in our custom fields right within our contacts table.

All About WP_List_Table

Each post type that has a dashboard uses the WP_List_Table class to render the familiar table and its associated actions:

By default, WordPress will display the following columns:

  • checkbox (used to select/deselect posts, which can then have actions applied to them)
  • title
  • author
  • comments
  • date

For our Contact custom post type, this isn’t very useful if we want to quickly see key details at a glance, such as the contact’s phone number or photo.

WordPress provides a filter and an action that can be used to add new columns to the WP_List_Table and determine its output for each post.

Let’s start by adding the manage_edit-{POST_TYPE}_columns filter to our plugin class’ construct. {POST_TYPE} will be the name of our Post Type, which in this case is contact:

/**
* Constructor. Called when plugin is initialised
*/
function __construct() {

	add_action( 'init', array( $this, 'register_custom_post_type' ) );
	add_action( 'plugins_loaded', array( $this, 'acf_fields' ) );
	add_filter( 'manage_edit-contact_columns', array( $this, 'add_table_columns' ) );
    
}

We also need to define our add_table_columns() function, which tells WordPress the names of our additional columns that we’d like to display on our Contacts table. This function accepts an array of existing columns, which we can extend by adding our custom table columns.

/**
* Adds table columns to the Contacts WP_List_Table
*
* @param array $columns Existing Columns
* @return array New Columns
*/
function add_table_columns( $columns ) {

	$columns['email_address'] = __( 'Email Address', 'tuts-crm' );
	$columns['phone_number'] = __( 'Phone Number', 'tuts-crm' );
	$columns['photo'] = __( 'Photo', 'tuts-crm' );
    
	return $columns;
    
}

We make sure that the array keys match ACF’s custom field names. If you have different fields, be sure that your column key names match your fields’ field name setting.

View your Contacts table by clicking on Contacts in the WordPress dashboard menu, and you’ll see our new columns:

However, there’s no data being displayed for each Contact in the table. We need to add the manage_{POST_TYPE}_posts_custom_column action to our plugin class’ constructor. {POST_TYPE} will again be the name of our post Type, which in this case is contact:

/**
* Constructor. Called when plugin is initialised
*/
function __construct() {

	add_action( 'init', array( $this, 'register_custom_post_type' ) );
	add_action( 'plugins_loaded', array( $this, 'acf_fields' ) );
	add_filter( 'manage_edit-contact_columns', array( $this, 'add_table_columns' ) );
	add_action( 'manage_contact_posts_custom_column', array( $this, 'output_table_columns_data'), 10, 2 );
    
}

We also need to define our output_table_columns_data() function, which tells WordPress what to display for each Contact and column combination. Because we made sure our column key names match our ACF Field Names, this makes our coding easier. Advanced Custom Fields has a get_field() function, which accepts both Field Name and Post ID parameters to retrieve the value stored:

/**
* Outputs our Contact custom field data, based on the column requested
*
* @param string $columnName Column Key Name
* @param int $post_id Post ID
*/
function output_table_columns_data( $columnName, $post_id ) {
	echo get_field( $columnName, $post_id );	
}

Reload your Contacts table, and you’ll see your custom fields:

Depending on your PHP configuration, the Photo column will either be blank, or output a PHP notice:

The image field in Advanced Custom Fields returns an array when using get_field(), comprising of the image details and each registered image size’s URL, width and height (a registered image size is typically based on your installed Themes and Plugins).

Let’s modify our function to output the right array data to display an image:

/**
* Outputs our Contact custom field data, based on the column requested
*
* @param string $columnName Column Key Name
* @param int $post_id Post ID
*/
function output_table_columns_data( $columnName, $post_id ) {

	// Field
	$field = get_field( $columnName, $post_id );
	
	if ( 'photo' == $columnName ) {
		echo '<img src="' . $field['sizes']['thumbnail'].'" width="'.$field['sizes']['thumbnail-width'] . '" height="' . $field['sizes']['thumbnail-height'] . '" />';
	} else {
		// Output field
		echo $field;
	}
    
}

Reload your Contacts Table, and you should see each Contact’s photo:

Sortable Columns

What if we need to quickly sort our Contacts by name, phone number or email address? We can already sort on the name (or, rather, the title) column, but right now there’s no functionality to tell WordPress how to sort on our phone number and email address columns.

Back to our plugin’s constructor. We need to add the manage_{POST_TYPE}_posts_custom_column filter to our plugin class’ construct, to tell WordPress that we want to enable certain columns to be sortable. {POST_TYPE} will again be the name of our Post Type, which in this case is contact:

/**
* Constructor. Called when plugin is initialised
*/
function __construct() {

	add_action( 'init', array( $this, 'register_custom_post_type' ) );
	add_action( 'plugins_loaded', array( $this, 'acf_fields' ) );
	add_filter( 'manage_edit-contact_columns', array( $this, 'add_table_columns' ) );
	add_action( 'manage_contact_posts_custom_column', array( $this, 'output_table_columns_data'), 10, 2 );
	add_filter( 'manage_edit-contact_sortable_columns', array( $this, 'define_sortable_table_columns') );
    
}

As with our previous actions and filters, we also need to define our define_sortable_table_columns() function, which tells WordPress which columns can be sortable:

/**
* Defines which Contact columsn are sortable
*
* @param array $columns Existing sortable columns
* @return array New sortable columns
*/
function define_sortable_table_columns( $columns ) {

	$columns['email_address'] = 'email_address';
	$columns['phone_number'] = 'phone_number';
    
	return $columns;
    
}

Hover the mouse cursor over the Email Address and Phone Number columns, and you’ll see an arrow appear, showing us that we can sort by the data in the relevant column:

Right now, clicking on the column header to sort by its data won’t do anything, as the orderby parameter that’s set in the URL isn’t one that WordPress will recognize.

In our plugin’s constructor, let’s add a filter on the request method, and then define our function to check if we’re trying to sort by a custom column (and if so, amend the posts query so WordPress can understand it):

/**
* Constructor. Called when plugin is initialised
*/
function __construct() {

	add_action( 'init', array( $this, 'register_custom_post_type') );
	add_action( 'plugins_loaded', array( $this, 'acf_fields') );
	add_filter( 'manage_edit-contact_columns', array( $this, 'add_table_columns') );
	add_action( 'manage_contact_posts_custom_column', array( $this, 'output_table_columns_data'), 10, 2 );
	add_filter( 'manage_edit-contact_sortable_columns', array( $this, 'define_sortable_table_columns') );
    
	if ( is_admin() ) {
		add_filter( 'request', array( $this, 'orderby_sortable_table_columns' ) );
	}
    
}

Because the request filter runs on every page load of WordPress (whether the frontend web site or WordPress dashboard interface), we want to minimize how often it’s called. We do this by only adding the filter if we are in the WordPress Administration (is_admin()).

Next, let’s define our orderby_sortable_table_columns() function:

/**
* Inspect the request to see if we are on the Contacts WP_List_Table and attempting to
* sort by email address or phone number.  If so, amend the Posts query to sort by
* that custom meta key
*
* @param array $vars Request Variables
* @return array New Request Variables
*/
function orderby_sortable_table_columns( $vars ) {

	// Don't do anything if we are not on the Contact Custom Post Type
	if ( 'contact' != $vars['post_type'] ) return $vars;
	
	// Don't do anything if no orderby parameter is set
	if ( ! isset( $vars['orderby'] ) ) return $vars;
	
	// Check if the orderby parameter matches one of our sortable columns
	if ( $vars['orderby'] == 'email_address' OR
		$vars['orderby'] == 'phone_number' ) {
		// Add orderby meta_value and meta_key parameters to the query
		$vars = array_merge( $vars, array(
        	'meta_key' => $vars['orderby'],
			'orderby' => 'meta_value',
		));
	}
	
	return $vars;
    
}

This function checks that we are viewing our Contacts Custom Post Type, and if so that an orderby parameter has been set matching either email_address or phone_number. These are set if the user has clicked a column in the Contacts Table to order by Phone Number or Email Address.

Let’s click on the Email Address column, and we’ll see the results are ordered correctly:

Clicking it again will reverse the order of results:

Up Next…

In the next article, we’re going to extend the filter and search functionality, allowing us to search the data stored within our Advanced Custom Fields.

Source:: Net Tuts

Leave a Reply

Your email address will not be published. Required fields are marked *