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?)


  • It can be difficult to understand how this place works, and politeness costs nothing. Consider going easy on the non-ninjas from time to time.
    – snthr
    Jun 22, 2011 at 14:33
  • i said "please" :)
    – Bainternet
    Jun 22, 2011 at 15:29

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 ) )

        $_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() ) {

        $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] );
                    $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>';


    $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="/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="/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.

  • Hi TheDeadMedic, thanks for that, it seems to be what I'm after. Is there any way to put the title of the post into the <li> before the song name? I tried with the_title but it ain't working...
    – snthr
    Jun 18, 2011 at 18:12
  • You want get_the_title() - see revised answer. Jun 18, 2011 at 18:17
  • That's fantastic, thanks. Aaaand... one last thing. :) Is there a way to output another custom field in the same <li>? Say, to include the name of the band the song is by, or the date it was released, assuming that they're available as separate custom fields accompanying the posts? Thanks!
    – snthr
    Jun 19, 2011 at 18:20
  • So like, <li>TITLE - [SONG NAME LINK] - DATE</li>? Would you want to control what appears through the shortcode (for example, `[song-list show="name,date"]), or just hardcode it in the function? Jun 19, 2011 at 18:47
  • Yep, that's what I'm after. If it could be included in the shortcode (and so could be easily edited or expanded in future) that would be best, but I could live with it being hardcoded in the function.
    – snthr
    Jun 19, 2011 at 19:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.