1

I have created the following custom post types:

  • Artists
    • Albums
      • Songs

Songs are children of Albums, Albums are children of Artists.

I am trying to set up a navigation structure that shows this relationship, in the following format: http://www.example.com/artists/alicia-keys/discography/girl-on-fire/brand-new-me. alicia-keys is a post name of type artists, girl-on-fire is a post name for the post type albums & brand-new-me is a post name for the post type Songs.

When someone shortens the url to say, http//www.example.com/artists/alicia-keys/discography it should display albums that belong to Alicia Keys and if you simply type http://www.example.com/albums it shows all albums from all artists. if I have a link within the Artists CPT e.g. 'See all albums', I want it to open a new page which displays all albums belonging to the parent artist

I have the following files:

  • single-artists.php
  • single-albums.php
  • single-songs.php
  • archive-artists.php
  • archive-albums.php
  • archive-songs.php

The question can be broken down as follows:

  1. Is there a rewrite rule and/or filter code that I will need to write in functions.php? if so, how do i go about it?

  2. do I need to change/rewrite the slug? if so, what do I need to do

  3. Will I need additional files to achieve the above link structure? if yes please explain.

Just a note: I am using Wp-types plugin to create the custom post types however, I strongly feel this problem is more of a Wordpress challenge rather than one restricted to a specific plugin. After all, I believe the plugin produces almost the same code that you would get if you were to code the post types from scratch but nonetheless, I have also posted a similar question in their forum. If I get an answer there, I will also supply it here to help anyone else who might face such a problem in the future.

UPDATE: Potential path to solution

After some googling I was able to find a path that I believe will lead to solving the problem. I was able to create the following link structure, which loads all albums for alicia keys :

  • example.com/artists/alicia-keys/discography .

The code below is taken from a tutorial by Tareq Hasan and modified for the example. see the original here - http://code.tutsplus.com/articles/custom-page-template-page-based-on-url-rewrite--wp-30564

As the original author of the code states, this requires PHP 5.3.0 and up due to use of PHP anonymous functions.

in functions.php

<?php
 //create a rewrite rule that will append /discography to artists endpoint
 function ex_rewrite(){
  //add_rewrite_rule( 'artists/([^/]+)/discography', 'index.php?artists=$matches[1]&discography=yes', 'top' );
   add_rewrite_rule( 'artists/([^/]+)/discography/([^/]+)/?', 'index.php?album_artist=$matches[1]&discography=yes&albums=$matches[2]', 'top' );
 }
 add_action('init','ex_rewrite');
//register query variable so that wordpress recognizes discography query_var
function ex_query_var($vars){
 $vars[]= 'discography';
 $vars[] = 'album_artist';
 return $vars;
}
 add_filter('query_vars','ex_query_var');
//tell wordpress that when it sees the discography query variable,set/load a template called single-artists-discography.
add_filter( 'template_include', function( $path ) {
if ( get_query_var( 'album_artist' ) && is_singular( 'artists' ) ) {
    return get_template_directory() . '/single-artists.php';
}
if ( get_query_var( 'discography' ) && is_singular( 'artists' ) ) {
    return get_template_directory() . '/single-artists-discography.php';
}
return $path;
});

The single-artists-discography holds the logic of displaying all albums belonging to the specific artist. in this case, it will display all albums that belong to Alicia Keys. This answers part three of the question breakdown above, the need for additional files.

So the challenge narrows down to this:

  • How to rewrite an Album's URL to display as example.com/artists/alicia-keys/discography/girl-on-fire.
  • How to rewrite a song's URL to display as example.com/artists/alicia-keys/discography/girl-on-fire/brand-new-me

I hope now more knowledgeable developers can now use this as a pointer to help me figure out how to solve the above,as I research more.

