Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Is there a way (besides adding a Custom Link) to add a custom post type archive to a menu in WordPress? If it's added using a custom link (e.g. /cpt-archive-slug/), WordPress does not apply classes like current-menu-item to the list element, which presents challenges when styling the menu.

If the custom link contains the entire URL (e.g. http://site.com/cpt-archive-slug/), those classes are added. However, that's probably not a 'best practice'.

share|improve this question

8 Answers 8

up vote 5 down vote accepted

your best opption is custom link with full url as Custom post types archives are different form taxonomy based archives (categories,tags,any custom taxonomy) and date based archives which have there own archive slug.

share|improve this answer

I know this is old but I have this problem too and I found a rather clean way to handle it is to use a custom menu walker

class KB_Custom_Menu_Walker extends Walker_Nav_Menu {

  protected static $custom_post_types = array();

  public function start_el(&$output, $item, $depth=0, $args=array(), $id=0) {
    if (isset( self::$custom_post_types[ $item->url ] )) {
      $item->url = get_post_type_archive_link( self::$custom_post_types[$item->url] );
    }
    parent::start_el($output, $item, $depth, $args, $id);
  }

  public static function custom_post_types($type=null) {
    if ($type) {
      self::$custom_post_types[ '#post_type_'.$type ] = $type;
    }
    return self::$custom_post_types;
  }
}

Having a custom link menu item with URL of #post_type_album, you can use it like this:

# Where you defined your custom post type (could be anywhere anyway)
KB_Custom_Menu_Walker::custom_post_types('album');

# And display the menu
wp_nav_menu(array(
  'theme_location' => 'primary-nav',
  'walker' => new KB_Custom_Menu_Walker(),
));

Note: This assume that your post type's slug and name are the same.

share|improve this answer
    
+1 for the late quality answer. This is good for the community! –  Brian Fegter Sep 23 '12 at 3:13

I have expanded a bit on tungd's answer to provide more genericity with that approach. This implementation allows adding arbitrary mappings between menu 'macros' and internal Wordpress URLs that only the backend knows about.

I've also decided to use ! as the prefix for these macros to avoid them clashing with named anchors - this includes an overhead to strip off the 'http://' from the link URL (as Wordpress will attempt to normalise these weird links). If that implementation bothers you, you can always remove the preg_replace() call and use # as your link prefix as before.

class Extendable_Menu_Walker extends Walker_Nav_Menu
{
    protected static $custom_urls = array();

    public static function setupUrls()
    {
        // calls to self::mapPostType($postTypeName) and 
        // self::createMapping($wildcard, $url) go here...
    }

    public function start_el(&$output, $item, $depth=0, $args=array(), $id=0)
    {
        $url = preg_replace('@^https?://@', '', $item->url);
        if (isset( self::$custom_urls[ $url ] )) {
            $item->url = self::$custom_urls[ $url ];
        }
        parent::start_el($output, $item, $depth, $args, $id);
    }

    public static function createMapping($urlKey, $realUrl)
    {
        self::$custom_urls['!' . $urlKey] = $realUrl;
    }

    public static function mapPostType($type)
    {
        self::createMapping('post_type_' . $type, get_post_type_archive_link($type));
    }
}

add_action('init', array('Extendable_Menu_Walker', 'setupUrls'));
share|improve this answer

You can create a Page and then apply a custom archive template to it. I create and use archive-{post-type}.php fo the template name just like you would for the default archive, I just manually apply it to the Page.

If you do this, I'd recommend setting your CPT to has_archive='false' to avoid permalink collisions, regardless just make sure your Page has a different permalink slug than your CPT archive slug is set to.

share|improve this answer

I ran into the same exact problem, and found this solution:

// $menu is the menu as a string 
if ( check if youa re on the page you are looking for )
    $menu = str_replace( '<li class="menu-item"><a href="http://bla.com/bloop/">', '<li class="current-menu-item menu-item"><a href="http://bla.com/bloop/">'>, $menu );  

Source: http://www.wptavern.com/forum/plugins-hacks/2169-current-page-custom-post-type-archive.html

share|improve this answer

I think I'd have to create a new "parent" post-type, so the custom-post-type I want to show becomes a "child", and that parent WILL be listed in the menu.

This should be an option. Creating a dummy parent just for getting it listed in the menu is too much work.

I'd like a way to do it from the template, or functions, so I don't have to do it in each menu settings for each site in my multisite installation.

share|improve this answer
1  
I meant, the custom-post-type posts I wanted to show in the menu will be the only sub-type of the new-created parent, and I hope it gets listed under the parent in the menu settings. –  sergio Sep 27 '11 at 15:06

I Found this answer when searching for a similar issue. I just copied the code into my finctions.php file and it worked right out of the box :) hope it helps!

Dynamic navigation for custom post type (pages)

share|improve this answer

What I do instead of adding the link is creating an empty page. In that page I add something like "Content generated from cpt-name" in the editor so my client knows where all the stuff is coming from.

Then I add that page to the menu and with the following code I ensure that is highlighted when archives-cpt or single-cpt is running.

/**
 * Fix to add Custom post types to nav menu
 * If you custom post is called "concepts"
 * create a new empty page called concepts and add this
 */
 function additional_active_item_classes($classes = array(), $menu_item = false){
    global $wp_query;


    if(in_array('current-menu-item', $menu_item->classes)){
        $classes[] = 'current-menu-item';
    }

    if ( $menu_item->title == 'PAGE TITLE' && is_post_type_archive('cpt-slug') ) {
        $classes[] = 'current-menu-item';
    }

    if ( $menu_item->title == 'PAGE TITLE' && is_singular('cpt-slug') ) {
        $classes[] = 'current-menu-item';
    }


    return $classes;
}
add_filter( 'nav_menu_css_class', 'additional_active_item_classes', 10, 2 );

This will check for menu/page title and if we are actually seeing the archive or individual page of a custom post type. If it's true it will add the current-menu-item class.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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