Populating a page with content from post custom fields

I’m doing up a blog at the moment where each post is accompanied by the name of a song. Adding the songs to the posts is fairly easily done via custom fields.

However, I also need to have a page which lists all the songs in one place. Can this be done without using a plugin? If so, how? (And if not, then what’s the best plugin to use to do it?)

Thanks.

1 Answer
1

I suggest using shortcode, as this’ll easily allow you embed the song list anywhere in your content, in any page or post.

UPDATE: I got a little carried away, and ended up with this!

function song_list_shortcode( $attrs )
{
    $r = ( object )wp_parse_args( $attrs, array(
        'format' => '%post_title - %link',
        'link'   => '%song_key_name',
        'key'    => 'song_key_name'
    ) );

    $query = new WP_Query( array( 'meta_query' => array( array( 'key' => $r->key ) ), 'nopaging' => true, 'update_post_term_cache' => false ) );
    if ( !$query->have_posts() )
        return '';

    $meta_keys = array();
    foreach ( array( 'format', 'link' ) as $type ) {
        // find meta keys
        if ( !preg_match_all( '#%([a-z0-9_-]+)#', $r->$type, $_keys ) )
            continue;

        $_keys = array_flip( $_keys[1] );
        unset( $_keys['post_title'], $_keys['link'] ); // don't want these, not meta keys

        $meta_keys = $meta_keys + $_keys; // add new keys on to meta key stack
    }

    if ( !empty( $meta_keys ) )
        $meta_keys = array_keys( $meta_keys );

    $output="<ul class="songs">";
    while ( $query->have_posts() ) {
        $query->the_post();

        $format = $r->format;
        $link = $r->link;

        if ( !empty( $meta_keys ) ) {
            // grab all meta data in one swoop (should be cached from query)
            $meta_data = get_post_custom( $query->post->ID );

            // swap out all meta key names with their actual value!
            foreach ( $meta_keys as $key ) {

                // using get_post_custom(), all meta data values are arrays
                if ( isset( $meta_data[ $key ][0] ) )
                    $value = esc_html( $meta_data[ $key ][0] );
                else
                    $value=""; // meta key not found, so replace with blank

                list( $format, $link ) = str_replace( "%$key", $value, array( $format, $link ) );
            }
        }

        // swap out %post_title with actual post title
        list( $format, $link ) = str_replace( '%post_title', get_the_title(), array( $format, $link ) );

        // swap out %link in $format with actual $link
        $output .= '<li>' . str_replace( '%link', '<a href="' . get_permalink() . '">' . $link . '</a>', $format ) . '</li>';
    }

    wp_reset_postdata();

    $output .= '</ul>';
    return $output;
}
add_shortcode( 'song-list', 'song_list_shortcode' );

You can use it like so;

[song-list format="%post_title - %link - %song_date_meta_key"]
// The post title - <a href="https://wordpress.stackexchange.com/post/">Song Name</a> - Song Date

See how using % indicates a meta key, which’ll get swapped out with the value at run-time.

Also note that %post_title and %link are two special parameters that get swapped with the post title and anchor link, respectively.

You can also format the contents of the link text in the same way;

[song-list link="Date: %song_date_meta_key"]
// Post Title <a href="https://wordpress.stackexchange.com/post/">Date: Song Date</a>

Finally, the key attribute controls which posts are retrieved.

[song-list key="song_name"]
// Retrieves all posts with the meta key 'song_name'

I’d recommend replacing the hard-coded defaults at the beginning of the function with your most often used parameters.

Leave a Comment