6
  • I agree. The plugin used to create the post types should not make this question "off topic". I will be working on a challenge similar to your very well organized and detailed question soon. If I uncover an answer for you I will post it here. Good luck!
    – jdm2112
    Commented Nov 1, 2014 at 20:15
  • @jdm2112 thanks JDM. I will be exploring other related posts as well to see if I can piece different information together to come up with a solution.I personally contacted a well known wordpress developer and if he manages to give me an answer, I will post it here to help other people with the same scenario
    – nelson
    Commented Nov 1, 2014 at 21:49
  • 1
    there are 3 parts to this you'll need to solve - storing the relationship between the 3 post types (meta data), generating the rewrite rules to handle incoming requests (post type registration rewrite arguments, add_rewrite_rule), and generating the permalinks for each post type with parent objects in the URL (post_type_link filter). you can probably find examples of all of these things here and start to assemble a working solution.
    – Milo
    Commented Nov 1, 2014 at 23:13
  • @Milo thanks for the tips. Creating a relationship is no problem (the plugin provides functionality for that). The challenging parts are now the rewrite rules and filters.
    – nelson
    Commented Nov 2, 2014 at 1:09
  • Nelson, if you can answer your question yourself, then please add that as answer - not as edit to your question. You can accept that question later on. Thanks.
    – kaiser
    Commented Nov 6, 2014 at 14:44

3 Answers 3

3

In order to rewrite a custom post type's URL, or permalink, you'll need to filter the 'post_type_link', which is run whenever get_permalink() is called, allowing you to change a post's permalink. I've include some guide code below.

It seems like you have a handle on the rewrite rules but let me know if you need more help.

add_filter( 'post_type_link', 'filter_the_post_type_link', 1, 4 );
function filter_the_post_type_link( $post_link, $post, $leavename, $sample ) {

    // Filter the permalink by post type
    switch( $post->post_type ) {

        case 'albums':

            // Build permalink
            $post_link = get_bloginfo( 'url' ) . '/artists/';

            // Get the artist
            $album_artist = ??

            // Add artist to permalink
            $post_link .= "{$album_artist}/";

            // Add album name to permalink
            $post_link .= "discography/{$post->post_name}";

            break;

        case 'songs':

            // Build permalink
            $post_link = get_bloginfo( 'url' ) . '/artists/';

            // Get the artist
            $album_artist = ??

            // Add artist to permalink
            $post_link .= "{$album_artist}/";

            // Get the album
            $album = ??

            // Add album to permalink
            $post_link .= "discography/{$album}/";

            // Add song name to permalink
            $post_link .= "{$post->post_name}";

            break;

    }

    return $post_link;

}
11
  • thanks for the answer.I will explore it in depth once I open business in the morning (its late night as I type this).
    – nelson
    Commented Nov 3, 2014 at 18:11
  • Managed to implement the code as you have displayed above, including the artist's slug. However, there's a problem and it is stemming from the discography solution I put in the code update to the question : The URL generates properly but when wordpress sees 'discography', it shows the single-artists-discography template and code execution stops there i.e. it ignores anything that follows /discography/ . What should I do to rectify
    – nelson
    Commented Nov 4, 2014 at 8:42
  • If you want to add a rewrite rule for a specific album, then add add_rewrite_rule( 'artists/([^/]+)/discography/([^/]+)/?', 'index.php?artists=$matches[1]&discography=yes&albums=$matches[2]', 'top' ); BUT WordPress will only allow one post type per query so if albums are the main content, then you'll need to change 'artists' to a custom query var: add_rewrite_rule( 'artists/([^/]+)/discography/([^/]+)/?', 'index.php?album_artist=$matches[1]&discography=yes&albums=$matches[2]', 'top' ); Then setup 'album_artist' like you did 'discography' and you can use that variable in your template. Commented Nov 4, 2014 at 14:06
  • This is exactly why you need to register your post types and add extra rules in a precise order as I mentioned in my answer. Rules have to cascade from most to least specific. You also don't need extra rules for any singular content, those can be handled when you register post types.
    – Milo
    Commented Nov 4, 2014 at 14:39
  • @Milo OK. Let me explore and then get back to you.
    – nelson
    Commented Nov 4, 2014 at 15:47
2

