Skip to content

From Magic Fields 1.x to ACF, Part 4

Other posts in this series: Part 1; Part 2; Part 3; Part 4 (this post); Part 5; Part 6; Part 7; Part 8; Part 9

Working Out The Kinks

I made an error in setting up the custom post types. They should all have an archive enabled, since I do in fact put these into the nav menus. Don’t forget nav_menu_item to allow menus to work!

The next problem is getting the menus to work. I’ve got a hierarchical category structure for musical works (using sub-categories for eras); and performers (using subcategories for instrument/voice, and also for current season guests). Out of the box, even with the “categories” taxonomy set, the menus just don’t work.

I was able to get the musical works portion working fairly quickly using this bit of code in my functions.php file:

add_filter('pre_get_posts', 'query_post_type');
function query_post_type($query) {
if( is_category() ) {
$post_type = get_query_var('post_type');
$post_type = $post_type;
$post_type = array('nav_menu_item', 'post', 'musical_work');
return $query;

Replace “musical_work” with the slug for your custom post type, and that hierarchical menu works. However.

add_filter('pre_get_posts', 'query_other_post_type');
function query_other_post_type($query) {
if( is_category() ) {
$post_type = get_query_var('post_type');
$post_type = $post_type;
$post_type = array('nav_menu_item', 'post', 'performer');
return $query;

Does not. You can find that chunk of code in a lot of places, and it works nicely when you’ve got one custom post type taxonomy. But I’ve got several, and you can’t do it the way above, and you can’t do it by re-declaring ‘query_other_post_type’ either.

I tried this block of code in a template:

<? global $wp_query;
var_dump($wp_query->query_vars); ?>

And found a query variable that might help. ‘category_name’ contains not the parent category, which would be nice, but it does contain the child categories. And I’ve got a finite list of those categories (58), and they’re not likely to expand. There are musical eras for the “works:” Renaissance, Baroque, etc. Maybe in 80 years we’ll add “22nd Century.” And for performers there’s a finite list of instruments. The last time we had anything new was when a guy came in and played the Theremin at a Halloween concert. So that list is fixed as well. So I added a couple of arrays to my functions.php file:

function get_template_variable($category_name) {
$work_types = array(
$performer_type = array(
... );

And after you lay those out there are a few one-offs:

if ( in_array ($category_name, $work_types)) return 'musical_work';
elseif ( in_array ($category_name, $performer_type)) return 'performer';
elseif ( 'performances' == $category_name ) return 'program';
elseif ( 'seasons' == $category_name ) return 'season';

The return value corresponds to the custom post types I set up in Custom Post Type UI. Then I altered the function above to do this:

add_filter('pre_get_posts', 'query_other_post_type');
function query_other_post_type($query) {

Clear the $template_variable.

$template_variable = '';
if( is_category() ) {
$post_type = get_query_var('post_type');
$post_type = $post_type;

This is where it’s different. Call the get_template_variable function feeding it the get_query_var of “category_name.” That will return one of the above categories (clarinetist, baroque, etc.).

$template_variable = get_template_variable(get_query_var('category_name'));

And then instead of feeding the $post_type array a fixed string like ‘performer,’ you give it the return value of the function. Then set the post_type and return the query.

$post_type = array('nav_menu_item', 'post', $template_variable);
return $query;

And with that, my menus started to work again, and the linked categories on individual posts went to the correct category archive page.


My first widget was a primitive one, it was slow but it did the job. I wrote a plugin to drop into the side bar and show related information depending on the type of post.

For a musical work:

  • Title
  • Subtitle
  • Composer
  • Date of composition
  • Arranger, if any
  • Copyright date, if any
  • When we’d performed the work (with a link to that program post)

That last one was the tricky one. The first fields are all custom fields for the post; but that last requires backtracing any performance posts that referenced that particular work.

For a performer:

  • Linked name
  • Type (instrument, voice, etc.)
  • When he/she has performed with us.

The last one again poses the backtracing problem.

For a performance or a season:

  • A list of soloists, with their names linked

Magic Fields could do this, but it was messy. Advanced Custom Fields does it pretty nicely, and in a more native WP kind of way. Start with a folder in your wp-content/plugins folder, and add a .php file. The PHP file will have the typical lines at the top, with the plugin name, description, version, author.

Then declare your classname, which extends WP_Widget. Add a constructor function, and then the

public function widget( $args, $instance ) {

Without laying out all of the heavy coding — which was really more typing than anything else — I’ll walk through some pseudo-code. This method for backtracing is laid out on the ACF docs pages, it’s easy to follow along and customize.

First, get the post ID of the current post.

Save it to a variable ($thePostID). Then determine the post_type.

$post_type = get_query_var('post_type');

Branch out from there.

If it’s a “season”, Set an appropriate title for the widget. Then I get my array of guest artists:

$guest_artists = get_field('guest_artists', $thePostID);
foreach ($guest_artists as $guest_artist) {

Using $guest_artist as a post ID, I can use ACF’s get_field() function to pull whatever I need about the guest artist. Like

get_field('photo', $guest_artist);

I can grab names, photos, permalinks, etc. and wrap them in HTML code. Essentially the same thing for programs.

Performers and musical works require the backtracing. After I’ve got my list of fields and am displaying them the way I want, I use the querying relationship fields code like this:

$performer_name = get_field('name', $thePostID);
$bbeccw_title = $performer_name;

$bbeccw_title is what I’ll feed in to the widget title. Next is the heavy lifting part, where I find an array of posts where the post type is ‘program’, and the ‘artist_artist’ key has a value of $thePostID.

//program dates?
$program_posts = get_posts(array(
'post_type' => 'program',
'meta_query' => array(
'key' => 'artist_artists',
'value' => '"' . $thePostID . '"',
'compare' => 'LIKE'

Then rattle through the list pushing HTML code into the $bbeccw variable, which will become the widget body.

if ( $program_posts ) {
$bbeccw_content = '';
$bbeccw_content .= <h5>Recent and Future Appearances with the BBE</h5>';
$bbeccw_content .= "<p>";
foreach ( $program_posts as $program_post ) {
if ( get_field('basic_info_date', $program_post) ) {
$bbeccw_content .='<a href="' . get_permalink($program_post) . '">';
$concert_date = get_field('basic_info_date', $program_post);
$bbeccw_content .= date('l, F j, Y', strtotime($concert_date)) . "</a><br />\n";
$bbeccw_content .= "</p>\n";

So each possible post type will generate a $bbeccw_title, and a $bbeccw_content block. And I execute them like this:

echo $args['before_widget'];
echo $args['before_title'] . apply_filters( 'widget_title', $bbeccw_title ) . $args['after_title'];
echo $bbeccw_content;
echo $args['after_widget'];

Then at the end, register your widget.

add_action( 'widgets_init', function(){
register_widget( 'BBECCW_Widget' );

The way I went about this was to just use the empty widget code I found here, activate the plugin, and add the new widget to a sidebar it to make sure it works. Then just start adding your code.

One Comment

  1. Tom Tom

    After running through all of this and cleaning up a few typographical errors in my code, I just swapped out the old site for the new clone. Depending on your hosting and level of access/expertise that would involve dragging files from one directory to another, or just changing a setting in your .conf file. Remember to change the entries in your wp-config.php, and in the wp_options table of your database.

Leave a Reply

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

Share This