This here is a clarification and addition to Rachel's answer. Combining this together with her code will produce the full solution(I was advised to put this here by a moderator rather than editing the question with an update. I give 100% credit for this answer to Rachel)

<?php
 //rewrite rules for discography, news, albums,songs
 function ex_rewrite(){
 //discography rewrite
 add_rewrite_rule( 'artists/([^/]+)/discography/?$', 'index.php?artists=$matches[1]&discography=yes','top');
 //news
add_rewrite_rule('artists/([^/]+)/news/?$', 'index.php?artists=$matches[1]&news=yes','top');
//album rewrite
add_rewrite_rule( 'artists/([^/]+)/discography/([^/]+)/?$', 'index.php?album_artist=$matches[1]&discography=yes&albums=$matches[2]', 'top' );
//song rewrite
add_rewrite_rule( 'artists/([^/]+)/discography/([^/]+)/([^/]+)/?$', 'index.php?album_artist=$matches[1]&discography=yes&albums=$matches[2]&songs=$matches[3]', 'top' );
 }
 add_action('init','ex_rewrite');
//register query variable so that wordpress recognizes discography query_var as well as other variables you might be using
function ex_query_var($vars){
 $vars[]= 'discography';
 $vars[] = 'album_artist';
 $vars[] = 'news';
 return $vars;
}
 add_filter('query_vars','ex_query_var');
//tell wordpress that when it sees the discography or news variable, redirect to to their respective templates.
add_filter( 'template_include', function( $path ) {
if ( get_query_var( 'discography' ) && is_singular( 'artists' ) ) {
    return get_template_directory() . '/single-artists-discography.php';
    // URL will result in example.com/artists/alicia-keys/discography
}
 if ( get_query_var( 'news' ) && is_singular( 'artists' ) ) {
    return get_template_directory() . '/single-artists-news.php';
  //URL will result in example.com/artists/alicia-keys/news
}
return $path;
});
//Rachel's code starts here......

As for the link in example.com/artists/alicia-keys that will take you to example.com/artists/alicia-keys/discography:

<a href="<?php echo get_permalink($post->ID). 'discography/';?>"> See Alicia Keys' Full Discography</a>

In Rachel's code, there are variables where you need to supply the slugs of the parent posts($album_artist, $album). This is where the method you used to establish the relationships between the post types comes into play. I am using a plugin which provides functionality of establishing relationships as well as extracting parent information using foreign keys.

I felt it is important to note this for those who might wonder what information needs to be supplied for both variables.

0

It seems you've got the discography rule taken care of. You can generate the rewrite rules for all of your single post types via the rewrite argument when your post types are registered. This may require you to manually register your post types, not sure what level of control that plugin provides. You'll also have to be careful about exactly what order you register the post types in so that your rules cascade from most to least specific. This may be another reason you'll have to manually register your post types.

Artists is the most simple, that will be just:

rewrite => array( 'slug' => 'artists' )

An album would be:

rewrite => array( 'slug' => 'artists/%artist%/discography' )

Lastly, songs would be:

rewrite => array( 'slug' => 'artists/%artist%/discography/%album%' )

You'll then need a post_type_link function which loads the related data and swaps it in place of the %artist% and %album% rewrite tags. I'm not sure how those relationships are stored, so I can't tell you how that will work. You can see a similar example of this function with a taxonomy term on this answer.

2
  • thanks for the answer. The plugin does provide a field for you to supply a slug rewrite rule thus I can say with absolute certainty that I can implement what you have provided. I will explore your approach as well as Rachel's answer above once I open business in morning as it is late night in my time zone :-)
    – nelson
    Commented Nov 3, 2014 at 18:14
  • I feel that both your answers are correct though Rachel gave me a more detailed explanation which helped me construct the URL rewrites correctly. The only problem that has emerged is that discography has stopped working. rather than editing this question to the point of deviating from original intent, I have created a new one to address this specific problem - wordpress.stackexchange.com/questions/32297/… . I hope you can help with that. I could have given you a useful upvote but unfortunately my current reputation is low :-)
    – nelson
    Commented Nov 5, 2014 at 8:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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